vibe-splain 2.7.3 → 3.0.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/commands/export.d.ts +1 -0
- package/dist/commands/export.js +19 -0
- package/dist/commands/serve.d.ts +1 -1
- package/dist/commands/serve.js +2 -2
- package/dist/export/ArtifactBundleWriter.d.ts +22 -0
- package/dist/export/ArtifactBundleWriter.js +56 -0
- package/dist/export/ExportOrchestrator.d.ts +12 -0
- package/dist/export/ExportOrchestrator.js +72 -0
- package/dist/export/Watcher.d.ts +1 -0
- package/dist/export/Watcher.js +47 -0
- package/dist/export/renderers/AgentMarkdownRenderer.d.ts +8 -0
- package/dist/export/renderers/AgentMarkdownRenderer.js +90 -0
- package/dist/export/renderers/DeltaRenderer.d.ts +6 -0
- package/dist/export/renderers/DeltaRenderer.js +22 -0
- package/dist/export/renderers/GraphRenderer.d.ts +9 -0
- package/dist/export/renderers/GraphRenderer.js +18 -0
- package/dist/export/renderers/HtmlRenderer.d.ts +6 -0
- package/dist/export/renderers/HtmlRenderer.js +54 -0
- package/dist/export/renderers/JsonRenderer.d.ts +6 -0
- package/dist/export/renderers/JsonRenderer.js +12 -0
- package/dist/export/renderers/RawAnalysisRenderer.d.ts +6 -0
- package/dist/export/renderers/RawAnalysisRenderer.js +12 -0
- package/dist/export/renderers/Renderer.d.ts +5 -0
- package/dist/export/renderers/Renderer.js +2 -0
- package/dist/export/renderers/ValidationRenderer.d.ts +6 -0
- package/dist/export/renderers/ValidationRenderer.js +14 -0
- package/dist/index.js +722 -574
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +3 -3
- package/dist/mcp/tools/mark_stale.d.ts +1 -1
- package/dist/mcp/tools/mark_stale.js +9 -3
- package/dist/mcp/tools/scan_project.d.ts +1 -1
- package/dist/mcp/tools/scan_project.js +24 -4
- package/dist/mcp/tools/set_project_brief.d.ts +1 -1
- package/dist/mcp/tools/set_project_brief.js +9 -3
- package/dist/mcp/tools/write_decision_card.d.ts +1 -1
- package/dist/mcp/tools/write_decision_card.js +15 -4
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function exportCommand(projectRoot: string, options: any): Promise<void>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readDossier } from '@vibe-splain/brain';
|
|
2
|
+
import { ExportOrchestrator } from '../export/ExportOrchestrator.js';
|
|
3
|
+
export async function exportCommand(projectRoot, options) {
|
|
4
|
+
const root = projectRoot || process.cwd();
|
|
5
|
+
console.error(`[vibe-splain] Exporting dossier for ${root}`);
|
|
6
|
+
const dossier = await readDossier(root);
|
|
7
|
+
if (!dossier) {
|
|
8
|
+
console.error('[vibe-splain] Dossier not found. Run scan first.');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const orchestrator = new ExportOrchestrator(root);
|
|
12
|
+
await orchestrator.writeBundle(dossier, {
|
|
13
|
+
format: options.format,
|
|
14
|
+
budget: options.budget ? parseInt(options.budget, 10) : undefined,
|
|
15
|
+
scope: options.scope,
|
|
16
|
+
});
|
|
17
|
+
console.error('[vibe-splain] Export complete.');
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=export.js.map
|
package/dist/commands/serve.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function serveCommand(): Promise<void>;
|
|
1
|
+
export declare function serveCommand(options?: any): Promise<void>;
|
package/dist/commands/serve.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { startMCPServer } from '../mcp/server.js';
|
|
2
|
-
export async function serveCommand() {
|
|
2
|
+
export async function serveCommand(options) {
|
|
3
3
|
console.error('[vibe-splain] Starting MCP server...');
|
|
4
|
-
await startMCPServer();
|
|
4
|
+
await startMCPServer(options);
|
|
5
5
|
// Process stays alive — do NOT call process.exit() here
|
|
6
6
|
}
|
|
7
7
|
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface Artifact {
|
|
2
|
+
type: string;
|
|
3
|
+
path: string;
|
|
4
|
+
content: string | Buffer;
|
|
5
|
+
}
|
|
6
|
+
export interface ManifestArtifact {
|
|
7
|
+
type: string;
|
|
8
|
+
path: string;
|
|
9
|
+
checksum: string;
|
|
10
|
+
sizeBytes: number;
|
|
11
|
+
}
|
|
12
|
+
export interface ArtifactManifest {
|
|
13
|
+
schemaVersion: string;
|
|
14
|
+
generatedAt: string;
|
|
15
|
+
projectRoot: string;
|
|
16
|
+
artifacts: ManifestArtifact[];
|
|
17
|
+
}
|
|
18
|
+
export declare class ArtifactBundleWriter {
|
|
19
|
+
private projectRoot;
|
|
20
|
+
constructor(projectRoot: string);
|
|
21
|
+
writeBundle(artifacts: Artifact[]): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { writeFile, mkdir, rm, rename } from 'fs/promises';
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
export class ArtifactBundleWriter {
|
|
5
|
+
projectRoot;
|
|
6
|
+
constructor(projectRoot) {
|
|
7
|
+
this.projectRoot = projectRoot;
|
|
8
|
+
}
|
|
9
|
+
async writeBundle(artifacts) {
|
|
10
|
+
const outputDir = join(this.projectRoot, '.vibe-splainer');
|
|
11
|
+
const stagingDir = join(this.projectRoot, '.vibe-splainer.tmp');
|
|
12
|
+
try {
|
|
13
|
+
await rm(stagingDir, { recursive: true, force: true });
|
|
14
|
+
const { existsSync } = await import('fs');
|
|
15
|
+
const { cp } = await import('fs/promises');
|
|
16
|
+
if (existsSync(outputDir)) {
|
|
17
|
+
await cp(outputDir, stagingDir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
await mkdir(stagingDir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
const manifestArtifacts = [];
|
|
23
|
+
for (const artifact of artifacts) {
|
|
24
|
+
const destPath = join(stagingDir, artifact.path);
|
|
25
|
+
await mkdir(join(destPath, '..'), { recursive: true });
|
|
26
|
+
await writeFile(destPath, artifact.content);
|
|
27
|
+
const contentStr = artifact.content;
|
|
28
|
+
const buffer = typeof contentStr === 'string' ? Buffer.from(contentStr, 'utf-8') : contentStr;
|
|
29
|
+
manifestArtifacts.push({
|
|
30
|
+
type: artifact.type,
|
|
31
|
+
path: artifact.path,
|
|
32
|
+
checksum: 'sha256:' + createHash('sha256').update(buffer).digest('hex'),
|
|
33
|
+
sizeBytes: buffer.length,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const manifest = {
|
|
37
|
+
schemaVersion: '1.0.0',
|
|
38
|
+
generatedAt: new Date().toISOString(),
|
|
39
|
+
projectRoot: this.projectRoot,
|
|
40
|
+
artifacts: manifestArtifacts,
|
|
41
|
+
};
|
|
42
|
+
await writeFile(join(stagingDir, 'artifact_manifest.json'), JSON.stringify(manifest, null, 2), 'utf8');
|
|
43
|
+
// Atomic rename.
|
|
44
|
+
// Rename fails if destination is a non-empty directory.
|
|
45
|
+
// So we first delete the existing outputDir.
|
|
46
|
+
// Since it's inside the project root, it's safe to do rm -rf .vibe-splainer
|
|
47
|
+
await rm(outputDir, { recursive: true, force: true });
|
|
48
|
+
await rename(stagingDir, outputDir);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
await rm(stagingDir, { recursive: true, force: true });
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=ArtifactBundleWriter.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Dossier, AnalysisStore, ImportGraph } from '@vibe-splain/brain';
|
|
2
|
+
export interface ExportOptions {
|
|
3
|
+
format?: 'json' | 'html' | 'markdown' | 'delta';
|
|
4
|
+
budget?: number;
|
|
5
|
+
scope?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class ExportOrchestrator {
|
|
8
|
+
private projectRoot;
|
|
9
|
+
constructor(projectRoot: string);
|
|
10
|
+
writeBundle(dossier: Dossier, options?: ExportOptions, store?: AnalysisStore, graph?: ImportGraph): Promise<void>;
|
|
11
|
+
private buildViewModel;
|
|
12
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { readAnalysis, RecommendationEngine } from '@vibe-splain/brain';
|
|
2
|
+
import { ArtifactBundleWriter } from './ArtifactBundleWriter.js';
|
|
3
|
+
import { JsonRenderer } from './renderers/JsonRenderer.js';
|
|
4
|
+
import { HtmlRenderer } from './renderers/HtmlRenderer.js';
|
|
5
|
+
import { DeltaRenderer } from './renderers/DeltaRenderer.js';
|
|
6
|
+
import { AgentMarkdownRenderer } from './renderers/AgentMarkdownRenderer.js';
|
|
7
|
+
import { ValidationRenderer } from './renderers/ValidationRenderer.js';
|
|
8
|
+
import { RawAnalysisRenderer } from './renderers/RawAnalysisRenderer.js';
|
|
9
|
+
import { GraphRenderer } from './renderers/GraphRenderer.js';
|
|
10
|
+
export class ExportOrchestrator {
|
|
11
|
+
projectRoot;
|
|
12
|
+
constructor(projectRoot) {
|
|
13
|
+
this.projectRoot = projectRoot;
|
|
14
|
+
}
|
|
15
|
+
async writeBundle(dossier, options = {}, store, graph) {
|
|
16
|
+
const finalStore = store || await readAnalysis(this.projectRoot);
|
|
17
|
+
if (!finalStore) {
|
|
18
|
+
throw new Error('Analysis store not found. Scan the project first.');
|
|
19
|
+
}
|
|
20
|
+
// Aggressive Boilerplate Culling
|
|
21
|
+
for (const p of dossier.pillars) {
|
|
22
|
+
p.decisions = p.decisions.filter(c => !(c.severity === 1 && c.category === 'Convention'));
|
|
23
|
+
p.cardCount = p.decisions.length;
|
|
24
|
+
}
|
|
25
|
+
const viewModel = this.buildViewModel(dossier, finalStore);
|
|
26
|
+
// Always render JSON, Delta, Validation, and Raw Analysis.
|
|
27
|
+
const artifacts = [];
|
|
28
|
+
artifacts.push(...await new JsonRenderer().render(viewModel, finalStore));
|
|
29
|
+
artifacts.push(...await new DeltaRenderer().render(viewModel, finalStore));
|
|
30
|
+
artifacts.push(...await new ValidationRenderer().render(viewModel, finalStore));
|
|
31
|
+
artifacts.push(...await new RawAnalysisRenderer().render(viewModel, finalStore));
|
|
32
|
+
if (graph) {
|
|
33
|
+
artifacts.push(...await new GraphRenderer(graph).render(viewModel, finalStore));
|
|
34
|
+
}
|
|
35
|
+
// Determine additional formats
|
|
36
|
+
const formats = ['html', 'markdown']; // default
|
|
37
|
+
if (options.format && options.format !== 'json' && options.format !== 'delta') {
|
|
38
|
+
formats.length = 0;
|
|
39
|
+
formats.push(options.format);
|
|
40
|
+
}
|
|
41
|
+
if (formats.includes('html')) {
|
|
42
|
+
artifacts.push(...await new HtmlRenderer().render(viewModel, finalStore));
|
|
43
|
+
}
|
|
44
|
+
if (formats.includes('markdown')) {
|
|
45
|
+
artifacts.push(...await new AgentMarkdownRenderer(options.budget).render(viewModel, finalStore));
|
|
46
|
+
}
|
|
47
|
+
const writer = new ArtifactBundleWriter(this.projectRoot);
|
|
48
|
+
await writer.writeBundle(artifacts);
|
|
49
|
+
}
|
|
50
|
+
buildViewModel(dossier, store) {
|
|
51
|
+
const recommendations = {};
|
|
52
|
+
for (const file of dossier.map.topGravity) {
|
|
53
|
+
const persisted = store.files[file];
|
|
54
|
+
if (persisted) {
|
|
55
|
+
recommendations[file] = RecommendationEngine.generateRecommendations(persisted);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
for (const file of dossier.map.topHeat) {
|
|
59
|
+
if (!recommendations[file]) {
|
|
60
|
+
const persisted = store.files[file];
|
|
61
|
+
if (persisted) {
|
|
62
|
+
recommendations[file] = RecommendationEngine.generateRecommendations(persisted);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
...dossier,
|
|
68
|
+
recommendations,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=ExportOrchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function startWatcher(projectRoot: string, watchedPaths: string[]): void;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import chokidar from 'chokidar';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import { readFile } from 'fs/promises';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { readDossier } from '@vibe-splain/brain';
|
|
6
|
+
import { ExportOrchestrator } from './ExportOrchestrator.js';
|
|
7
|
+
export function startWatcher(projectRoot, watchedPaths) {
|
|
8
|
+
const watcher = chokidar.watch(watchedPaths.length > 0 ? watchedPaths : projectRoot, {
|
|
9
|
+
ignoreInitial: true,
|
|
10
|
+
ignored: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.vibe-splainer/**'],
|
|
11
|
+
persistent: true,
|
|
12
|
+
});
|
|
13
|
+
watcher.on('change', async (filepath) => {
|
|
14
|
+
try {
|
|
15
|
+
const dossier = await readDossier(projectRoot);
|
|
16
|
+
if (!dossier)
|
|
17
|
+
return;
|
|
18
|
+
const content = await readFile(filepath, 'utf8');
|
|
19
|
+
const newHash = createHash('sha256').update(content).digest('hex');
|
|
20
|
+
let mutated = false;
|
|
21
|
+
for (const pillar of dossier.pillars) {
|
|
22
|
+
for (const card of pillar.decisions) {
|
|
23
|
+
if (!card.primaryFile)
|
|
24
|
+
continue;
|
|
25
|
+
const absMatch = filepath === join(projectRoot, card.primaryFile) || filepath.endsWith('/' + card.primaryFile);
|
|
26
|
+
if (absMatch && card.lastScannedHash !== newHash) {
|
|
27
|
+
card.status = 'stale';
|
|
28
|
+
const rel = card.primaryFile;
|
|
29
|
+
if (!dossier.stalePaths.includes(rel))
|
|
30
|
+
dossier.stalePaths.push(rel);
|
|
31
|
+
mutated = true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (mutated) {
|
|
36
|
+
const orchestrator = new ExportOrchestrator(projectRoot);
|
|
37
|
+
await orchestrator.writeBundle(dossier);
|
|
38
|
+
console.error(`[vibe-splain] File changed: ${filepath}. Dossier artifacts updated.`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.error('[vibe-splain] Watcher error:', err);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
console.error('[vibe-splain] File watcher started');
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=Watcher.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DossierViewModel, AnalysisStore } from '@vibe-splain/brain';
|
|
2
|
+
import type { Renderer } from './Renderer.js';
|
|
3
|
+
import type { Artifact } from '../ArtifactBundleWriter.js';
|
|
4
|
+
export declare class AgentMarkdownRenderer implements Renderer {
|
|
5
|
+
private budget;
|
|
6
|
+
constructor(budget?: number);
|
|
7
|
+
render(viewModel: DossierViewModel, store: AnalysisStore): Artifact[];
|
|
8
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export class AgentMarkdownRenderer {
|
|
2
|
+
budget;
|
|
3
|
+
constructor(budget = 8000) {
|
|
4
|
+
this.budget = budget;
|
|
5
|
+
}
|
|
6
|
+
render(viewModel, store) {
|
|
7
|
+
let md = `# Architectural Dossier: ${viewModel.projectRoot}\n\n`;
|
|
8
|
+
if (viewModel.map.brief) {
|
|
9
|
+
md += `## Project Brief\n${viewModel.map.brief}\n\n`;
|
|
10
|
+
}
|
|
11
|
+
md += `## Stack & Entrypoints\n`;
|
|
12
|
+
md += `- Stack: ${viewModel.map.stack.join(', ')}\n`;
|
|
13
|
+
md += `- Entrypoints: ${viewModel.map.entrypoints.join(', ')}\n\n`;
|
|
14
|
+
// Flatten decisions
|
|
15
|
+
const allDecisions = viewModel.pillars.flatMap(p => p.decisions).concat(viewModel.wildDiscoveries);
|
|
16
|
+
const uniqueDecisions = new Map();
|
|
17
|
+
for (const d of allDecisions) {
|
|
18
|
+
if (d.primaryFile && !uniqueDecisions.has(d.primaryFile)) {
|
|
19
|
+
uniqueDecisions.set(d.primaryFile, d);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const tier1 = [];
|
|
23
|
+
const tier2 = [];
|
|
24
|
+
const tier3 = [];
|
|
25
|
+
// Sort files by gravity
|
|
26
|
+
const sortedFiles = Object.values(store.files)
|
|
27
|
+
.filter(f => f.isRealSource)
|
|
28
|
+
.sort((a, b) => b.gravity - a.gravity);
|
|
29
|
+
for (const f of sortedFiles) {
|
|
30
|
+
const card = uniqueDecisions.get(f.relativePath);
|
|
31
|
+
const isCritical = card && card.severity >= 4;
|
|
32
|
+
if (f.gravity >= 70 || isCritical) {
|
|
33
|
+
tier1.push(f.relativePath);
|
|
34
|
+
}
|
|
35
|
+
else if (f.gravity >= 40 || card) {
|
|
36
|
+
tier2.push(f.relativePath);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
tier3.push(f.relativePath);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
md += `## Tier 1: Critical Files & Risks\n\n`;
|
|
43
|
+
for (const path of tier1) {
|
|
44
|
+
const f = store.files[path];
|
|
45
|
+
const card = uniqueDecisions.get(path);
|
|
46
|
+
const recs = viewModel.recommendations[path] || [];
|
|
47
|
+
md += `### ${path}\n`;
|
|
48
|
+
md += `- Gravity: ${Math.round(f.gravity)} | Heat: ${Math.round(f.heat)}\n`;
|
|
49
|
+
md += `- Domain: ${f.productDomain} | Role: ${f.frameworkRole}\n`;
|
|
50
|
+
if (card) {
|
|
51
|
+
md += `\n**Verdict**: ${card.thesis}\n`;
|
|
52
|
+
md += `**Severity**: ${card.severity} | **Category**: ${card.category}\n`;
|
|
53
|
+
md += `**Narrative**: ${card.narrative}\n`;
|
|
54
|
+
}
|
|
55
|
+
if (recs.length > 0) {
|
|
56
|
+
md += `\n**Safe Patch Strategies**:\n`;
|
|
57
|
+
for (const r of recs) {
|
|
58
|
+
md += `- **${r.strategy}**: ${r.description}\n`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
md += `\n---\n\n`;
|
|
62
|
+
}
|
|
63
|
+
md += `## Tier 2: Important Files\n\n`;
|
|
64
|
+
for (const path of tier2) {
|
|
65
|
+
const f = store.files[path];
|
|
66
|
+
const card = uniqueDecisions.get(path);
|
|
67
|
+
md += `- **${path}** (Gravity: ${Math.round(f.gravity)})`;
|
|
68
|
+
if (card) {
|
|
69
|
+
md += ` — ${card.thesis}`;
|
|
70
|
+
}
|
|
71
|
+
md += `\n`;
|
|
72
|
+
}
|
|
73
|
+
md += `\n`;
|
|
74
|
+
md += `## Tier 3: Index\n\n`;
|
|
75
|
+
for (const path of tier3) {
|
|
76
|
+
const f = store.files[path];
|
|
77
|
+
md += `- ${path} (Gravity: ${Math.round(f.gravity)})\n`;
|
|
78
|
+
}
|
|
79
|
+
// In a real robust implementation, we would truncate tiers starting from Tier 3 to fit the budget.
|
|
80
|
+
// Given the simplicity, we'll return the full markdown.
|
|
81
|
+
return [
|
|
82
|
+
{
|
|
83
|
+
type: 'markdown',
|
|
84
|
+
path: 'dossier.agent.md',
|
|
85
|
+
content: md,
|
|
86
|
+
}
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=AgentMarkdownRenderer.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DossierViewModel, AnalysisStore } from '@vibe-splain/brain';
|
|
2
|
+
import type { Renderer } from './Renderer.js';
|
|
3
|
+
import type { Artifact } from '../ArtifactBundleWriter.js';
|
|
4
|
+
export declare class DeltaRenderer implements Renderer {
|
|
5
|
+
render(_viewModel: DossierViewModel, store: AnalysisStore): Artifact[];
|
|
6
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export class DeltaRenderer {
|
|
2
|
+
render(_viewModel, store) {
|
|
3
|
+
const deltaTargets = Object.values(store.files)
|
|
4
|
+
.filter(pf => pf.isRealSource)
|
|
5
|
+
.sort((a, b) => b.gravity - a.gravity)
|
|
6
|
+
.map(pf => ({
|
|
7
|
+
path: pf.relativePath,
|
|
8
|
+
gravity: Math.round(pf.gravity),
|
|
9
|
+
isLoadBearing: pf.canonicalLoadBearing,
|
|
10
|
+
blastRadius: pf.importedBy,
|
|
11
|
+
pillarHint: pf.pillarHint,
|
|
12
|
+
}));
|
|
13
|
+
return [
|
|
14
|
+
{
|
|
15
|
+
type: 'delta',
|
|
16
|
+
path: 'delta_targets.json',
|
|
17
|
+
content: JSON.stringify(deltaTargets, null, 2),
|
|
18
|
+
}
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=DeltaRenderer.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ImportGraph } from '@vibe-splain/brain';
|
|
2
|
+
import type { DossierViewModel, AnalysisStore } from '@vibe-splain/brain';
|
|
3
|
+
import type { Renderer } from './Renderer.js';
|
|
4
|
+
import type { Artifact } from '../ArtifactBundleWriter.js';
|
|
5
|
+
export declare class GraphRenderer implements Renderer {
|
|
6
|
+
private graph?;
|
|
7
|
+
constructor(graph?: ImportGraph | undefined);
|
|
8
|
+
render(_viewModel: DossierViewModel, _store: AnalysisStore): Artifact[];
|
|
9
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export class GraphRenderer {
|
|
2
|
+
graph;
|
|
3
|
+
constructor(graph) {
|
|
4
|
+
this.graph = graph;
|
|
5
|
+
}
|
|
6
|
+
render(_viewModel, _store) {
|
|
7
|
+
if (!this.graph)
|
|
8
|
+
return [];
|
|
9
|
+
return [
|
|
10
|
+
{
|
|
11
|
+
type: 'graph',
|
|
12
|
+
path: 'graph.json',
|
|
13
|
+
content: JSON.stringify(this.graph, null, 2),
|
|
14
|
+
}
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=GraphRenderer.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DossierViewModel, AnalysisStore } from '@vibe-splain/brain';
|
|
2
|
+
import type { Renderer } from './Renderer.js';
|
|
3
|
+
import type { Artifact } from '../ArtifactBundleWriter.js';
|
|
4
|
+
export declare class HtmlRenderer implements Renderer {
|
|
5
|
+
render(viewModel: DossierViewModel, _store: AnalysisStore): Artifact[];
|
|
6
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { join, dirname, relative } from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
function getAllFiles(dirPath, arrayOfFiles = []) {
|
|
6
|
+
const files = readdirSync(dirPath);
|
|
7
|
+
files.forEach(function (file) {
|
|
8
|
+
const fullPath = join(dirPath, file);
|
|
9
|
+
if (statSync(fullPath).isDirectory()) {
|
|
10
|
+
arrayOfFiles = getAllFiles(fullPath, arrayOfFiles);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
arrayOfFiles.push(fullPath);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
return arrayOfFiles;
|
|
17
|
+
}
|
|
18
|
+
export class HtmlRenderer {
|
|
19
|
+
render(viewModel, _store) {
|
|
20
|
+
let templateDir = join(__dirname, '..', '..', 'ui'); // from dist/export/renderers -> dist/ui
|
|
21
|
+
if (!existsSync(templateDir)) {
|
|
22
|
+
// Unbundled path (from cli/src/export/renderers -> cli/dist/ui)
|
|
23
|
+
templateDir = join(__dirname, '..', '..', '..', '..', 'dist', 'ui');
|
|
24
|
+
}
|
|
25
|
+
if (!existsSync(templateDir)) {
|
|
26
|
+
console.error('[vibe-splain] UI template not found at', templateDir, '- skipping UI regeneration');
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
const artifacts = [];
|
|
30
|
+
const allFiles = getAllFiles(templateDir);
|
|
31
|
+
for (const file of allFiles) {
|
|
32
|
+
const relPath = relative(templateDir, file);
|
|
33
|
+
if (relPath === 'index.html') {
|
|
34
|
+
const templateHtml = readFileSync(file, 'utf8');
|
|
35
|
+
const injection = `<script>window.__VIBE_DOSSIER__ = ${JSON.stringify(viewModel)};</script>`;
|
|
36
|
+
const bakedHtml = templateHtml.replace('<!-- VIBE_DOSSIER_INJECTION_POINT -->', injection);
|
|
37
|
+
artifacts.push({
|
|
38
|
+
type: 'html',
|
|
39
|
+
path: join('ui', relPath),
|
|
40
|
+
content: bakedHtml,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
artifacts.push({
|
|
45
|
+
type: 'asset',
|
|
46
|
+
path: join('ui', relPath),
|
|
47
|
+
content: readFileSync(file),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return artifacts;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=HtmlRenderer.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DossierViewModel, AnalysisStore } from '@vibe-splain/brain';
|
|
2
|
+
import type { Renderer } from './Renderer.js';
|
|
3
|
+
import type { Artifact } from '../ArtifactBundleWriter.js';
|
|
4
|
+
export declare class JsonRenderer implements Renderer {
|
|
5
|
+
render(viewModel: DossierViewModel, _store: AnalysisStore): Artifact[];
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DossierViewModel, AnalysisStore } from '@vibe-splain/brain';
|
|
2
|
+
import type { Renderer } from './Renderer.js';
|
|
3
|
+
import type { Artifact } from '../ArtifactBundleWriter.js';
|
|
4
|
+
export declare class RawAnalysisRenderer implements Renderer {
|
|
5
|
+
render(_viewModel: DossierViewModel, store: AnalysisStore): Artifact[];
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DossierViewModel, AnalysisStore } from '@vibe-splain/brain';
|
|
2
|
+
import type { Renderer } from './Renderer.js';
|
|
3
|
+
import type { Artifact } from '../ArtifactBundleWriter.js';
|
|
4
|
+
export declare class ValidationRenderer implements Renderer {
|
|
5
|
+
render(viewModel: DossierViewModel, _store: AnalysisStore): Artifact[];
|
|
6
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export class ValidationRenderer {
|
|
2
|
+
render(viewModel, _store) {
|
|
3
|
+
if (!viewModel.map.validation)
|
|
4
|
+
return [];
|
|
5
|
+
return [
|
|
6
|
+
{
|
|
7
|
+
type: 'validation',
|
|
8
|
+
path: 'validation_report.json',
|
|
9
|
+
content: JSON.stringify(viewModel.map.validation, null, 2),
|
|
10
|
+
}
|
|
11
|
+
];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=ValidationRenderer.js.map
|