vibe-splain 2.7.3 → 3.1.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.
Files changed (39) hide show
  1. package/dist/commands/export.d.ts +1 -0
  2. package/dist/commands/export.js +19 -0
  3. package/dist/commands/serve.d.ts +1 -1
  4. package/dist/commands/serve.js +2 -2
  5. package/dist/export/ArtifactBundleWriter.d.ts +22 -0
  6. package/dist/export/ArtifactBundleWriter.js +74 -0
  7. package/dist/export/ExportOrchestrator.d.ts +12 -0
  8. package/dist/export/ExportOrchestrator.js +73 -0
  9. package/dist/export/Watcher.d.ts +1 -0
  10. package/dist/export/Watcher.js +55 -0
  11. package/dist/export/renderers/AgentMarkdownRenderer.d.ts +9 -0
  12. package/dist/export/renderers/AgentMarkdownRenderer.js +106 -0
  13. package/dist/export/renderers/DeltaRenderer.d.ts +6 -0
  14. package/dist/export/renderers/DeltaRenderer.js +22 -0
  15. package/dist/export/renderers/GraphRenderer.d.ts +9 -0
  16. package/dist/export/renderers/GraphRenderer.js +18 -0
  17. package/dist/export/renderers/HtmlRenderer.d.ts +6 -0
  18. package/dist/export/renderers/HtmlRenderer.js +77 -0
  19. package/dist/export/renderers/JsonRenderer.d.ts +6 -0
  20. package/dist/export/renderers/JsonRenderer.js +12 -0
  21. package/dist/export/renderers/RawAnalysisRenderer.d.ts +6 -0
  22. package/dist/export/renderers/RawAnalysisRenderer.js +12 -0
  23. package/dist/export/renderers/Renderer.d.ts +5 -0
  24. package/dist/export/renderers/Renderer.js +2 -0
  25. package/dist/export/renderers/ValidationRenderer.d.ts +6 -0
  26. package/dist/export/renderers/ValidationRenderer.js +14 -0
  27. package/dist/index.js +888 -660
  28. package/dist/mcp/server.d.ts +1 -1
  29. package/dist/mcp/server.js +3 -3
  30. package/dist/mcp/tools/mark_stale.d.ts +1 -1
  31. package/dist/mcp/tools/mark_stale.js +9 -3
  32. package/dist/mcp/tools/scan_project.d.ts +1 -1
  33. package/dist/mcp/tools/scan_project.js +25 -5
  34. package/dist/mcp/tools/set_project_brief.d.ts +1 -1
  35. package/dist/mcp/tools/set_project_brief.js +9 -3
  36. package/dist/mcp/tools/write_decision_card.d.ts +1 -1
  37. package/dist/mcp/tools/write_decision_card.js +15 -4
  38. package/dist/ui/index.html +2 -2
  39. 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
