@vibecheckai/cli 3.7.0 → 3.8.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/README.md +135 -63
- package/bin/_deprecations.js +447 -19
- package/bin/_router.js +1 -1
- package/bin/registry.js +347 -280
- package/bin/runners/context/generators/cursor-enhanced.js +2439 -0
- package/bin/runners/lib/agent-firewall/enforcement/gateway.js +1059 -0
- package/bin/runners/lib/agent-firewall/enforcement/index.js +98 -0
- package/bin/runners/lib/agent-firewall/enforcement/mode.js +318 -0
- package/bin/runners/lib/agent-firewall/enforcement/orchestrator.js +484 -0
- package/bin/runners/lib/agent-firewall/enforcement/proof-artifact.js +418 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/change-event.schema.json +173 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/intent.schema.json +181 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/verdict.schema.json +222 -0
- package/bin/runners/lib/agent-firewall/enforcement/verdict-v2.js +333 -0
- package/bin/runners/lib/agent-firewall/index.js +200 -0
- package/bin/runners/lib/agent-firewall/integration/index.js +20 -0
- package/bin/runners/lib/agent-firewall/integration/ship-gate.js +437 -0
- package/bin/runners/lib/agent-firewall/intent/alignment-engine.js +622 -0
- package/bin/runners/lib/agent-firewall/intent/auto-detect.js +426 -0
- package/bin/runners/lib/agent-firewall/intent/index.js +102 -0
- package/bin/runners/lib/agent-firewall/intent/schema.js +352 -0
- package/bin/runners/lib/agent-firewall/intent/store.js +283 -0
- package/bin/runners/lib/agent-firewall/interception/fs-interceptor.js +502 -0
- package/bin/runners/lib/agent-firewall/interception/index.js +23 -0
- package/bin/runners/lib/agent-firewall/session/collector.js +451 -0
- package/bin/runners/lib/agent-firewall/session/index.js +26 -0
- package/bin/runners/lib/artifact-envelope.js +540 -0
- package/bin/runners/lib/auth-shared.js +977 -0
- package/bin/runners/lib/checkpoint.js +941 -0
- package/bin/runners/lib/cleanup/engine.js +571 -0
- package/bin/runners/lib/cleanup/index.js +53 -0
- package/bin/runners/lib/cleanup/output.js +375 -0
- package/bin/runners/lib/cleanup/rules.js +1060 -0
- package/bin/runners/lib/doctor/diagnosis-receipt.js +454 -0
- package/bin/runners/lib/doctor/failure-signatures.js +526 -0
- package/bin/runners/lib/doctor/fix-script.js +336 -0
- package/bin/runners/lib/doctor/modules/build-tools.js +453 -0
- package/bin/runners/lib/doctor/modules/index.js +62 -3
- package/bin/runners/lib/doctor/modules/os-quirks.js +706 -0
- package/bin/runners/lib/doctor/modules/repo-integrity.js +485 -0
- package/bin/runners/lib/doctor/safe-repair.js +384 -0
- package/bin/runners/lib/engines/attack-detector.js +1192 -0
- package/bin/runners/lib/entitlements-v2.js +2 -2
- package/bin/runners/lib/missions/briefing.js +427 -0
- package/bin/runners/lib/missions/checkpoint.js +753 -0
- package/bin/runners/lib/missions/hardening.js +851 -0
- package/bin/runners/lib/missions/plan.js +421 -32
- package/bin/runners/lib/missions/safety-gates.js +645 -0
- package/bin/runners/lib/missions/schema.js +478 -0
- package/bin/runners/lib/packs/bundle.js +675 -0
- package/bin/runners/lib/packs/evidence-pack.js +671 -0
- package/bin/runners/lib/packs/pack-factory.js +837 -0
- package/bin/runners/lib/packs/permissions-pack.js +686 -0
- package/bin/runners/lib/packs/proof-graph-pack.js +779 -0
- package/bin/runners/lib/safelist/index.js +96 -0
- package/bin/runners/lib/safelist/integration.js +334 -0
- package/bin/runners/lib/safelist/matcher.js +696 -0
- package/bin/runners/lib/safelist/schema.js +948 -0
- package/bin/runners/lib/safelist/store.js +438 -0
- package/bin/runners/lib/schemas/ship-manifest.schema.json +251 -0
- package/bin/runners/lib/ship-gate.js +832 -0
- package/bin/runners/lib/ship-manifest.js +1153 -0
- package/bin/runners/lib/ship-output.js +1 -1
- package/bin/runners/lib/unified-cli-output.js +710 -383
- package/bin/runners/lib/upsell.js +3 -3
- package/bin/runners/lib/why-tree.js +650 -0
- package/bin/runners/runAllowlist.js +33 -4
- package/bin/runners/runApprove.js +240 -1122
- package/bin/runners/runAudit.js +692 -0
- package/bin/runners/runAuth.js +325 -29
- package/bin/runners/runCheckpoint.js +442 -494
- package/bin/runners/runCleanup.js +343 -0
- package/bin/runners/runDoctor.js +269 -19
- package/bin/runners/runFix.js +411 -32
- package/bin/runners/runForge.js +411 -0
- package/bin/runners/runIntent.js +906 -0
- package/bin/runners/runKickoff.js +878 -0
- package/bin/runners/runLaunch.js +2000 -0
- package/bin/runners/runLink.js +785 -0
- package/bin/runners/runMcp.js +1741 -837
- package/bin/runners/runPacks.js +2089 -0
- package/bin/runners/runPolish.js +41 -0
- package/bin/runners/runSafelist.js +1190 -0
- package/bin/runners/runScan.js +21 -9
- package/bin/runners/runShield.js +1282 -0
- package/bin/runners/runShip.js +395 -16
- package/bin/vibecheck.js +34 -6
- package/mcp-server/README.md +117 -158
- package/mcp-server/handlers/tool-handler.ts +3 -3
- package/mcp-server/index.js +16 -0
- package/mcp-server/intent-firewall-interceptor.js +529 -0
- package/mcp-server/manifest.json +473 -0
- package/mcp-server/package.json +1 -1
- package/mcp-server/registry/tool-registry.js +315 -523
- package/mcp-server/registry/tools.json +442 -428
- package/mcp-server/tier-auth.js +68 -11
- package/mcp-server/tools-v3.js +70 -16
- package/package.json +1 -1
- package/bin/runners/runProof.zip +0 -0
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
// bin/runners/lib/packs/proof-graph-pack.js
|
|
2
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
3
|
+
// PROOF GRAPH PACK - Proof graph with cross-links to receipts and visualization
|
|
4
|
+
// Interactive graph showing verification coverage and proof chains
|
|
5
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const {
|
|
10
|
+
createPackBuilder,
|
|
11
|
+
PACK_TYPE,
|
|
12
|
+
ARTIFACT_TYPE,
|
|
13
|
+
collectReceiptReferences,
|
|
14
|
+
loadReceipt,
|
|
15
|
+
} = require('./pack-factory');
|
|
16
|
+
|
|
17
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
18
|
+
// GRAPH DATA LOADING
|
|
19
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Load proof graph from various sources
|
|
23
|
+
* @param {string} repoRoot - Repository root
|
|
24
|
+
* @returns {object|null} Proof graph data
|
|
25
|
+
*/
|
|
26
|
+
function loadProofGraph(repoRoot) {
|
|
27
|
+
// Try primary location
|
|
28
|
+
const primaryPath = path.join(repoRoot, '.vibecheck/proof-graph.json');
|
|
29
|
+
if (fs.existsSync(primaryPath)) {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(fs.readFileSync(primaryPath, 'utf8'));
|
|
32
|
+
} catch (e) {
|
|
33
|
+
// Continue to fallback
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Try reality graph
|
|
38
|
+
const realityPath = path.join(repoRoot, '.vibecheck/reality-graph.json');
|
|
39
|
+
if (fs.existsSync(realityPath)) {
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(fs.readFileSync(realityPath, 'utf8'));
|
|
42
|
+
} catch (e) {
|
|
43
|
+
// Continue to fallback
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Try to build from scan results
|
|
48
|
+
const scanPath = path.join(repoRoot, '.vibecheck/results/latest.json');
|
|
49
|
+
if (fs.existsSync(scanPath)) {
|
|
50
|
+
try {
|
|
51
|
+
const scan = JSON.parse(fs.readFileSync(scanPath, 'utf8'));
|
|
52
|
+
return buildGraphFromScan(scan);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
// Return empty graph
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Build basic graph from scan results
|
|
63
|
+
* @param {object} scan - Scan results
|
|
64
|
+
* @returns {object} Graph data
|
|
65
|
+
*/
|
|
66
|
+
function buildGraphFromScan(scan) {
|
|
67
|
+
const nodes = [];
|
|
68
|
+
const edges = [];
|
|
69
|
+
|
|
70
|
+
// Create nodes from findings
|
|
71
|
+
const findings = scan.findings || scan.report?.findings || [];
|
|
72
|
+
|
|
73
|
+
for (const finding of findings) {
|
|
74
|
+
nodes.push({
|
|
75
|
+
id: finding.id,
|
|
76
|
+
type: 'finding',
|
|
77
|
+
label: finding.title || finding.message,
|
|
78
|
+
category: finding.category,
|
|
79
|
+
severity: finding.severity,
|
|
80
|
+
file: finding.file || finding.evidence?.[0]?.file,
|
|
81
|
+
status: 'unverified',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Create file nodes
|
|
86
|
+
const files = new Set(findings.map(f => f.file || f.evidence?.[0]?.file).filter(Boolean));
|
|
87
|
+
for (const file of files) {
|
|
88
|
+
nodes.push({
|
|
89
|
+
id: `file:${file}`,
|
|
90
|
+
type: 'file',
|
|
91
|
+
label: path.basename(file),
|
|
92
|
+
path: file,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Create edges from findings to files
|
|
97
|
+
for (const finding of findings) {
|
|
98
|
+
const file = finding.file || finding.evidence?.[0]?.file;
|
|
99
|
+
if (file) {
|
|
100
|
+
edges.push({
|
|
101
|
+
source: finding.id,
|
|
102
|
+
target: `file:${file}`,
|
|
103
|
+
type: 'located_in',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
nodes,
|
|
110
|
+
edges,
|
|
111
|
+
metadata: {
|
|
112
|
+
generatedFrom: 'scan',
|
|
113
|
+
findingCount: findings.length,
|
|
114
|
+
fileCount: files.size,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Load proof runs from receipts
|
|
121
|
+
* @param {string} repoRoot - Repository root
|
|
122
|
+
* @returns {Array} Proof runs
|
|
123
|
+
*/
|
|
124
|
+
function loadProofRuns(repoRoot) {
|
|
125
|
+
const runs = [];
|
|
126
|
+
const receiptsDir = path.join(repoRoot, '.vibecheck/receipts');
|
|
127
|
+
|
|
128
|
+
if (!fs.existsSync(receiptsDir)) return runs;
|
|
129
|
+
|
|
130
|
+
const dirs = fs.readdirSync(receiptsDir, { withFileTypes: true })
|
|
131
|
+
.filter(d => d.isDirectory())
|
|
132
|
+
.map(d => d.name);
|
|
133
|
+
|
|
134
|
+
for (const dir of dirs) {
|
|
135
|
+
const manifestPath = path.join(receiptsDir, dir, 'manifest.json');
|
|
136
|
+
if (fs.existsSync(manifestPath)) {
|
|
137
|
+
try {
|
|
138
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
139
|
+
runs.push({
|
|
140
|
+
runId: dir,
|
|
141
|
+
...manifest,
|
|
142
|
+
});
|
|
143
|
+
} catch (e) {
|
|
144
|
+
// Skip invalid
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return runs.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
153
|
+
// GRAPH ENHANCEMENT
|
|
154
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Enhance graph with receipt cross-links
|
|
158
|
+
* @param {object} graph - Base graph
|
|
159
|
+
* @param {object} receipts - Receipt references
|
|
160
|
+
* @param {Array} proofRuns - Proof runs
|
|
161
|
+
* @returns {object} Enhanced graph
|
|
162
|
+
*/
|
|
163
|
+
function enhanceGraphWithReceipts(graph, receipts, proofRuns) {
|
|
164
|
+
const enhanced = {
|
|
165
|
+
...graph,
|
|
166
|
+
nodes: [...(graph.nodes || [])],
|
|
167
|
+
edges: [...(graph.edges || [])],
|
|
168
|
+
receipts: receipts || {},
|
|
169
|
+
proofRuns: proofRuns || [],
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Add receipt nodes
|
|
173
|
+
if (receipts.ship) {
|
|
174
|
+
enhanced.nodes.push({
|
|
175
|
+
id: 'receipt:ship',
|
|
176
|
+
type: 'receipt',
|
|
177
|
+
label: `Ship: ${receipts.ship.verdict || 'N/A'}`,
|
|
178
|
+
receiptType: 'ship',
|
|
179
|
+
verdict: receipts.ship.verdict,
|
|
180
|
+
timestamp: receipts.ship.timestamp,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (receipts.reality) {
|
|
185
|
+
enhanced.nodes.push({
|
|
186
|
+
id: 'receipt:reality',
|
|
187
|
+
type: 'receipt',
|
|
188
|
+
label: `Reality: ${receipts.reality.verdict || 'N/A'}`,
|
|
189
|
+
receiptType: 'reality',
|
|
190
|
+
verdict: receipts.reality.verdict,
|
|
191
|
+
runId: receipts.reality.runId,
|
|
192
|
+
timestamp: receipts.reality.timestamp,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (receipts.scan) {
|
|
197
|
+
enhanced.nodes.push({
|
|
198
|
+
id: 'receipt:scan',
|
|
199
|
+
type: 'receipt',
|
|
200
|
+
label: `Scan: ${receipts.scan.findingCount} findings`,
|
|
201
|
+
receiptType: 'scan',
|
|
202
|
+
findingCount: receipts.scan.findingCount,
|
|
203
|
+
timestamp: receipts.scan.timestamp,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Link findings to scan receipt
|
|
207
|
+
for (const node of enhanced.nodes) {
|
|
208
|
+
if (node.type === 'finding') {
|
|
209
|
+
enhanced.edges.push({
|
|
210
|
+
source: node.id,
|
|
211
|
+
target: 'receipt:scan',
|
|
212
|
+
type: 'detected_by',
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Compute statistics
|
|
219
|
+
enhanced.statistics = computeGraphStats(enhanced);
|
|
220
|
+
|
|
221
|
+
return enhanced;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Compute graph statistics
|
|
226
|
+
* @param {object} graph - Graph data
|
|
227
|
+
* @returns {object} Statistics
|
|
228
|
+
*/
|
|
229
|
+
function computeGraphStats(graph) {
|
|
230
|
+
const nodes = graph.nodes || [];
|
|
231
|
+
const edges = graph.edges || [];
|
|
232
|
+
|
|
233
|
+
const nodesByType = {};
|
|
234
|
+
for (const node of nodes) {
|
|
235
|
+
nodesByType[node.type] = (nodesByType[node.type] || 0) + 1;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const edgesByType = {};
|
|
239
|
+
for (const edge of edges) {
|
|
240
|
+
edgesByType[edge.type] = (edgesByType[edge.type] || 0) + 1;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const findingsBySeverity = {};
|
|
244
|
+
for (const node of nodes.filter(n => n.type === 'finding')) {
|
|
245
|
+
findingsBySeverity[node.severity] = (findingsBySeverity[node.severity] || 0) + 1;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
totalNodes: nodes.length,
|
|
250
|
+
totalEdges: edges.length,
|
|
251
|
+
nodesByType,
|
|
252
|
+
edgesByType,
|
|
253
|
+
findingsBySeverity,
|
|
254
|
+
verifiedNodes: nodes.filter(n => n.status === 'verified').length,
|
|
255
|
+
unverifiedNodes: nodes.filter(n => n.status === 'unverified').length,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
260
|
+
// GRAPH FORMAT CONVERTERS
|
|
261
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Convert graph to DOT format
|
|
265
|
+
* @param {object} graph - Graph data
|
|
266
|
+
* @returns {string} DOT content
|
|
267
|
+
*/
|
|
268
|
+
function graphToDot(graph) {
|
|
269
|
+
const lines = ['digraph ProofGraph {'];
|
|
270
|
+
lines.push(' rankdir=LR;');
|
|
271
|
+
lines.push(' node [shape=box, style=filled];');
|
|
272
|
+
lines.push('');
|
|
273
|
+
|
|
274
|
+
// Node styles by type
|
|
275
|
+
const styles = {
|
|
276
|
+
finding: 'fillcolor="#ff7b7233", color="#ff7b72"',
|
|
277
|
+
file: 'fillcolor="#58a6ff33", color="#58a6ff"',
|
|
278
|
+
receipt: 'fillcolor="#3fb95033", color="#3fb950", shape=diamond',
|
|
279
|
+
route: 'fillcolor="#a371f733", color="#a371f7"',
|
|
280
|
+
component: 'fillcolor="#f778ba33", color="#f778ba"',
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// Add nodes
|
|
284
|
+
for (const node of graph.nodes || []) {
|
|
285
|
+
const style = styles[node.type] || 'fillcolor="#8b949e33", color="#8b949e"';
|
|
286
|
+
const label = (node.label || node.id).replace(/"/g, '\\"').slice(0, 50);
|
|
287
|
+
lines.push(` "${node.id}" [label="${label}", ${style}];`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
lines.push('');
|
|
291
|
+
|
|
292
|
+
// Add edges
|
|
293
|
+
for (const edge of graph.edges || []) {
|
|
294
|
+
const label = edge.type || '';
|
|
295
|
+
lines.push(` "${edge.source}" -> "${edge.target}" [label="${label}"];`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
lines.push('}');
|
|
299
|
+
|
|
300
|
+
return lines.join('\n');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Convert graph to Mermaid format
|
|
305
|
+
* @param {object} graph - Graph data
|
|
306
|
+
* @returns {string} Mermaid content
|
|
307
|
+
*/
|
|
308
|
+
function graphToMermaid(graph) {
|
|
309
|
+
const lines = ['flowchart LR'];
|
|
310
|
+
|
|
311
|
+
// Node shape by type
|
|
312
|
+
const shapes = {
|
|
313
|
+
finding: ['{{', '}}'],
|
|
314
|
+
file: ['[/', '/]'],
|
|
315
|
+
receipt: ['([', '])'],
|
|
316
|
+
route: ['[', ']'],
|
|
317
|
+
component: ['(', ')'],
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// Add nodes
|
|
321
|
+
for (const node of graph.nodes || []) {
|
|
322
|
+
const [open, close] = shapes[node.type] || ['[', ']'];
|
|
323
|
+
const label = (node.label || node.id).replace(/"/g, '').slice(0, 40);
|
|
324
|
+
const id = node.id.replace(/[^a-zA-Z0-9]/g, '_');
|
|
325
|
+
lines.push(` ${id}${open}"${label}"${close}`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
lines.push('');
|
|
329
|
+
|
|
330
|
+
// Add edges
|
|
331
|
+
for (const edge of graph.edges || []) {
|
|
332
|
+
const sourceId = edge.source.replace(/[^a-zA-Z0-9]/g, '_');
|
|
333
|
+
const targetId = edge.target.replace(/[^a-zA-Z0-9]/g, '_');
|
|
334
|
+
const label = edge.type ? `|${edge.type}|` : '';
|
|
335
|
+
lines.push(` ${sourceId} -->${label} ${targetId}`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return lines.join('\n');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
342
|
+
// HTML VISUALIZATION
|
|
343
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Generate interactive HTML visualization
|
|
347
|
+
* @param {object} graph - Enhanced graph data
|
|
348
|
+
* @returns {string} HTML content
|
|
349
|
+
*/
|
|
350
|
+
function generateHtmlVisualization(graph) {
|
|
351
|
+
const stats = graph.statistics || {};
|
|
352
|
+
const mermaidCode = graphToMermaid(graph).replace(/`/g, '\\`');
|
|
353
|
+
|
|
354
|
+
const receiptCards = [];
|
|
355
|
+
if (graph.receipts?.ship) {
|
|
356
|
+
receiptCards.push(`
|
|
357
|
+
<div class="receipt-card ship">
|
|
358
|
+
<div class="receipt-icon">🚀</div>
|
|
359
|
+
<div class="receipt-info">
|
|
360
|
+
<div class="receipt-type">Ship Receipt</div>
|
|
361
|
+
<div class="receipt-verdict">${graph.receipts.ship.verdict || 'N/A'}</div>
|
|
362
|
+
<div class="receipt-time">${graph.receipts.ship.timestamp ? new Date(graph.receipts.ship.timestamp).toLocaleString() : 'N/A'}</div>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
`);
|
|
366
|
+
}
|
|
367
|
+
if (graph.receipts?.reality) {
|
|
368
|
+
receiptCards.push(`
|
|
369
|
+
<div class="receipt-card reality">
|
|
370
|
+
<div class="receipt-icon">🔍</div>
|
|
371
|
+
<div class="receipt-info">
|
|
372
|
+
<div class="receipt-type">Reality Receipt</div>
|
|
373
|
+
<div class="receipt-verdict">${graph.receipts.reality.verdict || 'N/A'}</div>
|
|
374
|
+
<div class="receipt-time">Run: ${graph.receipts.reality.runId || 'N/A'}</div>
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
`);
|
|
378
|
+
}
|
|
379
|
+
if (graph.receipts?.scan) {
|
|
380
|
+
receiptCards.push(`
|
|
381
|
+
<div class="receipt-card scan">
|
|
382
|
+
<div class="receipt-icon">📊</div>
|
|
383
|
+
<div class="receipt-info">
|
|
384
|
+
<div class="receipt-type">Scan Receipt</div>
|
|
385
|
+
<div class="receipt-verdict">${graph.receipts.scan.findingCount} findings</div>
|
|
386
|
+
<div class="receipt-time">${graph.receipts.scan.timestamp ? new Date(graph.receipts.scan.timestamp).toLocaleString() : 'N/A'}</div>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return `<!DOCTYPE html>
|
|
393
|
+
<html lang="en">
|
|
394
|
+
<head>
|
|
395
|
+
<meta charset="UTF-8">
|
|
396
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
397
|
+
<title>Proof Graph - Vibecheck</title>
|
|
398
|
+
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
|
399
|
+
<style>
|
|
400
|
+
:root {
|
|
401
|
+
--bg: #0d1117;
|
|
402
|
+
--surface: #161b22;
|
|
403
|
+
--border: #30363d;
|
|
404
|
+
--text: #c9d1d9;
|
|
405
|
+
--text-muted: #8b949e;
|
|
406
|
+
--accent: #ff9650;
|
|
407
|
+
--success: #3fb950;
|
|
408
|
+
--warning: #d29922;
|
|
409
|
+
--danger: #f85149;
|
|
410
|
+
--info: #58a6ff;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
414
|
+
|
|
415
|
+
body {
|
|
416
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
417
|
+
background: var(--bg);
|
|
418
|
+
color: var(--text);
|
|
419
|
+
line-height: 1.6;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.header {
|
|
423
|
+
background: var(--surface);
|
|
424
|
+
border-bottom: 1px solid var(--border);
|
|
425
|
+
padding: 1.5rem 2rem;
|
|
426
|
+
display: flex;
|
|
427
|
+
justify-content: space-between;
|
|
428
|
+
align-items: center;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
h1 {
|
|
432
|
+
font-size: 1.5rem;
|
|
433
|
+
color: var(--accent);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.stats {
|
|
437
|
+
display: flex;
|
|
438
|
+
gap: 2rem;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.stat {
|
|
442
|
+
text-align: center;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.stat-value {
|
|
446
|
+
font-size: 1.5rem;
|
|
447
|
+
font-weight: bold;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.stat-label {
|
|
451
|
+
font-size: 0.75rem;
|
|
452
|
+
color: var(--text-muted);
|
|
453
|
+
text-transform: uppercase;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.container {
|
|
457
|
+
display: flex;
|
|
458
|
+
height: calc(100vh - 80px);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.sidebar {
|
|
462
|
+
width: 320px;
|
|
463
|
+
background: var(--surface);
|
|
464
|
+
border-right: 1px solid var(--border);
|
|
465
|
+
overflow-y: auto;
|
|
466
|
+
padding: 1rem;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.sidebar h2 {
|
|
470
|
+
font-size: 0.875rem;
|
|
471
|
+
color: var(--text-muted);
|
|
472
|
+
text-transform: uppercase;
|
|
473
|
+
letter-spacing: 0.05em;
|
|
474
|
+
margin-bottom: 1rem;
|
|
475
|
+
padding-bottom: 0.5rem;
|
|
476
|
+
border-bottom: 1px solid var(--border);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.receipt-card {
|
|
480
|
+
background: var(--bg);
|
|
481
|
+
border-radius: 8px;
|
|
482
|
+
padding: 1rem;
|
|
483
|
+
margin-bottom: 0.75rem;
|
|
484
|
+
display: flex;
|
|
485
|
+
gap: 1rem;
|
|
486
|
+
align-items: center;
|
|
487
|
+
border-left: 3px solid;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.receipt-card.ship { border-color: var(--success); }
|
|
491
|
+
.receipt-card.reality { border-color: var(--info); }
|
|
492
|
+
.receipt-card.scan { border-color: var(--warning); }
|
|
493
|
+
|
|
494
|
+
.receipt-icon {
|
|
495
|
+
font-size: 1.5rem;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.receipt-type {
|
|
499
|
+
font-size: 0.75rem;
|
|
500
|
+
color: var(--text-muted);
|
|
501
|
+
text-transform: uppercase;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.receipt-verdict {
|
|
505
|
+
font-weight: 600;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.receipt-time {
|
|
509
|
+
font-size: 0.75rem;
|
|
510
|
+
color: var(--text-muted);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.legend {
|
|
514
|
+
margin-top: 1.5rem;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.legend-item {
|
|
518
|
+
display: flex;
|
|
519
|
+
align-items: center;
|
|
520
|
+
gap: 0.5rem;
|
|
521
|
+
margin-bottom: 0.5rem;
|
|
522
|
+
font-size: 0.875rem;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.legend-color {
|
|
526
|
+
width: 12px;
|
|
527
|
+
height: 12px;
|
|
528
|
+
border-radius: 3px;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.legend-color.finding { background: #ff7b72; }
|
|
532
|
+
.legend-color.file { background: #58a6ff; }
|
|
533
|
+
.legend-color.receipt { background: #3fb950; }
|
|
534
|
+
.legend-color.route { background: #a371f7; }
|
|
535
|
+
|
|
536
|
+
.main {
|
|
537
|
+
flex: 1;
|
|
538
|
+
overflow: auto;
|
|
539
|
+
padding: 2rem;
|
|
540
|
+
display: flex;
|
|
541
|
+
flex-direction: column;
|
|
542
|
+
align-items: center;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.mermaid {
|
|
546
|
+
background: var(--surface);
|
|
547
|
+
border-radius: 12px;
|
|
548
|
+
padding: 2rem;
|
|
549
|
+
min-width: 600px;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.controls {
|
|
553
|
+
margin-bottom: 1rem;
|
|
554
|
+
display: flex;
|
|
555
|
+
gap: 0.5rem;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
button {
|
|
559
|
+
background: var(--surface);
|
|
560
|
+
color: var(--text);
|
|
561
|
+
border: 1px solid var(--border);
|
|
562
|
+
padding: 0.5rem 1rem;
|
|
563
|
+
border-radius: 6px;
|
|
564
|
+
cursor: pointer;
|
|
565
|
+
font-size: 0.875rem;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
button:hover {
|
|
569
|
+
border-color: var(--accent);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.node-list {
|
|
573
|
+
max-height: 300px;
|
|
574
|
+
overflow-y: auto;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.node-item {
|
|
578
|
+
padding: 0.5rem;
|
|
579
|
+
border-radius: 4px;
|
|
580
|
+
margin-bottom: 0.25rem;
|
|
581
|
+
font-size: 0.8rem;
|
|
582
|
+
background: var(--bg);
|
|
583
|
+
cursor: pointer;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.node-item:hover {
|
|
587
|
+
border: 1px solid var(--accent);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.node-type {
|
|
591
|
+
font-size: 0.65rem;
|
|
592
|
+
color: var(--text-muted);
|
|
593
|
+
text-transform: uppercase;
|
|
594
|
+
}
|
|
595
|
+
</style>
|
|
596
|
+
</head>
|
|
597
|
+
<body>
|
|
598
|
+
<div class="header">
|
|
599
|
+
<h1>Proof Graph</h1>
|
|
600
|
+
<div class="stats">
|
|
601
|
+
<div class="stat">
|
|
602
|
+
<div class="stat-value">${stats.totalNodes || 0}</div>
|
|
603
|
+
<div class="stat-label">Nodes</div>
|
|
604
|
+
</div>
|
|
605
|
+
<div class="stat">
|
|
606
|
+
<div class="stat-value">${stats.totalEdges || 0}</div>
|
|
607
|
+
<div class="stat-label">Edges</div>
|
|
608
|
+
</div>
|
|
609
|
+
<div class="stat">
|
|
610
|
+
<div class="stat-value">${stats.findingsBySeverity?.BLOCK || 0}</div>
|
|
611
|
+
<div class="stat-label">Blocks</div>
|
|
612
|
+
</div>
|
|
613
|
+
<div class="stat">
|
|
614
|
+
<div class="stat-value">${stats.findingsBySeverity?.WARN || 0}</div>
|
|
615
|
+
<div class="stat-label">Warns</div>
|
|
616
|
+
</div>
|
|
617
|
+
</div>
|
|
618
|
+
</div>
|
|
619
|
+
|
|
620
|
+
<div class="container">
|
|
621
|
+
<div class="sidebar">
|
|
622
|
+
<h2>Cross-References</h2>
|
|
623
|
+
${receiptCards.length > 0 ? receiptCards.join('') : '<p style="color: var(--text-muted); font-size: 0.875rem;">No receipts found</p>'}
|
|
624
|
+
|
|
625
|
+
<div class="legend">
|
|
626
|
+
<h2>Legend</h2>
|
|
627
|
+
<div class="legend-item">
|
|
628
|
+
<div class="legend-color finding"></div>
|
|
629
|
+
<span>Finding</span>
|
|
630
|
+
</div>
|
|
631
|
+
<div class="legend-item">
|
|
632
|
+
<div class="legend-color file"></div>
|
|
633
|
+
<span>File</span>
|
|
634
|
+
</div>
|
|
635
|
+
<div class="legend-item">
|
|
636
|
+
<div class="legend-color receipt"></div>
|
|
637
|
+
<span>Receipt</span>
|
|
638
|
+
</div>
|
|
639
|
+
<div class="legend-item">
|
|
640
|
+
<div class="legend-color route"></div>
|
|
641
|
+
<span>Route</span>
|
|
642
|
+
</div>
|
|
643
|
+
</div>
|
|
644
|
+
|
|
645
|
+
<h2 style="margin-top: 1.5rem;">Nodes (${(graph.nodes || []).length})</h2>
|
|
646
|
+
<div class="node-list">
|
|
647
|
+
${(graph.nodes || []).slice(0, 50).map(n => `
|
|
648
|
+
<div class="node-item">
|
|
649
|
+
<div class="node-type">${n.type}</div>
|
|
650
|
+
<div>${(n.label || n.id).slice(0, 40)}</div>
|
|
651
|
+
</div>
|
|
652
|
+
`).join('')}
|
|
653
|
+
${(graph.nodes || []).length > 50 ? `<p style="color: var(--text-muted); padding: 0.5rem;">... and ${(graph.nodes || []).length - 50} more</p>` : ''}
|
|
654
|
+
</div>
|
|
655
|
+
</div>
|
|
656
|
+
|
|
657
|
+
<div class="main">
|
|
658
|
+
<div class="controls">
|
|
659
|
+
<button onclick="location.reload()">Refresh</button>
|
|
660
|
+
<button onclick="downloadSVG()">Download SVG</button>
|
|
661
|
+
</div>
|
|
662
|
+
|
|
663
|
+
<pre class="mermaid">
|
|
664
|
+
${mermaidCode}
|
|
665
|
+
</pre>
|
|
666
|
+
</div>
|
|
667
|
+
</div>
|
|
668
|
+
|
|
669
|
+
<script>
|
|
670
|
+
mermaid.initialize({
|
|
671
|
+
startOnLoad: true,
|
|
672
|
+
theme: 'dark',
|
|
673
|
+
securityLevel: 'loose',
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
function downloadSVG() {
|
|
677
|
+
const svg = document.querySelector('.mermaid svg');
|
|
678
|
+
if (svg) {
|
|
679
|
+
const svgData = new XMLSerializer().serializeToString(svg);
|
|
680
|
+
const blob = new Blob([svgData], { type: 'image/svg+xml' });
|
|
681
|
+
const url = URL.createObjectURL(blob);
|
|
682
|
+
const a = document.createElement('a');
|
|
683
|
+
a.href = url;
|
|
684
|
+
a.download = 'proof-graph.svg';
|
|
685
|
+
a.click();
|
|
686
|
+
URL.revokeObjectURL(url);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Embedded graph data
|
|
691
|
+
window.PROOF_GRAPH = ${JSON.stringify(graph, null, 2)};
|
|
692
|
+
</script>
|
|
693
|
+
</body>
|
|
694
|
+
</html>`;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
698
|
+
// PACK BUILDER
|
|
699
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Build proof graph pack
|
|
703
|
+
* @param {string} repoRoot - Repository root
|
|
704
|
+
* @param {object} options - Build options
|
|
705
|
+
* @returns {object} Build result
|
|
706
|
+
*/
|
|
707
|
+
async function buildProofGraphPack(repoRoot, options = {}) {
|
|
708
|
+
const { outputDir, createZip = false } = options;
|
|
709
|
+
|
|
710
|
+
// Load graph data
|
|
711
|
+
let graph = loadProofGraph(repoRoot);
|
|
712
|
+
|
|
713
|
+
if (!graph) {
|
|
714
|
+
graph = { nodes: [], edges: [], metadata: { empty: true } };
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// Load receipts and proof runs
|
|
718
|
+
const receipts = collectReceiptReferences(repoRoot);
|
|
719
|
+
const proofRuns = loadProofRuns(repoRoot);
|
|
720
|
+
|
|
721
|
+
// Enhance graph
|
|
722
|
+
const enhancedGraph = enhanceGraphWithReceipts(graph, receipts, proofRuns);
|
|
723
|
+
|
|
724
|
+
// Generate formats
|
|
725
|
+
const dotFormat = graphToDot(enhancedGraph);
|
|
726
|
+
const mermaidFormat = graphToMermaid(enhancedGraph);
|
|
727
|
+
const htmlVisualization = generateHtmlVisualization(enhancedGraph);
|
|
728
|
+
|
|
729
|
+
// Build pack
|
|
730
|
+
const builder = createPackBuilder(PACK_TYPE.PROOF_GRAPH, repoRoot);
|
|
731
|
+
|
|
732
|
+
if (outputDir) {
|
|
733
|
+
builder.outputTo(outputDir);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
builder
|
|
737
|
+
.addArtifact('proof-graph.json', JSON.stringify(enhancedGraph, null, 2), ARTIFACT_TYPE.JSON, 'Enhanced proof graph data')
|
|
738
|
+
.addArtifact('proof-graph.dot', dotFormat, ARTIFACT_TYPE.DOT, 'Graphviz DOT format')
|
|
739
|
+
.addArtifact('proof-graph.mmd', mermaidFormat, ARTIFACT_TYPE.MERMAID, 'Mermaid diagram format')
|
|
740
|
+
.addArtifact('visualization.html', htmlVisualization, ARTIFACT_TYPE.HTML, 'Interactive graph visualization')
|
|
741
|
+
.withMetadata({
|
|
742
|
+
nodeCount: enhancedGraph.nodes?.length || 0,
|
|
743
|
+
edgeCount: enhancedGraph.edges?.length || 0,
|
|
744
|
+
hasShipReceipt: !!receipts.ship,
|
|
745
|
+
hasRealityReceipt: !!receipts.reality,
|
|
746
|
+
hasScanReceipt: !!receipts.scan,
|
|
747
|
+
proofRunCount: proofRuns.length,
|
|
748
|
+
statistics: enhancedGraph.statistics,
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
if (createZip) {
|
|
752
|
+
return builder.buildZip();
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
return builder.build();
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
759
|
+
// EXPORTS
|
|
760
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
761
|
+
|
|
762
|
+
module.exports = {
|
|
763
|
+
// Loading
|
|
764
|
+
loadProofGraph,
|
|
765
|
+
loadProofRuns,
|
|
766
|
+
buildGraphFromScan,
|
|
767
|
+
|
|
768
|
+
// Enhancement
|
|
769
|
+
enhanceGraphWithReceipts,
|
|
770
|
+
computeGraphStats,
|
|
771
|
+
|
|
772
|
+
// Formatters
|
|
773
|
+
graphToDot,
|
|
774
|
+
graphToMermaid,
|
|
775
|
+
generateHtmlVisualization,
|
|
776
|
+
|
|
777
|
+
// Builder
|
|
778
|
+
buildProofGraphPack,
|
|
779
|
+
};
|