Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"name": "responsible-vibe-mcp",
"version": "0.13.0",
"version": "0.13.1",
"description": "A Model Context Protocol server that acts as an intelligent conversation state manager and development guide for LLMs",
"main": "dist/index.js",
"bin": {
"responsible-vibe-mcp": "dist/index.js"
"responsible-vibe-mcp": "dist/index.js",
"workflow-visualizer": "workflow-visualizer/bin/visualizer.js"
},
"type": "module",
"engines": {
Expand All @@ -13,6 +14,7 @@
"files": [
"dist/**/*",
"resources/**/*",
"workflow-visualizer/**/*",
"README.md",
"SYSTEM_PROMPT.md",
"LOGGING.md",
Expand All @@ -24,7 +26,8 @@
"url": "git+https://github.com/mrsimpson/vibe-feature-mcp.git"
},
"scripts": {
"build": "tsc",
"build": "tsc && npm run build:visualizer",
"build:visualizer": "cd workflow-visualizer && npm install && npm run build",
"inspector": "npx @modelcontextprotocol/inspector",
"dev": "tsc --watch",
"clean": "rm -rf dist",
Expand Down
52 changes: 52 additions & 0 deletions workflow-visualizer/bin/visualizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env node

import { spawn } from 'child_process';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const visualizerDir = join(__dirname, '..');

console.log('🚀 Starting Workflow Visualizer...');
console.log('📁 Directory:', visualizerDir);

// Check if dependencies are installed
import { existsSync } from 'fs';
if (!existsSync(join(visualizerDir, 'node_modules'))) {
console.log('📦 Installing dependencies...');
const install = spawn('npm', ['install'], {
cwd: visualizerDir,
stdio: 'inherit'
});

install.on('close', (code) => {
if (code === 0) {
startServer();
} else {
console.error('❌ Failed to install dependencies');
process.exit(1);
}
});
} else {
startServer();
}

function startServer() {
console.log('🌐 Starting development server...');
const server = spawn('npm', ['run', 'dev'], {
cwd: visualizerDir,
stdio: 'inherit'
});

server.on('close', (code) => {
console.log(`Server exited with code ${code}`);
});

// Handle Ctrl+C
process.on('SIGINT', () => {
console.log('\n👋 Shutting down visualizer...');
server.kill('SIGINT');
process.exit(0);
});
}
3 changes: 3 additions & 0 deletions workflow-visualizer/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions workflow-visualizer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"version": "1.0.0",
"description": "Web app for visualizing responsible-vibe workflow state machines",
"type": "module",
"bin": {
"workflow-visualizer": "bin/visualizer.js"
},
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
Expand Down
136 changes: 3 additions & 133 deletions workflow-visualizer/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { FileUploadHandler } from './services/FileUploadHandler';
import { ErrorHandler } from './utils/ErrorHandler';
import { PlantUMLRenderer } from './visualization/PlantUMLRenderer';
import { getRequiredElement } from './utils/DomHelpers';
import type { InteractionEvent } from './types/ui-types';
import { YamlStateMachine, AppState, TransitionData } from './types/ui-types';

class WorkflowVisualizerApp {
Expand Down Expand Up @@ -53,7 +54,7 @@ class WorkflowVisualizerApp {
});
} else if (elementType === 'transition') {
this.handleElementClick({
elementType: 'link',
elementType: 'transition',
elementId: elementId,
data: data
});
Expand Down Expand Up @@ -203,23 +204,12 @@ class WorkflowVisualizerApp {
if (event.elementType === 'node' && event.data) {
console.log('Selecting state:', event.elementId);
this.selectState(event.elementId!, event.data);
} else if (event.elementType === 'link' && event.data) {
} else if (event.elementType === 'transition' && event.data) {
console.log('Selecting transition:', event.elementId);
this.selectTransition(event.elementId!, event.data);
}
}

/**
* Handle element hover in the diagram
*/
private handleElementHover(event: InteractionEvent): void {
// For now, just log hover events
// Could be extended to show tooltips
if (event.type === 'hover') {
console.log('Element hovered:', event.elementType, event.elementId);
}
}

/**
* Select a state node
*/
Expand Down Expand Up @@ -272,16 +262,6 @@ class WorkflowVisualizerApp {
}
}

/**
* Highlight a transition path
*/
private highlightTransitionPath(fromState: string, toState: string): void {
// Note: PlantUML diagrams don't support interactive path highlighting
// Path information is shown in the side panel instead
const pathElements = [fromState, toState];
this.appState.highlightedPath = pathElements;
}

/**
* Update the side panel content
*/
Expand Down Expand Up @@ -519,116 +499,6 @@ class WorkflowVisualizerApp {
}

/**
* Render state details
*/
private renderStateDetails(stateId: string, stateData: any, container: HTMLElement): void {
const workflow = this.appState.currentWorkflow!;
const isInitial = stateId === workflow.initial_state;

container.innerHTML = `
<div class="detail-section">
<h3 class="detail-title">
${stateId}
${isInitial ? '<span class="badge badge-success">Initial</span>' : ''}
</h3>
<p class="detail-content">${stateData.description}</p>
</div>

<div class="detail-section">
<h4 class="detail-subtitle">Default Instructions</h4>
<div class="code-block">${stateData.default_instructions}</div>
</div>

<div class="detail-section">
<h4 class="detail-subtitle">Transitions (${stateData.transitions.length})</h4>
<ul class="transitions-list">
${stateData.transitions.map((transition: any) => `
<li class="transition-item clickable-transition" data-from="${stateId}" data-to="${transition.to}" data-trigger="${transition.trigger}">
<div class="transition-trigger">${transition.trigger}</div>
<div class="transition-target">→ ${transition.to}</div>
<div class="transition-reason">${transition.transition_reason}</div>
</li>
`).join('')}
</ul>
</div>
`;

// Add click handlers to transitions
const transitionItems = container.querySelectorAll('.clickable-transition');
transitionItems.forEach(item => {
item.addEventListener('click', (e) => {
e.stopPropagation();
const fromState = item.getAttribute('data-from');
const toState = item.getAttribute('data-to');
const trigger = item.getAttribute('data-trigger');

if (fromState && toState && trigger) {
console.log('Side panel transition clicked:', `${fromState}->${toState}`);

// Find the full transition data
const fullTransition = stateData.transitions.find((t: any) =>
t.to === toState && t.trigger === trigger
);

if (fullTransition) {
this.selectTransition(`${fromState}->${toState}`, {
from: fromState,
to: toState,
trigger: trigger,
instructions: fullTransition.instructions,
additional_instructions: fullTransition.additional_instructions,
transition_reason: fullTransition.transition_reason
});
}
}
});

// Add hover effects
item.addEventListener('mouseenter', () => {
(item as HTMLElement).style.backgroundColor = '#f0f9ff';
(item as HTMLElement).style.cursor = 'pointer';
});

item.addEventListener('mouseleave', () => {
(item as HTMLElement).style.backgroundColor = '';
(item as HTMLElement).style.cursor = '';
});
});
}

/**
* Render transition details
*/
private renderTransitionDetails(transitionData: TransitionData, container: HTMLElement): void {
container.innerHTML = `
<div class="detail-section">
<h3 class="detail-title">Transition: ${transitionData.trigger}</h3>
<p class="detail-content">
<strong>${transitionData.from}</strong> → <strong>${transitionData.to}</strong>
</p>
</div>

<div class="detail-section">
<h4 class="detail-subtitle">Reason</h4>
<p class="detail-content">${transitionData.transition_reason}</p>
</div>

${transitionData.instructions ? `
<div class="detail-section">
<h4 class="detail-subtitle">Instructions</h4>
<div class="code-block">${transitionData.instructions}</div>
</div>
` : ''}

${transitionData.additional_instructions ? `
<div class="detail-section">
<h4 class="detail-subtitle">Additional Instructions</h4>
<div class="code-block">${transitionData.additional_instructions}</div>
</div>
` : ''}
`;
}

/**
* Clear the visualization
*/
Expand Down
4 changes: 4 additions & 0 deletions workflow-visualizer/src/types/plantuml-encoder.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module 'plantuml-encoder' {
export function encode(plantuml: string): string;
export function decode(encoded: string): string;
}
10 changes: 10 additions & 0 deletions workflow-visualizer/src/types/ui-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ import type { YamlStateMachine, YamlState, YamlTransition } from '../../../src/s
// Re-export for convenience
export type { YamlStateMachine, YamlState, YamlTransition };

/**
* Interaction event for diagram elements
*/
export interface InteractionEvent {
elementType: 'node' | 'edge' | 'transition';
elementId?: string;
data?: any;
originalEvent?: Event;
}

/**
* Application state interface
*/
Expand Down
Loading