@@ -1 +1 @@
1
- export declare function serveCommand(): Promise<void>;
1
+ export declare function serveCommand(options?: any): Promise<void>;
@@ -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,74 @@
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
+ const oldDir = join(this.projectRoot, '.vibe-splainer.old');
13
+ try {
14
+ await rm(stagingDir, { recursive: true, force: true });
15
+ await rm(oldDir, { recursive: true, force: true });
16
+ const { existsSync } = await import('fs');
17
+ const { cp } = await import('fs/promises');
18
+ if (existsSync(outputDir)) {
19
+ await cp(outputDir, stagingDir, { recursive: true });
20
+ }
21
+ else {
22
+ await mkdir(stagingDir, { recursive: true });
23
+ }
24
+ const manifestArtifacts = [];
25
+ for (const artifact of artifacts) {
26
+ const destPath = join(stagingDir, artifact.path);
27
+ await mkdir(join(destPath, '..'), { recursive: true });
28
+ await writeFile(destPath, artifact.content);
29
+ const contentStr = artifact.content;
30
+ const buffer = typeof contentStr === 'string' ? Buffer.from(contentStr, 'utf-8') : contentStr;
31
+ manifestArtifacts.push({
32
+ type: artifact.type,
33
+ path: artifact.path,
34
+ checksum: 'sha256:' + createHash('sha256').update(buffer).digest('hex'),
35
+ sizeBytes: buffer.length,
36
+ });
37
+ }
38
+ const manifest = {
39
+ schemaVersion: '1.0.0',
40
+ generatedAt: new Date().toISOString(),
41
+ projectRoot: this.projectRoot,
42
+ artifacts: manifestArtifacts,
43
+ };
44
+ await writeFile(join(stagingDir, 'artifact_manifest.json'), JSON.stringify(manifest, null, 2), 'utf8');
45
+ // Atomic swap pattern:
46
+ // 1. Rename current -> old
47
+ // 2. Rename staging -> current
48
+ // 3. Remove old
49
+ let swapped = false;
50
+ if (existsSync(outputDir)) {
51
+ await rename(outputDir, oldDir);
52
+ swapped = true;
53
+ }
54
+ try {
55
+ await rename(stagingDir, outputDir);
56
+ }
57
+ catch (err) {
58
+ // Rollback if possible
59
+ if (swapped) {
60
+ await rename(oldDir, outputDir);
61
+ }
62
+ throw err;
63
+ }
64
+ if (swapped) {
65
+ await rm(oldDir, { recursive: true, force: true });
66
+ }
67
+ }
68
+ catch (err) {
69
+ await rm(stagingDir, { recursive: true, force: true });
70
+ throw err;
71
+ }
72
+ }
73
+ }
74
+ //# 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,73 @@
1
+ import { readAnalysis, readActionBindings, 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
+ const bindings = await readActionBindings(this.projectRoot);
21
+ // Aggressive Boilerplate Culling
22
+ for (const p of dossier.pillars) {
23
+ p.decisions = p.decisions.filter(c => !(c.severity === 1 && c.category === 'Convention'));
24
+ p.cardCount = p.decisions.length;
25
+ }
26
+ const viewModel = this.buildViewModel(dossier, finalStore);
27
+ // Always render JSON, Delta, Validation, and Raw Analysis.
28
+ const artifacts = [];
29
+ artifacts.push(...await new JsonRenderer().render(viewModel, finalStore));
30
+ artifacts.push(...await new DeltaRenderer().render(viewModel, finalStore));
31
+ artifacts.push(...await new ValidationRenderer().render(viewModel, finalStore));
32
+ artifacts.push(...await new RawAnalysisRenderer().render(viewModel, finalStore));
33
+ if (graph) {
34
+ artifacts.push(...await new GraphRenderer(graph).render(viewModel, finalStore));
35
+ }
36
+ // Determine additional formats
37
+ const formats = ['html', 'markdown']; // default
38
+ if (options.format && options.format !== 'json' && options.format !== 'delta') {
39
+ formats.length = 0;
40
+ formats.push(options.format);
41
+ }
42
+ if (formats.includes('html')) {
43
+ artifacts.push(...await new HtmlRenderer().render(viewModel, finalStore));
44
+ }
45
+ if (formats.includes('markdown')) {
46
+ artifacts.push(...await new AgentMarkdownRenderer(options.budget, bindings).render(viewModel, finalStore));
47
+ }
48
+ const writer = new ArtifactBundleWriter(this.projectRoot);
49
+ await writer.writeBundle(artifacts);
50
+ }
51
+ buildViewModel(dossier, store) {
52
+ const recommendations = {};
53
+ for (const file of dossier.map.topGravity) {
54
+ const persisted = store.files[file];
55
+ if (persisted) {
56
+ recommendations[file] = RecommendationEngine.generateRecommendations(persisted);
57
+ }
58
+ }
59
+ for (const file of dossier.map.topHeat) {
60
+ if (!recommendations[file]) {
61
+ const persisted = store.files[file];
62
+ if (persisted) {
63
+ recommendations[file] = RecommendationEngine.generateRecommendations(persisted);
64
+ }
65
+ }
66
+ }
67
+ return {
68
+ ...dossier,
69
+ recommendations,
70
+ };
71
+ }
72
+ }
73
+ //# sourceMappingURL=ExportOrchestrator.js.map
@@ -0,0 +1 @@
1
+ export declare function startWatcher(projectRoot: string, watchedPaths: string[]): Promise<void>;
@@ -0,0 +1,55 @@
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
+ const activeWatchers = new Map();
8
+ export async function startWatcher(projectRoot, watchedPaths) {
9
+ // Clean up existing watcher for this project to prevent resource leaks
10
+ const existing = activeWatchers.get(projectRoot);
11
+ if (existing) {
12
+ await existing.close();
13
+ activeWatchers.delete(projectRoot);
14
+ }
15
+ const watcher = chokidar.watch(watchedPaths.length > 0 ? watchedPaths : projectRoot, {
16
+ ignoreInitial: true,
17
+ ignored: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.vibe-splainer/**'],
18
+ persistent: true,
19
+ });
20
+ watcher.on('change', async (filepath) => {
21
+ try {
22
+ const dossier = await readDossier(projectRoot);
23
+ if (!dossier)
24
+ return;
25
+ const content = await readFile(filepath, 'utf8');
26
+ const newHash = createHash('sha256').update(content).digest('hex');
27
+ let mutated = false;
28
+ for (const pillar of dossier.pillars) {
29
+ for (const card of pillar.decisions) {
30
+ if (!card.primaryFile)
31
+ continue;
32
+ const absMatch = filepath === join(projectRoot, card.primaryFile) || filepath.endsWith('/' + card.primaryFile);
33
+ if (absMatch && card.lastScannedHash !== newHash) {
34
+ card.status = 'stale';
35
+ const rel = card.primaryFile;
36
+ if (!dossier.stalePaths.includes(rel))
37
+ dossier.stalePaths.push(rel);
38
+ mutated = true;
39
+ }
40
+ }
41
+ }
42
+ if (mutated) {
43
+ const orchestrator = new ExportOrchestrator(projectRoot);
44
+ await orchestrator.writeBundle(dossier);
45
+ console.error(`[vibe-splain] File changed: ${filepath}. Dossier artifacts updated.`);
46
+ }
47
+ }
48
+ catch (err) {
49
+ console.error('[vibe-splain] Watcher error:', err);
50
+ }
51
+ });
52
+ activeWatchers.set(projectRoot, watcher);
53
+ console.error('[vibe-splain] File watcher started');
54
+ }
55
+ //# sourceMappingURL=Watcher.js.map
@@ -0,0 +1,9 @@
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
+ private bindings;
7
+ constructor(budget?: number, bindings?: any | null);
8
+ render(viewModel: DossierViewModel, store: AnalysisStore): Artifact[];
9
+ }
@@ -0,0 +1,106 @@
1
+ export class AgentMarkdownRenderer {
2
+ budget;
3
+ bindings;
4
+ constructor(budget = 8000, bindings = null) {
5
+ this.budget = budget;
6
+ this.bindings = bindings;
7
+ }
8
+ render(viewModel, store) {
9
+ let md = `# Architectural Dossier: ${viewModel.projectRoot}\n\n`;
10
+ if (viewModel.map.brief) {
11
+ md += `## Project Brief\n${viewModel.map.brief}\n\n`;
12
+ }
13
+ md += `## Stack & Entrypoints\n`;
14
+ md += `- Stack: ${viewModel.map.stack.join(', ')}\n`;
15
+ md += `- Entrypoints: ${viewModel.map.entrypoints.join(', ')}\n\n`;
16
+ // Flatten decisions
17
+ const allDecisions = viewModel.pillars.flatMap(p => p.decisions).concat(viewModel.wildDiscoveries);
18
+ const uniqueDecisions = new Map();
19
+ for (const d of allDecisions) {
20
+ if (d.primaryFile && !uniqueDecisions.has(d.primaryFile)) {
21
+ uniqueDecisions.set(d.primaryFile, d);
22
+ }
23
+ }
24
+ const tier1 = [];
25
+ const tier2 = [];
26
+ const tier3 = [];
27
+ // Sort files by gravity
28
+ const sortedFiles = Object.values(store.files)
29
+ .filter(f => f.isRealSource)
30
+ .sort((a, b) => b.gravity - a.gravity);
31
+ for (const f of sortedFiles) {
32
+ const card = uniqueDecisions.get(f.relativePath);
33
+ const isCritical = card && card.severity >= 4;
34
+ if (f.gravity >= 70 || isCritical) {
35
+ tier1.push(f.relativePath);
36
+ }
37
+ else if (f.gravity >= 40 || card) {
38
+ tier2.push(f.relativePath);
39
+ }
40
+ else {
41
+ tier3.push(f.relativePath);
42
+ }
43
+ }
44
+ md += `## Tier 1: Critical Files & Risks\n\n`;
45
+ for (const path of tier1) {
46
+ const f = store.files[path];
47
+ const card = uniqueDecisions.get(path);
48
+ const recs = viewModel.recommendations[path] || [];
49
+ md += `### ${path}\n`;
50
+ md += `- Gravity: ${Math.round(f.gravity)} | Heat: ${Math.round(f.heat)}\n`;
51
+ md += `- Domain: ${f.productDomain} | Role: ${f.frameworkRole}\n`;
52
+ if (card) {
53
+ md += `\n**Verdict**: ${card.thesis}\n`;
54
+ md += `**Severity**: ${card.severity} | **Category**: ${card.category}\n`;
55
+ md += `**Narrative**: ${card.narrative}\n`;
56
+ }
57
+ // Add Function-Level Action Bindings for Tier 1
58
+ if (this.bindings && this.bindings.files[path]) {
59
+ const fileBinding = this.bindings.files[path];
60
+ const criticalFunctions = fileBinding.functions.filter((fn) => fn.semanticActions.length > 0 || fn.isEntrypoint);
61
+ if (criticalFunctions.length > 0) {
62
+ md += `\n**Critical Functions**:\n`;
63
+ for (const fn of criticalFunctions) {
64
+ md += `- \`${fn.displayName}\` (lines ${fn.startLine}-${fn.endLine})${fn.isEntrypoint ? ' [Entrypoint]' : ''}\n`;
65
+ for (const action of fn.semanticActions) {
66
+ md += ` - **${action.actionKind}**${action.targetModel ? ` on ${action.targetModel}` : ''}: \`${action.calleeText}\` (line ${action.sourceLine})\n`;
67
+ }
68
+ }
69
+ }
70
+ }
71
+ if (recs.length > 0) {
72
+ md += `\n**Safe Patch Strategies**:\n`;
73
+ for (const r of recs) {
74
+ md += `- **${r.strategy}**: ${r.description}\n`;
75
+ }
76
+ }
77
+ md += `\n---\n\n`;
78
+ }
79
+ md += `## Tier 2: Important Files\n\n`;
80
+ for (const path of tier2) {
81
+ const f = store.files[path];
82
+ const card = uniqueDecisions.get(path);
83
+ md += `- **${path}** (Gravity: ${Math.round(f.gravity)})`;
84
+ if (card) {
85
+ md += ` — ${card.thesis}`;
86
+ }
87
+ md += `\n`;
88
+ }
89
+ md += `\n`;
90
+ md += `## Tier 3: Index\n\n`;
91
+ for (const path of tier3) {
92
+ const f = store.files[path];
93
+ md += `- ${path} (Gravity: ${Math.round(f.gravity)})\n`;
94
+ }
95
+ // In a real robust implementation, we would truncate tiers starting from Tier 3 to fit the budget.
96
+ // Given the simplicity, we'll return the full markdown.
97
+ return [
98
+ {
99
+ type: 'markdown',
100
+ path: 'dossier.agent.md',
101
+ content: md,
102
+ }
103
+ ];
104
+ }
105
+ }
106
+ //# 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,77 @@
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
+ // Robust template resolution for bundled/unbundled environments
21
+ const candidatePaths = [
22
+ join(__dirname, 'ui'), // bundled: dist/index.js -> dist/ui
23
+ join(__dirname, '..', '..', 'ui'), // unbundled: dist/export/renderers -> dist/ui
24
+ join(__dirname, '..', 'ui'), // alt bundle
25
+ join(__dirname, '..', '..', '..', 'ui', 'dist'), // dev: packages/cli/src/export/renderers -> packages/ui/dist
26
+ join(__dirname, '..', '..', 'packages', 'ui', 'dist'), // repo root -> packages/ui/dist
27
+ ];
28
+ let templateDir = '';
29
+ for (const p of candidatePaths) {
30
+ if (existsSync(p) && existsSync(join(p, 'index.html'))) {
31
+ // Double check it's not the source packages/ui (which has vite.config.ts)
32
+ // We want the BUILT UI in the dist folder.
33
+ if (!existsSync(join(p, 'vite.config.ts')) || p.endsWith('dist')) {
34
+ templateDir = p;
35
+ break;
36
+ }
37
+ }
38
+ }
39
+ // Fallback to any 'ui' folder that has index.html as a last resort
40
+ if (!templateDir) {
41
+ for (const p of candidatePaths) {
42
+ if (existsSync(join(p, 'index.html'))) {
43
+ templateDir = p;
44
+ break;
45
+ }
46
+ }
47
+ }
48
+ if (!templateDir) {
49
+ console.error('[vibe-splain] UI template not found. Checked:', candidatePaths);
50
+ return [];
51
+ }
52
+ const artifacts = [];
53
+ const allFiles = getAllFiles(templateDir);
54
+ for (const file of allFiles) {
55
+ const relPath = relative(templateDir, file);
56
+ if (relPath === 'index.html') {
57
+ const templateHtml = readFileSync(file, 'utf8');
58
+ const injection = `<script>window.__VIBE_DOSSIER__ = ${JSON.stringify(viewModel)};</script>`;
59
+ const bakedHtml = templateHtml.replace('<!-- VIBE_DOSSIER_INJECTION_POINT -->', injection);
60
+ artifacts.push({
61
+ type: 'html',
62
+ path: join('ui', relPath),
63
+ content: bakedHtml,
64
+ });
65
+ }
66
+ else {
67
+ artifacts.push({
68
+ type: 'asset',
69
+ path: join('ui', relPath),
70
+ content: readFileSync(file),
71
+ });
72
+ }
73
+ }
74
+ return artifacts;
75
+ }
76
+ }
77
+ //# 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,12 @@
1
+ export class JsonRenderer {
2
+ render(viewModel, _store) {
3
+ return [
4
+ {
5
+ type: 'dossier',
6
+ path: 'dossier.json',
7
+ content: JSON.stringify(viewModel, null, 2),
8
+ }
9
+ ];
10
+ }
11
+ }
12
+ //# sourceMappingURL=JsonRenderer.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 RawAnalysisRenderer implements Renderer {
5
+ render(_viewModel: DossierViewModel, store: AnalysisStore): Artifact[];
6
+ }
@@ -0,0 +1,12 @@
1
+ export class RawAnalysisRenderer {
2
+ render(_viewModel, store) {
3
+ return [
4
+ {
5
+ type: 'analysis',
6
+ path: 'analysis.json',
7
+ content: JSON.stringify(store, null, 2),
8
+ }
9
+ ];
10
+ }
11
+ }
12
+ //# sourceMappingURL=RawAnalysisRenderer.js.map
@@ -0,0 +1,5 @@
1
+ import type { DossierViewModel, AnalysisStore } from '@vibe-splain/brain';
2
+ import type { Artifact } from '../ArtifactBundleWriter.js';
3
+ export interface Renderer {
4
+ render(viewModel: DossierViewModel, store: AnalysisStore): Promise<Artifact[]> | Artifact[];
5
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Renderer.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 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