LEVERAGEAI Decision Making Processes
Architecting Claude Part 1
Evaluations, Standards and Styling: Proper Analysis weighing Constraint Driven Design; Robust and Resilient Performance; On a Scale of Elegant Simplicity:
Full Context can be found @ :
https://claude.ai/chat/69459500-ac65-4343-8be9-75a5c0cb81ed?artifactId=main_application
Constraint Analysis
Primary Constraint: Existing categorization framework limits integration complexity. The 8 libraries map cleanly to current categories:
Automation (5/8): schedule, watchdog, pyautogui, smtplib+email, pyttsx3
Data Processing (2/8): openpyxl, pytesseract+Pillow
Web Scraping (1/8): requests+BeautifulSoup
Secondary Constraint: Dependency management becomes the architectural bottleneck - these libraries require external system dependencies (tesseract, audio drivers).
Integration Architecture
Template-Driven Approach
Create script scaffolds that embed library patterns directly into PyStudio's existing framework:
/templates/automation/
├── scheduler_template.py
├── file_watcher_template.py
├── gui_automation_template.py
├── email_automation_template.py
└── tts_template.py
Execution Layer Enhancement
Extend current script execution to handle:
Pre-execution dependency checks
Runtime environment setup
Error context for missing system dependencies
Category Enhancement
Leverage existing tag system to add sub-categories:
automation→scheduler,file-monitor,gui-control,email,speechdata-processing→excel,ocrweb-scraping→content-extraction
Leverage Points
Template Integration: Instead of building new UI, enhance the "New Script" workflow with library-specific templates
Dependency Pipeline: Create installation scripts that run before first execution
Error Context: Transform library import failures into actionable setup instructions
Critical Decision
The existing script structure suggests PyStudio already handles execution contexts well. The question: Does it run in a sandboxed Python environment or system Python?
This determines whether we:
A) Build dependency management into the app
B) Rely on user system configuration
Next: Provide the codebase. I'll identify the execution constraint and design the integration to amplify existing architectural strengths rather than add complexity.
# # Thought Process:
The user is clarifying that we're rebuilding PyStudio from scratch, so I don't need to be constrained by the existing categories shown in the screenshot. This opens up much more architectural flexibility.
Looking at the 8 Python libraries from the document:
schedule - task scheduling/automation
watchdog - file system monitoring
pyautogui - GUI automation
smtplib + email - email automation
pytesseract + Pillow - OCR/image processing
openpyxl - Excel manipulation
requests + BeautifulSoup - web scraping
pyttsx3 - text-to-speech
Since we're rebuilding, I should think about this from a systems architecture perspective with leverage points in mind. What's the core constraint that shapes everything else?
The constraint is likely going to be execution environment management - these libraries have varying dependency requirements. Some are pure Python (schedule, requests), others require system binaries (tesseract), and others interact with OS-level services (pyautogui, pyttsx3).
From an elegant simplicity perspective, I should identify the minimal set of categories that capture the essential patterns, then design the architecture to handle the complexity behind simple interfaces.
Looking at the patterns:
Time-based execution (schedule)
Event-driven execution (watchdog)
System interaction (pyautogui, pyttsx3)
Data transformation (openpyxl, pytesseract+Pillow)
Network operations (requests+BeautifulSoup, smtplib+email)
But that's still thinking in terms of technical implementation. From a user perspective, what are they actually trying to accomplish?
Automation: Replace manual repetitive tasks
Integration: Connect different systems/services
Processing: Transform data between formats
Monitoring: Watch for changes and respond
Actually, let me step back. The leverage point here is probably the execution model itself.
I see a breakthrough in organizing scripts by their fundamental execution paradigms. The new categorization focuses on when and how scripts run, which directly aligns with user intentions and system architecture. This approach transcends traditional library-based classifications, creating a more intuitive and flexible framework for automation tools.
The four core execution modes - Scheduled, Reactive, Interactive, and Continuous - provide a clean, user-centric way to conceptualize script behavior. Each mode represents a distinct approach to task management and system interaction.
Architecture Foundation
Core Constraint Identified: Execution model determines everything else.
Proposed Category Structure
Instead of grouping by library function, group by execution pattern:
1. Scheduled
schedule- time-based triggerssmtplib+email- recurring reports/notificationsPattern: Runs on time, exits cleanly
2. Reactive
watchdog- file system eventsrequests+BeautifulSoup- webhook/API responsesPattern: Waits for external events, processes, continues waiting
3. Interactive
pyautogui- GUI automation sequencespytesseract+Pillow- on-demand OCR processingopenpyxl- batch data transformationPattern: Runs once per invocation, returns results
4. Persistent
pyttsx3- continuous voice interfaceCombined automation workflows
Pattern: Runs continuously, maintains state
Architectural Implications
This execution-based categorization solves the dependency management constraint elegantly:
Scheduled/Interactive: Can validate dependencies at script creation
Reactive/Persistent: Must validate dependencies at startup and gracefully handle failures
Template Strategy
Each category gets execution-specific scaffolding:
python
# Scheduled template
class ScheduledTask:
def setup(self): pass
def execute(self): pass # User implements this
def cleanup(self): pass
# Reactive template
class ReactiveTask:
def setup(self): pass
def handle_event(self, event): pass # User implements this
def should_continue(self): return True
Critical Questions for Codebase Review
Runtime Environment: Embedded Python interpreter or system Python?
Process Model: Single process or script isolation?
State Management: How do persistent scripts maintain state between app restarts?
The answers determine whether we build a script runner or a Python runtime manager.
Ready for codebase analysis.
User:
Embedded Python interpreter or system Python? # What are the advantages and disadvantages of each?
Single process or script isolation? Please explain the advantages and disadvantages of these as ell.
State Management: How do persistent scripts maintain state between app restarts? I hadn't thought of this - lets brainstorm and engineer and elegant strategy and solution.
LLM - Claude
Thoughts:
These are fundamental architectural questions that will shape the entire system. Let me think through each systematically.
Embedded Python interpreter vs System Python
Embedded Python interpreter: Advantages:
Complete control over the Python environment
No dependency conflicts with user's system Python
Can bundle specific versions of libraries we need
Sandboxed execution - security benefits
Consistent behavior across different user environments
Can implement custom import hooks, security restrictions
Easier to manage library versions
Disadvantages:
Larger app size (need to bundle Python interpreter)
More complex build process
Need to handle platform-specific binaries
Updates require app updates
May not have access to user's existing Python packages
More memory usage (separate interpreter)
System Python: Advantages:
Smaller app footprint
User can leverage their existing Python environment and packages
Easier to integrate with user's development workflow
Can use whatever Python version user prefers
Access to user's installed packages
Disadvantages:
Dependency hell - conflicts with user's environment
Inconsistent behavior across different systems
Security concerns - full system access
Harder to ensure consistent library versions
User might not have Python installed
Different Python versions might break scripts
Single process vs Script isolation
Single process: Advantages:
Lower memory overhead
Faster inter-script communication
Shared state between scripts possible
Easier to manage overall
Better for GUI responsiveness
Disadvantages:
One script crash can bring down everything
Memory leaks accumulate
Harder to kill runaway scripts
Global state pollution
Threading complexity for concurrent scripts
Script isolation (separate processes): Advantages:
Fault isolation - one crash doesn't affect others
True parallelism
Easy to kill individual scripts
Memory isolation
Better security isolation
Can run different Python versions per script
Disadvantages:
Higher memory overhead
Inter-process communication complexity
Harder to share state
More complex process management
Platform differences in process handling
State Management for Persistent Scripts
I'm exploring robust persistence strategies that balance simplicity and flexibility. A hybrid approach seems most promising, combining lightweight storage with adaptable state management. The goal is creating a system that can maintain script state across app restarts while keeping implementation straightforward and efficient.
Key considerations include minimizing overhead, supporting flexible storage mechanisms, and providing seamless state preservation. The solution needs to accommodate different script requirements while maintaining a clean, intuitive interface for developers.
LLM Claude Response:
Runtime Environment Analysis
Embedded Python Interpreter
Advantages:
Dependency determinism: Zero conflicts with user's system
Security sandbox: Controlled execution environment
Version consistency: Same behavior across all users
Library bundling: Ship exact versions we've tested
Disadvantages:
App size: +50-100MB for interpreter + core libraries
Update coupling: Python updates require app updates
Build complexity: Platform-specific binaries
System Python
Advantages:
Lean footprint: App stays small
User ecosystem: Access to their existing packages
Development workflow: Natural integration
Disadvantages:
Dependency chaos: Version conflicts kill reliability
Environment variability: "Works on my machine" syndrome
Security exposure: Full system access
Recommendation: Embedded interpreter. The reliability constraint trumps size concerns.
Process Architecture Analysis
Single Process
Advantages:
State sharing: Scripts can communicate directly
Resource efficiency: Lower memory overhead
Simpler IPC: Direct function calls
Disadvantages:
Cascade failure: One bad script kills everything
Resource contention: Blocking operations freeze UI
Memory pollution: Leaks accumulate
Script Isolation
Advantages:
Fault boundaries: Script crashes stay contained
Resource isolation: CPU/memory limits per script
True parallelism: No GIL constraints
Disadvantages:
Memory overhead: ~10-20MB per Python process
IPC complexity: Process communication mechanisms
State sharing: More complex inter-script coordination
Recommendation: Hybrid model. UI process + isolated script processes with managed IPC.
State Management Strategy
The Constraint
Persistent scripts need state that survives:
App restarts
Script modifications
System reboots
Crashes
Elegant Solution: Event-Sourced State Pods
class StatePod:
def __init__(self, script_id: str):
self.db = sqlite3.connect(f"state/{script_id}.db")
self.state = {}
self.replay_events()
def set(self, key: str, value: any):
self.state[key] = value
self._log_event("SET", key, value)
def get(self, key: str, default=None):
return self.state.get(key, default)
def _log_event(self, action, key, value):
self.db.execute(
"INSERT INTO events (timestamp, action, key, value) VALUES (?, ?, ?, ?)",
(time.time(), action, key, pickle.dumps(value))
)
Benefits:
Automatic persistence: Every state change logged
Crash recovery: Replay events to restore state
Script versioning: State survives code changes
Debugging: Full state history visible
Cleanup: Old events can be compacted
State Sharing Between Scripts:
python
# Shared message bus
SharedState.publish("weather_data", temperature_reading)
SharedState.subscribe("weather_data", callback=update_dashboard)
Memory Management:
Lazy loading: Only active scripts load state
State expiration: TTL on unused state entries
Compression: Pickle + gzip for large objects
This creates persistent script identities that maintain context across restarts while keeping the implementation simple and debuggable.
User:
*Here is the original code that needs Fixed:
Poorly Written Code Example:
import React, { useState, useEffect } from "react";
import { Document, SearchQuery, ScrapedContent, Report } from "@/entities/all";
import {
FileText,
Search,
Globe,
TrendingUp,
Zap,
Database,
Clock,
CheckCircle
} from "lucide-react";
import BrutalistCard from "../components/BrutalistCard";
import ProcessingQueue from "../components/ProcessingQueue";
export default function Dashboard() {
const [stats, setStats] = useState({
totalDocuments: 0,
totalSearches: 0,
totalScraped: 0,
totalReports: 0
});
const [recentActivity, setRecentActivity] = useState([]);
const [processingQueue, setProcessingQueue] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
loadDashboardData();
}, []);
const loadDashboardData = async () => {
try {
const [documents, searches, scraped, reports] = await Promise.all([
Document.list(),
SearchQuery.list(),
ScrapedContent.list(),
Report.list()
]);
setStats({
totalDocuments: documents.length,
totalSearches: searches.length,
totalScraped: scraped.length,
totalReports: reports.length
});
// Mock recent activity
const mockActivity = [
{ name: "python-tutorial.pdf", type: "Document Upload", status: "completed", timestamp: "2 min ago" },
{ name: "django-docs scraping", type: "Web Scraping", status: "processing", timestamp: "5 min ago", progress: 65 },
{ name: "Weekly Analytics Report", type: "Report Generation", status: "completed", timestamp: "1 hour ago" },
{ name: "machine-learning-guide.md", type: "Document Upload", status: "pending", timestamp: "3 hours ago" }
];
setRecentActivity(mockActivity);
setProcessingQueue(mockActivity.filter(item => item.status === "processing" || item.status === "pending"));
} catch (error) {
console.error("Error loading dashboard data:", error);
} finally {
setIsLoading(false);
}
};
const StatCard = ({ title, value, icon: Icon, color }) => (
<BrutalistCard color={color} hover>
<div className="flex items-center justify-between">
<div>
<p className="font-black text-2xl text-black mb-1">{value}</p>
<p className="font-bold text-sm text-black uppercase tracking-wider">
{title}
</p>
</div>
<div className="bg-black p-3 border-2 border-black">
<Icon className="w-6 h-6 text-white" />
</div>
</div>
</BrutalistCard>
);
if (isLoading) {
return (
<div className="space-y-8">
<div className="text-center">
<h1 className="text-4xl font-black text-black uppercase tracking-tight mb-4">
LOADING DASHBOARD...
</h1>
<div className="bg-blue-500 border-4 border-black brutalist-shadow p-8 inline-block">
<Zap className="w-12 h-12 text-white animate-pulse" />
</div>
</div>
</div>
);
}
return (
<div className="space-y-8">
{/* Header */}
<div className="text-center mb-12">
<h1 className="text-5xl font-black text-black uppercase tracking-tight mb-4">
SYSTEM DASHBOARD
</h1>
<div className="bg-yellow-400 border-4 border-black brutalist-shadow p-4 inline-block">
<p className="font-black text-xl text-black uppercase tracking-wider">
MONITORING ALL SYSTEMS
</p>
</div>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<StatCard
title="Documents"
value={stats.totalDocuments}
icon={FileText}
color="blue"
/>
<StatCard
title="Searches"
value={stats.totalSearches}
icon={Search}
color="pink"
/>
<StatCard
title="Scraped Pages"
value={stats.totalScraped}
icon={Globe}
color="green"
/>
<StatCard
title="Reports"
value={stats.totalReports}
icon={TrendingUp}
color="orange"
/>
</div>
{/* Main Content Grid */}
<div className="grid lg:grid-cols-2 gap-8">
{/* Processing Queue */}
<ProcessingQueue items={processingQueue} />
{/* Recent Activity */}
<BrutalistCard>
<div className="flex items-center justify-between mb-6">
<h3 className="text-xl font-black uppercase tracking-wider">
RECENT ACTIVITY
</h3>
<div className="bg-black text-white px-3 py-1 border-2 border-black">
<span className="font-black text-sm">LIVE FEED</span>
</div>
</div>
<div className="space-y-4 max-h-96 overflow-y-auto">
{recentActivity.map((activity, index) => (
<div
key={index}
className="bg-gray-100 border-4 border-black p-4 flex items-center justify-between"
>
<div className="flex items-center space-x-4">
{activity.status === 'completed' ? (
<CheckCircle className="w-6 h-6 text-green-500" />
) : activity.status === 'processing' ? (
<Zap className="w-6 h-6 text-blue-500 animate-pulse" />
) : (
<Clock className="w-6 h-6 text-gray-500" />
)}
<div>
<p className="font-black text-sm uppercase">
{activity.name}
</p>
<p className="font-bold text-xs text-gray-600 mt-1">
{activity.type} • {activity.timestamp}
</p>
</div>
</div>
<div className={`
px-3 py-1 border-2 border-black font-black text-xs uppercase
${activity.status === 'completed' ? 'bg-green-500 text-white' : ''}
${activity.status === 'processing' ? 'bg-blue-500 text-white' : ''}
${activity.status === 'pending' ? 'bg-gray-500 text-white' : ''}
`}>
{activity.status}
</div>
</div>
))}
</div>
</BrutalistCard>
</div>
{/* System Status */}
<div className="grid md:grid-cols-3 gap-6">
<BrutalistCard color="green">
<div className="text-center">
<Database className="w-8 h-8 text-black mx-auto mb-3" />
<p className="font-black text-lg text-black uppercase">
DATABASE
</p>
<p className="font-bold text-sm text-black mt-1">
OPERATIONAL
</p>
</div>
</BrutalistCard>
<BrutalistCard color="blue">
<div className="text-center">
<Zap className="w-8 h-8 text-black mx-auto mb-3" />
<p className="font-black text-lg text-black uppercase">
AI PROCESSING
</p>
<p className="font-bold text-sm text-black mt-1">
ACTIVE
</p>
</div>
</BrutalistCard>
<BrutalistCard color="pink">
<div className="text-center">
<TrendingUp className="w-8 h-8 text-black mx-auto mb-3" />
<p className="font-black text-lg text-black uppercase">
SEARCH ENGINE
</p>
<p className="font-bold text-sm text-black mt-1">
READY
</p>
</div>
</BrutalistCard>
</div>
</div>
);
}
Analysis
The user is asking me to critically analyze the React Dashboard component from a LEVERAGEAI perspective, focusing on:
Constraint-based approach
Architecture and Spec-driven design
Clear type definitions
Testing mapped out before code is written
No sloppy logic or unnecessary dependencies
Let me examine the code systematically:
javascriptimport React, { useState, useEffect } from "react";
import { Document, SearchQuery, ScrapedContent, Report } from "@/entities/all";
import {
FileText,
Search,
Globe,
TrendingUp,
Zap,
Database,
Clock,
CheckCircle
} from "lucide-react";
import BrutalistCard from "../components/BrutalistCard";
import ProcessingQueue from "../components/ProcessingQueue";
Issues I can identify:
No TypeScript - This is JavaScript, not TypeScript, so there are no explicit type definitions
Importing from "@/entities/all" - This is a barrel export which can cause circular dependencies and makes it unclear what's actually being used
No interface definitions - The component doesn't define interfaces for props, state, or data structures
Mixed concerns - The component is doing data fetching, state management, and rendering all in one place
Looking at the component structure:
javascriptexport default function Dashboard() {
const [stats, setStats] = useState({
totalDocuments: 0,
totalSearches: 0,
totalScraped: 0,
totalReports: 0
});
const [recentActivity, setRecentActivity] = useState([]);
const [processingQueue, setProcessingQueue] = useState([]);
const [isLoading, setIsLoading] = useState(true);
Issues:
No type safety - useState calls have no type parameters
Implicit any types - Arrays and objects have no defined structure
No prop interface - Component takes no props but should probably be configurable
The state management reveals significant type safety and structural vulnerabilities.
Without proper TypeScript interfaces,
the component lacks robust type checking, leaving it prone to runtime errors and
reducing code predictability. The useState hooks are essentially untyped, which means
any data structure could be accidentally passed, breaking expected behavior.
The data loading approach mixes real API calls with mock data, creating an inconsistent
and potentially unreliable data retrieval mechanism. Hardcoded mock entries suggest
a prototype-level implementation that needs more sophisticated data generation or
external configuration.
The StatCard component demonstrates a clean, declarative design with clear prop
expectations, using destructuring to extract specific visual configuration
parameters like title, value, icon, and color.
However, the implementation reveals several architectural weaknesses.
The component lacks robust prop validation, relies on inline definitions that
reduce modularity, and uses hardcoded CSS classes that limit design system flexibility.
The rendering strategy doesn't account for dynamic layout requirements or potential
performance optimizations through memoization.
The overall rendering logic exposes critical architectural concerns around
error handling, loading states, and component responsibilities.
The current implementation tightly couples data fetching, state management,
and UI rendering, violating core separation of concerns principles.
Accessibility and responsive design considerations appear minimal, with no
clear strategy for handling different screen sizes or providing semantic
HTML structure for screen readers.
From an engineering perspective, the component demonstrates significant technical debt.
Its monolithic design creates challenges for testing, scaling, and maintaining the codebase.
The lack of dependency injection and error boundaries introduces
substantial risk for system-wide failures.
Performance constraints further compound these issues, with potential memory
leaks and unmanaged API interactions that could degrade application responsiveness
and user experience.
Further Analysis:
Critical Analysis: Dashboard Component Failures
Type Safety Violation
javascript
const [recentActivity, setRecentActivity] = useState([]);
Problem: No TypeScript, no interfaces, no runtime validation. This is any[] masquerading as type safety.
Correct Approach:
typescript
interface ActivityItem {
name: string;
type: ActivityType;
status: ProcessingStatus;
timestamp: string;
progress?: number;
}
const [recentActivity, setRecentActivity] = useState<ActivityItem[]>([]);
Architecture Violation: Mixed Concerns
javascript
export default function Dashboard() {
// Data fetching
// State management
// Business logic
// Rendering
// Error handling
}
Problem: Single component doing 5 different jobs. Untestable, unmaintainable.
Constraint-Based Solution:
Data Layer:
useDashboardData()hookPresentation Layer: Pure components
State Layer: Context/reducer pattern
Error Boundary: Separate component
Dependency Chaos
javascript
import { Document, SearchQuery, ScrapedContent, Report } from "@/entities/all";
Problem: Barrel imports create circular dependencies and hide actual usage.
Correct: Import only what you use from specific modules.
Mock Data Anti-Pattern
javascript
// Mock recent activity
const mockActivity = [
{ name: "python-tutorial.pdf", type: "Document Upload" }
];
Problem: Production code contains hardcoded test data. This is a deployment time bomb.
Solution: Dependency injection with data adapters.
Error Handling Failure
javascript
} catch (error) {
console.error("Error loading dashboard data:", error);
} finally {
setIsLoading(false);
}
Problem: Silent failure. User sees stale data with no indication of failure.
Constraint: Dashboard must surface errors and provide recovery paths.
No Testing Strategy
The component structure makes it impossible to:
Mock data sources
Test error states
Test loading states
Test user interactions
Achieve proper coverage
Performance Neglect
javascript
const StatCard = ({ title, value, icon: Icon, color }) => (
Problem: Inline component definition causes unnecessary re-renders.
The Root Constraint Violation
This component couples data fetching to presentation. Every change to data structure requires UI changes. Every UI change risks breaking data logic.
Correct Architecture:
typescript
// Data layer
const dashboardData = useDashboardData();
// Presentation layer
return <DashboardView data={dashboardData} />;
Specification Absence
No clear definition of:
What constitutes dashboard "success"
Error recovery behaviors
Performance requirements
Data freshness requirements
Before writing code, we need:
Data contracts (what APIs return)
Error scenarios (network fail, auth fail, etc.)
Performance constraints (load time, update frequency)
User flows (what happens on each interaction)
This is prototype code shipped to production. The architectural debt will compound exponentially.
Example of poorly written code --
The following is a digest of the repository "PyKnowledgeApp".
This digest is designed to be easily parsed by Large Language Models.
--- SUMMARY ---
Repository: PyKnowledgeApp
Files Analyzed: 79
Total Text Size: 923.61 KB
Estimated Tokens (text only): ~237,904
--- DIRECTORY STRUCTURE ---
PyKnowledgeApp/
├── src/
│ ├── api/
│ │ ├── base44Client.js
│ │ ├── entities.js
│ │ └── integrations.js
│ ├── components/
│ │ ├── ui/
│ │ │ ├── accordion.jsx
│ │ │ ├── alert-dialog.jsx
│ │ │ ├── alert.jsx
│ │ │ ├── aspect-ratio.jsx
│ │ │ ├── avatar.jsx
│ │ │ ├── badge.jsx
│ │ │ ├── breadcrumb.jsx
│ │ │ ├── button.jsx
│ │ │ ├── calendar.jsx
│ │ │ ├── card.jsx
│ │ │ ├── carousel.jsx
│ │ │ ├── chart.jsx
│ │ │ ├── checkbox.jsx
│ │ │ ├── collapsible.jsx
│ │ │ ├── command.jsx
│ │ │ ├── context-menu.jsx
│ │ │ ├── dialog.jsx
│ │ │ ├── drawer.jsx
│ │ │ ├── dropdown-menu.jsx
│ │ │ ├── form.jsx
│ │ │ ├── hover-card.jsx
│ │ │ ├── input-otp.jsx
│ │ │ ├── input.jsx
│ │ │ ├── label.jsx
│ │ │ ├── menubar.jsx
│ │ │ ├── navigation-menu.jsx
│ │ │ ├── pagination.jsx
│ │ │ ├── popover.jsx
│ │ │ ├── progress.jsx
│ │ │ ├── radio-group.jsx
│ │ │ ├── resizable.jsx
│ │ │ ├── scroll-area.jsx
│ │ │ ├── select.jsx
│ │ │ ├── separator.jsx
│ │ │ ├── sheet.jsx
│ │ │ ├── sidebar.jsx
│ │ │ ├── skeleton.jsx
│ │ │ ├── slider.jsx
│ │ │ ├── sonner.jsx
│ │ │ ├── switch.jsx
│ │ │ ├── table.jsx
│ │ │ ├── tabs.jsx
│ │ │ ├── textarea.jsx
│ │ │ ├── toast.jsx
│ │ │ ├── toaster.jsx
│ │ │ ├── toggle-group.jsx
│ │ │ ├── toggle.jsx
│ │ │ ├── tooltip.jsx
│ │ │ └── use-toast.jsx
│ │ ├── BrutalistButton.jsx
│ │ ├── BrutalistCard.jsx
│ │ └── ProcessingQueue.jsx
│ ├── hooks/
│ │ └── use-mobile.jsx
│ ├── pages/
│ │ ├── Dashboard.jsx
│ │ ├── index.jsx
│ │ ├── KnowledgeBase.jsx
│ │ ├── Layout.jsx
│ │ ├── Reports.jsx
│ │ ├── Search.jsx
│ │ ├── Settings.jsx
│ │ ├── Upload.jsx
│ │ └── WebScraper.jsx
│ ├── utils/
│ │ └── index.ts [binary]
│ ├── App.css
│ ├── App.jsx
│ ├── index.css
│ └── main.jsx
├── components.json
├── eslint.config.js
├── jsconfig.json
├── package.json
├── postcss.config.js
├── PyStudio_export.json
├── README.md
├── tailwind.config.js
└── vite.config.js
--- FILE CONTENTS ---
============================================================
FILE: src/api/base44Client.js
============================================================
import { createClient } from '@base44/sdk';
// import { getAccessToken } from '@base44/sdk/utils/auth-utils';
// Create a client with authentication required
export const base44 = createClient({
appId: "68b2d9dd295ddae8d2a8eac8",
requiresAuth: true // Ensure authentication is required for all operations
});
============================================================
FILE: src/api/entities.js
============================================================
import { base44 } from './base44Client';
export const Document = base44.entities.Document;
export const SearchQuery = base44.entities.SearchQuery;
export const ScrapedContent = base44.entities.ScrapedContent;
export const Report = base44.entities.Report;
// auth sdk:
export const User = base44.auth;
============================================================
FILE: src/api/integrations.js
============================================================
import { base44 } from './base44Client';
export const Core = base44.integrations.Core;
export const InvokeLLM = base44.integrations.Core.InvokeLLM;
export const SendEmail = base44.integrations.Core.SendEmail;
export const UploadFile = base44.integrations.Core.UploadFile;
export const GenerateImage = base44.integrations.Core.GenerateImage;
export const ExtractDataFromUploadedFile = base44.integrations.Core.ExtractDataFromUploadedFile;
============================================================
FILE: src/components/ui/accordion.jsx
============================================================
import * as React from "react"
import * as AccordionPrimitive from "@radix-ui/react-accordion"
import { ChevronDown } from "lucide-react"
import { cn } from "@/lib/utils"
const Accordion = AccordionPrimitive.Root
const AccordionItem = React.forwardRef(({ className, ...props }, ref) => (
<AccordionPrimitive.Item ref={ref} className={cn("border-b", className)} {...props} />
))
AccordionItem.displayName = "AccordionItem"
const AccordionTrigger = React.forwardRef(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline text-left [&[data-state=open]>svg]:rotate-180",
className
)}
{...props}>
{children}
<ChevronDown
className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
))
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
const AccordionContent = React.forwardRef(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}>
<div className={cn("pb-4 pt-0", className)}>{children}</div>
</AccordionPrimitive.Content>
))
AccordionContent.displayName = AccordionPrimitive.Content.displayName
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
============================================================
FILE: src/components/ui/alert-dialog.jsx
============================================================
import * as React from "react"
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
const AlertDialog = AlertDialogPrimitive.Root
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
const AlertDialogPortal = AlertDialogPrimitive.Portal
const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
ref={ref} />
))
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
)}
{...props} />
</AlertDialogPortal>
))
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
const AlertDialogHeader = ({
className,
...props
}) => (
<div
className={cn("flex flex-col space-y-2 text-center sm:text-left", className)}
{...props} />
)
AlertDialogHeader.displayName = "AlertDialogHeader"
const AlertDialogFooter = ({
className,
...props
}) => (
<div
className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
{...props} />
)
AlertDialogFooter.displayName = "AlertDialogFooter"
const AlertDialogTitle = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title ref={ref} className={cn("text-lg font-semibold", className)} {...props} />
))
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
const AlertDialogDescription = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props} />
))
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName
const AlertDialogAction = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Action ref={ref} className={cn(buttonVariants(), className)} {...props} />
))
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
const AlertDialogCancel = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel
ref={ref}
className={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)}
{...props} />
))
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
}
============================================================
FILE: src/components/ui/alert.jsx
============================================================
import * as React from "react"
import { cva } from "class-variance-authority";
import { cn } from "@/lib/utils"
const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
}
)
const Alert = React.forwardRef(({ className, variant, ...props }, ref) => (
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props} />
))
Alert.displayName = "Alert"
const AlertTitle = React.forwardRef(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props} />
))
AlertTitle.displayName = "AlertTitle"
const AlertDescription = React.forwardRef(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props} />
))
AlertDescription.displayName = "AlertDescription"
export { Alert, AlertTitle, AlertDescription }
Analysis:
Looking at this codebase, I can see several critical issues:
Base44 Dependency: They're using some third-party service called Base44 for client creation and entities. This creates a vendor lock-in and dependency on an external service that might not be reliable.
No TypeScript: Everything is JavaScript with no type safety.
-
Architecture Issues:
Mixed concerns in components
No clear separation between data, business logic, and UI
Barrel imports that create circular dependencies
No testing strategy visible
UI Component Overload: They have 50+ UI components from what looks like shadcn/ui, which is overkill and creates bloat.
No Clear Domain Model: The entities (Document, SearchQuery, ScrapedContent, Report) are just pass-throughs to Base44 entities.
No Python Integration: Despite being called "PyKnowledgeApp", there's no actual Python script execution or management visible.
Now, the user wants to rebuild this as "VibeCodeRehab" with:
TypeScript
Production ready architecture
Python script integration (which aligns with our earlier discussion about the 8 Python automation libraries)
I need to create a comprehensive roadmap that addresses:
The architectural constraints we identified earlier
The execution model (embedded Python + script isolation)
The state management strategy (Event-Sourced State Pods)
The category structure we designed (Scheduled, Reactive, Interactive, Persistent)
TypeScript throughout
Proper testing strategy
Clear separation of concerns
Let me structure this as a constraint-driven roadmap.
Critical Structure Analysis
Fatal Architectural Flaws:
Vendor Lock-in: Base44 dependency creates single point of failure
Type Void: JavaScript everywhere, no contracts, no safety
Component Bloat: 50+ UI components for a script manager
No Python Runtime: "PyKnowledgeApp" with zero Python execution
Mixed Concerns: Data/UI/Logic tangled in every file
The Base44 Problem: External entity management means zero control over data models, performance, or reliability.
VibeCodeRehab: Production Architecture Roadmap
Phase 1: Foundation (Week 1-2)
Core Constraint System
typescript
// Domain contracts first
interface ScriptExecution {
id: ScriptId;
status: ExecutionStatus;
startTime: Timestamp;
process: ProcessHandle;
stateSnapshot: StateSnapshot;
}
interface ExecutionStatus {
type: 'scheduled' | 'reactive' | 'interactive' | 'persistent';
state: 'pending' | 'running' | 'paused' | 'completed' | 'failed';
progress?: number;
lastHeartbeat: Timestamp;
}
Runtime Architecture
VibeCodeRehab/
├── core/
│ ├── python-runtime/ # Embedded Python + dependency management
│ ├── process-manager/ # Script isolation + IPC
│ ├── state-engine/ # Event-sourced state pods
│ └── execution-scheduler/ # Time-based + event-based triggers
├── domain/
│ ├── script-types/ # Scheduled, Reactive, Interactive, Persistent
│ ├── library-templates/ # 8 Python automation libraries
│ └── execution-context/ # Runtime environment contracts
├── infrastructure/
│ ├── sqlite-adapter/ # Local state persistence
│ ├── file-watcher/ # System integration
│ └── process-bridge/ # Python ↔ TypeScript communication
└── interface/
├── script-editor/ # Monaco-based editing
├── execution-monitor/ # Real-time process dashboard
└── template-library/ # Pre-built automation scripts
Phase 2: Python Runtime Integration (Week 3-4)
Embedded Python Strategy
typescript
class PythonRuntime {
private interpreter: EmbeddedPython;
private dependencyManager: DependencyManager;
async executeScript(script: ScriptDefinition): Promise<ExecutionHandle> {
// Validate dependencies
await this.dependencyManager.ensureLibraries(script.dependencies);
// Create isolated process
const process = await this.createIsolatedProcess(script);
// Setup state pod
const statePod = new StatePod(script.id);
return new ExecutionHandle(process, statePod);
}
}
Library Template System
typescript
// Template for schedule library
export const ScheduleTemplate: ScriptTemplate = {
id: 'schedule-automation',
category: 'scheduled',
dependencies: ['schedule', 'time'],
scaffold: `
import schedule
import time
from vibe_bridge import StatePod, notify_ui
state = StatePod("${scriptId}")
def task_function():
# User implements this
pass
schedule.every(${interval}).${unit}.do(task_function)
while True:
schedule.run_pending()
time.sleep(1)
`,
configSchema: z.object({
interval: z.number(),
unit: z.enum(['minutes', 'hours', 'days'])
})
};
Phase 3: State Management Engine (Week 5)
Event-Sourced State Pods
typescript
class StatePod {
constructor(private scriptId: string) {
this.db = new SQLiteAdapter(`state/${scriptId}.db`);
this.replayEvents();
}
set<T>(key: string, value: T): void {
this.state[key] = value;
this.logEvent('SET', key, value);
this.notifySubscribers(key, value);
}
subscribe(key: string, callback: (value: any) => void): void {
// Inter-script communication
}
}
Process Communication Bridge
python
# vibe_bridge.py - Injected into every script
class VibeBridge:
def __init__(self, script_id: str):
self.script_id = script_id
self.ipc = IPCChannel(script_id)
def set_state(self, key: str, value: any):
self.ipc.send('STATE_SET', {'key': key, 'value': value})
def notify_ui(self, message: str, level: str = 'info'):
self.ipc.send('UI_NOTIFY', {'message': message, 'level': level})
Phase 4: Execution Categories (Week 6-7)
Category-Specific Managers
typescript
class ScheduledExecutionManager {
private cronJobs: Map<ScriptId, CronJob> = new Map();
schedule(script: ScheduledScript): void {
const job = new CronJob(script.schedule, () => {
this.pythonRuntime.executeScript(script);
});
this.cronJobs.set(script.id, job);
}
}
class ReactiveExecutionManager {
private watchers: Map<ScriptId, FileWatcher> = new Map();
watch(script: ReactiveScript): void {
const watcher = new FileWatcher(script.watchPath, (event) => {
this.pythonRuntime.executeScript(script, { event });
});
this.watchers.set(script.id, watcher);
}
}
Phase 5: UI Layer (Week 8-9)
Constraint-Driven UI Architecture
typescript
// No component bloat - purpose-built components only
interface CoreComponents {
ScriptEditor: MonacoEditorComponent;
ExecutionMonitor: ProcessDashboardComponent;
TemplateLibrary: ScriptTemplateComponent;
StateInspector: StatePodViewerComponent;
}
// Real-time execution monitoring
const ExecutionDashboard: React.FC = () => {
const executions = useRealtimeExecutions();
const processes = useProcessMonitor();
return (
<ProcessGrid>
{executions.map(exec => (
<ProcessCard
key={exec.id}
execution={exec}
onTerminate={() => processManager.terminate(exec.id)}
onPause={() => processManager.pause(exec.id)}
/>
))}
</ProcessGrid>
);
};
Phase 6: Production Hardening (Week 10-12)
Testing Strategy
typescript
// Contract testing
describe('PythonRuntime', () => {
it('should isolate script failures', async () => {
const badScript = createFailingScript();
const goodScript = createWorkingScript();
await runtime.execute(badScript);
const result = await runtime.execute(goodScript);
expect(result.status).toBe('completed');
});
});
// Integration testing
describe('StatePod', () => {
it('should survive process crashes', async () => {
const pod = new StatePod('test-script');
pod.set('counter', 42);
// Simulate crash
pod.destroy();
const newPod = new StatePod('test-script');
expect(newPod.get('counter')).toBe(42);
});
});
Error Recovery System
typescript
class ExecutionRecoveryManager {
async recoverFromCrash(scriptId: ScriptId): Promise<void> {
const lastState = await this.stateEngine.getLastSnapshot(scriptId);
const script = await this.scriptManager.getScript(scriptId);
// Restart with recovered state
await this.pythonRuntime.executeScript(script, {
initialState: lastState
});
}
}
Core Principles Enforced:
Type Safety: TypeScript contracts for everything
Fault Isolation: Script failures don't cascade
State Persistence: Survives crashes and restarts
Real-time Monitoring: Live execution feedback
Dependency Management: Embedded Python with controlled libraries
Template-Driven: Pre-built automation patterns
Testing from Day 1: Contract and integration tests
Timeline: 12 weeks to production-ready Python automation platform.
Next Step: Define the core domain contracts and start with the PythonRuntime foundation.
The user wants me to continue with the next step, which I mentioned was "Define the core domain contracts and start with the PythonRuntime foundation." I should now provide concrete TypeScript domain contracts and begin architecting the PythonRuntime foundation.
Let me think about what the core domain contracts should include:
Core domain entities
Script execution contracts
State management contracts
Process management contracts
Library template contracts
Then I'll start designing the PythonRuntime foundation with the embedded interpreter approach we agreed on.
I should be systematic and architecture-driven as per the user's preferences, focusing on constraints and leverage points.
// =============================================
// CORE DOMAIN CONTRACTS
// =============================================
// Base types
export type ScriptId = string & { readonly brand: unique symbol };
export type ProcessId = string & { readonly brand: unique symbol };
export type Timestamp = number & { readonly brand: unique symbol };
// Script Categories - Execution Pattern Based
export type ScriptCategory = 'scheduled' | 'reactive' | 'interactive' | 'persistent';
export type ExecutionState =
| 'pending' // Queued for execution
| 'starting' // Process initialization
| 'running' // Active execution
| 'paused' // Temporarily suspended
| 'completed' // Finished successfully
| 'failed' // Execution error
| 'terminated' // User/system killed
| 'recovering'; // Crash recovery in progress
// Python Library Dependencies
export interface LibraryDependency {
readonly name: string;
readonly version: string;
readonly systemDeps?: string[]; // tesseract, audio drivers, etc.
readonly installCommand?: string;
}
// Script Definition Contract
export interface ScriptDefinition {
readonly id: ScriptId;
readonly name: string;
readonly category: ScriptCategory;
readonly code: string;
readonly dependencies: readonly LibraryDependency[];
readonly config: ScriptConfig;
readonly metadata: ScriptMetadata;
}
export interface ScriptConfig {
readonly timeout?: number; // milliseconds
readonly memory?: number; // MB limit
readonly restart?: boolean; // auto-restart on failure
readonly schedule?: ScheduleConfig;
readonly watchPaths?: string[]; // for reactive scripts
}
export interface ScheduleConfig {
readonly type: 'cron' | 'interval';
readonly expression: string; // cron or interval expression
readonly timezone?: string;
}
export interface ScriptMetadata {
readonly author: string;
readonly description: string;
readonly tags: readonly string[];
readonly created: Timestamp;
readonly modified: Timestamp;
readonly version: string;
}
// =============================================
// EXECUTION CONTRACTS
// =============================================
export interface ExecutionContext {
readonly scriptId: ScriptId;
readonly processId: ProcessId;
readonly category: ScriptCategory;
readonly startTime: Timestamp;
readonly environment: ExecutionEnvironment;
readonly statePod: StatePodHandle;
}
export interface ExecutionEnvironment {
readonly pythonVersion: string;
readonly workingDirectory: string;
readonly environmentVars: Record<string, string>;
readonly resourceLimits: ResourceLimits;
}
export interface ResourceLimits {
readonly maxMemory: number; // MB
readonly maxCpu: number; // percentage
readonly maxDuration: number; // milliseconds
}
export interface ExecutionResult {
readonly scriptId: ScriptId;
readonly processId: ProcessId;
readonly status: ExecutionState;
readonly startTime: Timestamp;
readonly endTime?: Timestamp;
readonly exitCode?: number;
readonly stdout: string;
readonly stderr: string;
readonly metrics: ExecutionMetrics;
}
export interface ExecutionMetrics {
readonly memoryPeak: number;
readonly cpuTime: number;
readonly duration: number;
readonly stateChanges: number;
}
// =============================================
// STATE MANAGEMENT CONTRACTS
// =============================================
export interface StatePodHandle {
readonly scriptId: ScriptId;
get<T>(key: string): Promise<T | undefined>;
set<T>(key: string, value: T): Promise<void>;
delete(key: string): Promise<void>;
subscribe<T>(key: string, callback: (value: T) => void): () => void;
getHistory(key: string): Promise<StateEvent[]>;
createSnapshot(): Promise<StateSnapshot>;
restoreFromSnapshot(snapshot: StateSnapshot): Promise<void>;
}
export interface StateEvent {
readonly timestamp: Timestamp;
readonly action: 'SET' | 'DELETE' | 'SNAPSHOT';
readonly key: string;
readonly value?: unknown;
readonly metadata?: Record<string, unknown>;
}
export interface StateSnapshot {
readonly scriptId: ScriptId;
readonly timestamp: Timestamp;
readonly state: Record<string, unknown>;
readonly checksum: string;
}
// =============================================
// PROCESS MANAGEMENT CONTRACTS
// =============================================
export interface ProcessHandle {
readonly processId: ProcessId;
readonly scriptId: ScriptId;
readonly pid: number;
readonly status: ExecutionState;
pause(): Promise<void>;
resume(): Promise<void>;
terminate(): Promise<void>;
getMetrics(): Promise<ProcessMetrics>;
sendSignal(signal: string): Promise<void>;
}
export interface ProcessMetrics {
readonly memoryUsage: number;
readonly cpuUsage: number;
readonly uptime: number;
readonly lastHeartbeat: Timestamp;
}
// =============================================
// TEMPLATE SYSTEM CONTRACTS
// =============================================
export interface ScriptTemplate {
readonly id: string;
readonly name: string;
readonly category: ScriptCategory;
readonly description: string;
readonly dependencies: readonly LibraryDependency[];
readonly scaffold: string; // Python code template
readonly configSchema: unknown; // Zod schema
readonly examples: readonly TemplateExample[];
}
export interface TemplateExample {
readonly name: string;
readonly description: string;
readonly config: unknown;
readonly expectedOutput: string;
}
// =============================================
// EVENT SYSTEM CONTRACTS
// =============================================
export type SystemEvent =
| ScriptExecutionEvent
| ProcessLifecycleEvent
| StateChangeEvent
| SystemHealthEvent;
export interface ScriptExecutionEvent {
readonly type: 'script.execution';
readonly scriptId: ScriptId;
readonly processId: ProcessId;
readonly state: ExecutionState;
readonly timestamp: Timestamp;
readonly metadata?: Record<string, unknown>;
}
export interface ProcessLifecycleEvent {
readonly type: 'process.lifecycle';
readonly processId: ProcessId;
readonly action: 'created' | 'started' | 'paused' | 'resumed' | 'terminated';
readonly timestamp: Timestamp;
}
export interface StateChangeEvent {
readonly type: 'state.change';
readonly scriptId: ScriptId;
readonly key: string;
readonly oldValue?: unknown;
readonly newValue?: unknown;
readonly timestamp: Timestamp;
}
export interface SystemHealthEvent {
readonly type: 'system.health';
readonly component: string;
readonly status: 'healthy' | 'degraded' | 'unhealthy';
readonly message: string;
readonly timestamp: Timestamp;
}
// =============================================
// ERROR HANDLING CONTRACTS
// =============================================
export abstract class VibeCodeError extends Error {
abstract readonly code: string;
abstract readonly category: 'user' | 'system' | 'dependency';
abstract readonly recoverable: boolean;
}
export class ScriptExecutionError extends VibeCodeError {
readonly code = 'SCRIPT_EXECUTION_FAILED';
readonly category = 'user';
readonly recoverable = true;
constructor(
public readonly scriptId: ScriptId,
public readonly exitCode: number,
public readonly stderr: string,
message: string
) {
super(message);
}
}
export class DependencyMissingError extends VibeCodeError {
readonly code = 'DEPENDENCY_MISSING';
readonly category = 'dependency';
readonly recoverable = true;
constructor(
public readonly dependency: LibraryDependency,
message: string
) {
super(message);
}
}
export class ResourceExhaustedError extends VibeCodeError {
readonly code = 'RESOURCE_EXHAUSTED';
readonly category = 'system';
readonly recoverable = false;
constructor(
public readonly resource: 'memory' | 'cpu' | 'disk',
public readonly limit: number,
public readonly actual: number,
message: string
) {
super(message);
}
}
// =============================================
// PYTHON RUNTIME FOUNDATION
// =============================================
import { ChildProcess, spawn } from 'child_process';
import { join, resolve } from 'path';
import { createWriteStream, existsSync, mkdirSync } from 'fs';
import {
ScriptDefinition,
ExecutionContext,
ExecutionResult,
ProcessHandle,
LibraryDependency,
ScriptId,
ProcessId,
ExecutionState,
VibeCodeError
} from './domain-contracts';
// =============================================
// EMBEDDED PYTHON INTERPRETER
// =============================================
export class EmbeddedPythonInterpreter {
private readonly pythonPath: string;
private readonly sitePackages: string;
private readonly initialized: boolean = false;
constructor(private readonly config: PythonConfig) {
this.pythonPath = resolve(config.runtimePath, 'python');
this.sitePackages = resolve(config.runtimePath, 'lib', 'site-packages');
}
async initialize(): Promise<void> {
if (this.initialized) return;
// Verify embedded Python exists
if (!existsSync(this.pythonPath)) {
throw new PythonRuntimeError(
'RUNTIME_MISSING',
`Embedded Python not found at ${this.pythonPath}`
);
}
// Verify basic functionality
await this.verifyInstallation();
// Install vibe-bridge module
await this.installVibeBridge();
this.initialized = true;
}
private async verifyInstallation(): Promise<void> {
return new Promise((resolve, reject) => {
const proc = spawn(this.pythonPath, ['-c', 'import sys; print(sys.version)'], {
stdio: 'pipe'
});
proc.on('exit', (code) => {
if (code === 0) {
resolve();
} else {
reject(new PythonRuntimeError(
'RUNTIME_BROKEN',
'Embedded Python verification failed'
));
}
});
});
}
private async installVibeBridge(): Promise<void> {
// Install our custom bridge module that handles IPC
const bridgeSource = this.generateVibeBridgeModule();
const bridgePath = join(this.sitePackages, 'vibe_bridge.py');
await require('fs/promises').writeFile(bridgePath, bridgeSource);
}
private generateVibeBridgeModule(): string {
return `
"""
VibeCodeRehab Python Bridge
Handles communication between Python scripts and TypeScript runtime
"""
import json
import sys
import os
from typing import Any, Optional, Callable
from datetime import datetime
class VibeBridge:
def __init__(self, script_id: str):
self.script_id = script_id
self.ipc_path = os.environ.get('VIBE_IPC_PATH', '/tmp/vibe_ipc')
self._heartbeat_interval = 5 # seconds
def set_state(self, key: str, value: Any) -> None:
"""Set state that persists across script restarts"""
self._send_ipc('STATE_SET', {'key': key, 'value': value})
def get_state(self, key: str, default: Any = None) -> Any:
"""Get persistent state value"""
response = self._send_ipc('STATE_GET', {'key': key})
return response.get('value', default)
def notify_ui(self, message: str, level: str = 'info') -> None:
"""Send notification to UI"""
self._send_ipc('UI_NOTIFY', {
'message': message,
'level': level,
'timestamp': datetime.now().isoformat()
})
def log_progress(self, current: int, total: int, message: str = '') -> None:
"""Report execution progress"""
progress = (current / total) * 100 if total > 0 else 0
self._send_ipc('PROGRESS_UPDATE', {
'current': current,
'total': total,
'percentage': progress,
'message': message
})
def heartbeat(self) -> None:
"""Send heartbeat to indicate script is alive"""
self._send_ipc('HEARTBEAT', {'timestamp': datetime.now().isoformat()})
def _send_ipc(self, command: str, data: dict) -> dict:
"""Send IPC message to TypeScript runtime"""
message = {
'script_id': self.script_id,
'command': command,
'data': data,
'timestamp': datetime.now().isoformat()
}
# Write to stdout in structured format for IPC
print(f"__VIBE_IPC__{json.dumps(message)}__END_IPC__", flush=True)
# For GET operations, we'd need to implement response handling
# This is a simplified version
return {}
# Global bridge instance - initialized by runtime
bridge: Optional[VibeBridge] = None
def init_bridge(script_id: str) -> VibeBridge:
global bridge
bridge = VibeBridge(script_id)
return bridge
def get_bridge() -> VibeBridge:
if bridge is None:
raise RuntimeError("VibeBridge not initialized. Call init_bridge() first.")
return bridge
`;
}
getExecutablePath(): string {
return this.pythonPath;
}
getSitePackagesPath(): string {
return this.sitePackages;
}
}
// =============================================
// DEPENDENCY MANAGER
// =============================================
export class DependencyManager {
constructor(
private readonly interpreter: EmbeddedPythonInterpreter,
private readonly cache: DependencyCache
) {}
async ensureDependencies(dependencies: readonly LibraryDependency[]): Promise<void> {
const missing = await this.findMissingDependencies(dependencies);
if (missing.length > 0) {
await this.installDependencies(missing);
}
// Verify all dependencies are working
await this.verifyDependencies(dependencies);
}
private async findMissingDependencies(
dependencies: readonly LibraryDependency[]
): Promise<LibraryDependency[]> {
const missing: LibraryDependency[] = [];
for (const dep of dependencies) {
const isInstalled = await this.isDependencyInstalled(dep);
if (!isInstalled) {
missing.push(dep);
}
}
return missing;
}
private async isDependencyInstalled(dep: LibraryDependency): Promise<boolean> {
return new Promise((resolve) => {
const proc = spawn(this.interpreter.getExecutablePath(), [
'-c',
`import ${dep.name}; print('${dep.name} installed')`
], { stdio: 'pipe' });
proc.on('exit', (code) => {
resolve(code === 0);
});
});
}
private async installDependencies(dependencies: LibraryDependency[]): Promise<void> {
for (const dep of dependencies) {
await this.installSingleDependency(dep);
}
}
private async installSingleDependency(dep: LibraryDependency): Promise<void> {
// Check cache first
if (await this.cache.has(dep)) {
await this.cache.install(dep, this.interpreter.getSitePackagesPath());
return;
}
// Install using pip
const pipArgs = ['pip', 'install', `${dep.name}==${dep.version}`];
return new Promise((resolve, reject) => {
const proc = spawn(this.interpreter.getExecutablePath(), pipArgs, {
stdio: 'pipe'
});
let stderr = '';
proc.stderr?.on('data', (data) => {
stderr += data.toString();
});
proc.on('exit', async (code) => {
if (code === 0) {
// Cache successful installation
await this.cache.store(dep, this.interpreter.getSitePackagesPath());
resolve();
} else {
reject(new DependencyInstallError(dep, stderr));
}
});
});
}
private async verifyDependencies(dependencies: readonly LibraryDependency[]): Promise<void> {
// Verify all dependencies can be imported together
const importStatements = dependencies
.map(dep => `import ${dep.name}`)
.join('; ');
return new Promise((resolve, reject) => {
const proc = spawn(this.interpreter.getExecutablePath(), [
'-c',
importStatements
], { stdio: 'pipe' });
proc.on('exit', (code) => {
if (code === 0) {
resolve();
} else {
reject(new DependencyConflictError(dependencies));
}
});
});
}
}
// =============================================
// PROCESS MANAGER
// =============================================
export class ProcessManager {
private readonly activeProcesses = new Map<ProcessId, ProcessHandle>();
private readonly processCounter = new ProcessCounter();
async createProcess(
script: ScriptDefinition,
context: ExecutionContext
): Promise<ProcessHandle> {
const processId = this.processCounter.next();
// Prepare execution environment
const env = this.prepareEnvironment(script, context);
const scriptPath = await this.writeScriptToFile(script, context);
// Create Python process
const childProcess = spawn(
context.environment.pythonPath || 'python',
[scriptPath],
{
env,
stdio: ['pipe', 'pipe', 'pipe'],
cwd: context.environment.workingDirectory
}
);
// Create process handle
const handle = new PythonProcessHandle(
processId,
script.id,
childProcess,
context
);
this.activeProcesses.set(processId, handle);
// Setup cleanup on exit
childProcess.on('exit', () => {
this.activeProcesses.delete(processId);
});
return handle;
}
private prepareEnvironment(
script: ScriptDefinition,
context: ExecutionContext
): NodeJS.ProcessEnv {
return {
...process.env,
...context.environment.environmentVars,
VIBE_SCRIPT_ID: script.id,
VIBE_PROCESS_ID: context.processId,
VIBE_IPC_PATH: this.getIPCPath(context.processId),
PYTHONPATH: context.environment.pythonPath
};
}
private async writeScriptToFile(
script: ScriptDefinition,
context: ExecutionContext
): Promise<string> {
const scriptDir = join(context.environment.workingDirectory, 'scripts');
const scriptPath = join(scriptDir, `${script.id}.py`);
if (!existsSync(scriptDir)) {
mkdirSync(scriptDir, { recursive: true });
}
// Inject bridge initialization
const wrappedScript = this.wrapScriptWithBridge(script);
await require('fs/promises').writeFile(scriptPath, wrappedScript);
return scriptPath;
}
private wrapScriptWithBridge(script: ScriptDefinition): string {
return `
# VibeCodeRehab Runtime Injection
import os
import sys
from vibe_bridge import init_bridge
# Initialize bridge
script_id = os.environ.get('VIBE_SCRIPT_ID')
bridge = init_bridge(script_id)
# User script begins here
${script.code}
`;
}
private getIPCPath(processId: ProcessId): string {
return join(os.tmpdir(), `vibe_ipc_${processId}`);
}
getActiveProcesses(): ProcessHandle[] {
return Array.from(this.activeProcesses.values());
}
getProcess(processId: ProcessId): ProcessHandle | undefined {
return this.activeProcesses.get(processId);
}
}
// =============================================
// PYTHON PROCESS HANDLE
// =============================================
class PythonProcessHandle implements ProcessHandle {
private _status: ExecutionState = 'starting';
constructor(
public readonly processId: ProcessId,
public readonly scriptId: ScriptId,
private readonly childProcess: ChildProcess,
private readonly context: ExecutionContext
) {
this.setupProcessMonitoring();
}
get status(): ExecutionState {
return this._status;
}
get pid(): number {
return this.childProcess.pid!;
}
async pause(): Promise<void> {
this.childProcess.kill('SIGSTOP');
this._status = 'paused';
}
async resume(): Promise<void> {
this.childProcess.kill('SIGCONT');
this._status = 'running';
}
async terminate(): Promise<void> {
this.childProcess.kill('SIGTERM');
this._status = 'terminated';
}
async getMetrics(): Promise<ProcessMetrics> {
// Implementation would query system metrics
return {
memoryUsage: 0,
cpuUsage: 0,
uptime: Date.now() - this.context.startTime,
lastHeartbeat: Date.now() as Timestamp
};
}
async sendSignal(signal: string): Promise<void> {
this.childProcess.kill(signal);
}
private setupProcessMonitoring(): void {
this.childProcess.stdout?.on('data', (data) => {
this.handleStdout(data.toString());
});
this.childProcess.stderr?.on('data', (data) => {
this.handleStderr(data.toString());
});
this.childProcess.on('exit', (code, signal) => {
this.handleExit(code, signal);
});
}
private handleStdout(data: string): void {
// Parse IPC messages from stdout
const ipcRegex = /__VIBE_IPC__(.+?)__END_IPC__/g;
let match;
while ((match = ipcRegex.exec(data)) !== null) {
try {
const message = JSON.parse(match[1]);
this.handleIPCMessage(message);
} catch (error) {
console.error('Failed to parse IPC message:', error);
}
}
// Regular stdout (after removing IPC messages)
const cleanOutput = data.replace(ipcRegex, '');
if (cleanOutput.trim()) {
// Emit stdout event
this.emit('stdout', cleanOutput);
}
}
private handleStderr(data: string): void {
this.emit('stderr', data);
}
private handleExit(code: number | null, signal: string | null): void {
if (code === 0) {
this._status = 'completed';
} else {
this._status = 'failed';
}
this.emit('exit', { code, signal });
}
private handleIPCMessage(message: any): void {
switch (message.command) {
case 'STATE_SET':
this.context.statePod.set(message.data.key, message.data.value);
break;
case 'UI_NOTIFY':
this.emit('notification', message.data);
break;
case 'PROGRESS_UPDATE':
this.emit('progress', message.data);
break;
case 'HEARTBEAT':
this.emit('heartbeat', message.data);
break;
}
}
private emit(event: string, data: any): void {
// EventEmitter implementation
// This would integrate with the broader event system
}
}
// =============================================
// SUPPORTING TYPES AND CLASSES
// =============================================
interface PythonConfig {
runtimePath: string;
sitePackagesPath?: string;
version: string;
}
interface DependencyCache {
has(dep: LibraryDependency): Promise<boolean>;
store(dep: LibraryDependency, sourcePath: string): Promise<void>;
install(dep: LibraryDependency, targetPath: string): Promise<void>;
}
class ProcessCounter {
private counter = 0;
next(): ProcessId {
return `proc_${++this.counter}` as ProcessId;
}
}
// =============================================
// ERROR CLASSES
// =============================================
class PythonRuntimeError extends VibeCodeError {
readonly category = 'system';
readonly recoverable = false;
constructor(
public readonly code: string,
message: string
) {
super(message);
}
}
class DependencyInstallError extends VibeCodeError {
readonly code = 'DEPENDENCY_INSTALL_FAILED';
readonly category = 'dependency';
readonly recoverable = true;
constructor(
public readonly dependency: LibraryDependency,
public readonly installError: string
) {
super(`Failed to install ${dependency.name}: ${installError}`);
}
}
class DependencyConflictError extends VibeCodeError {
readonly code = 'DEPENDENCY_CONFLICT';
readonly category = 'dependency';
readonly recoverable = true;
constructor(
public readonly dependencies: readonly LibraryDependency[]
) {
super(`Dependency conflict detected among: ${dependencies.map(d => d.name).join(', ')}`);
}
}
// =============================================
// EXECUTION ENGINE - ORCHESTRATION LAYER
// =============================================
import { EventEmitter } from 'events';
import { resolve, join } from 'path';
import {
ScriptDefinition,
ExecutionContext,
ExecutionResult,
ProcessHandle,
ScriptId,
ProcessId,
Timestamp,
ExecutionState,
StatePodHandle,
StateSnapshot,
SystemEvent,
ScriptCategory
} from './domain-contracts';
import {
EmbeddedPythonInterpreter,
DependencyManager,
ProcessManager
} from './python-runtime';
// =============================================
// MAIN EXECUTION ENGINE
// =============================================
export class ExecutionEngine extends EventEmitter {
private readonly pythonInterpreter: EmbeddedPythonInterpreter;
private readonly dependencyManager: DependencyManager;
private readonly processManager: ProcessManager;
private readonly stateEngine: StateEngine;
private readonly categoryManagers: Map<ScriptCategory, CategoryExecutionManager>;
constructor(config: ExecutionEngineConfig) {
super();
this.pythonInterpreter = new EmbeddedPythonInterpreter(config.python);
this.dependencyManager = new DependencyManager(
this.pythonInterpreter,
config.dependencyCache
);
this.processManager = new ProcessManager();
this.stateEngine = new StateEngine(config.database);
// Initialize category-specific execution managers
this.categoryManagers = new Map([
['scheduled', new ScheduledExecutionManager(this)],
['reactive', new ReactiveExecutionManager(this)],
['interactive', new InteractiveExecutionManager(this)],
['persistent', new PersistentExecutionManager(this)]
]);
}
async initialize(): Promise<void> {
// Initialize core components
await this.pythonInterpreter.initialize();
await this.stateEngine.initialize();
// Initialize category managers
for (const manager of this.categoryManagers.values()) {
await manager.initialize();
}
this.emit('system.ready');
}
async executeScript(script: ScriptDefinition): Promise<ExecutionResult> {
const executionId = this.generateExecutionId();
try {
// Pre-execution validation
await this.validateScript(script);
// Ensure dependencies
await this.dependencyManager.ensureDependencies(script.dependencies);
// Get category-specific manager
const categoryManager = this.categoryManagers.get(script.category);
if (!categoryManager) {
throw new Error(`No manager for category: ${script.category}`);
}
// Execute via category manager
const result = await categoryManager.execute(script, executionId);
this.emit('script.execution', {
type: 'script.execution',
scriptId: script.id,
processId: result.processId,
state: result.status,
timestamp: Date.now() as Timestamp
} as SystemEvent);
return result;
} catch (error) {
this.emit('script.error', {
type: 'script.execution',
scriptId: script.id,
processId: executionId,
state: 'failed',
timestamp: Date.now() as Timestamp,
metadata: { error: error.message }
} as SystemEvent);
throw error;
}
}
async scheduleScript(script: ScriptDefinition): Promise<void> {
if (script.category !== 'scheduled') {
throw new Error('Only scheduled scripts can be scheduled');
}
const manager = this.categoryManagers.get('scheduled') as ScheduledExecutionManager;
await manager.schedule(script);
}
async startReactiveScript(script: ScriptDefinition): Promise<void> {
if (script.category !== 'reactive') {
throw new Error('Only reactive scripts can be started as reactive');
}
const manager = this.categoryManagers.get('reactive') as ReactiveExecutionManager;
await manager.startWatching(script);
}
async getExecutionStatus(processId: ProcessId): Promise<ExecutionState> {
const process = this.processManager.getProcess(processId);
return process?.status || 'failed';
}
async getActiveExecutions(): Promise<ProcessHandle[]> {
return this.processManager.getActiveProcesses();
}
async recoverFromCrash(scriptId: ScriptId): Promise<void> {
const script = await this.getScript(scriptId);
const lastSnapshot = await this.stateEngine.getLastSnapshot(scriptId);
if (lastSnapshot) {
// Restart with recovered state
const context = await this.createExecutionContext(script, lastSnapshot);
await this.executeScript(script);
}
}
private async validateScript(script: ScriptDefinition): Promise<void> {
// Syntax validation
if (!script.code.trim()) {
throw new Error('Script code cannot be empty');
}
// Category-specific validation
const manager = this.categoryManagers.get(script.category);
if (manager && manager.validateScript) {
await manager.validateScript(script);
}
}
private generateExecutionId(): ProcessId {
return `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` as ProcessId;
}
private async createExecutionContext(
script: ScriptDefinition,
stateSnapshot?: StateSnapshot
): Promise<ExecutionContext> {
const processId = this.generateExecutionId();
const statePod = await this.stateEngine.createStatePod(script.id);
if (stateSnapshot) {
await statePod.restoreFromSnapshot(stateSnapshot);
}
return {
scriptId: script.id,
processId,
category: script.category,
startTime: Date.now() as Timestamp,
environment: {
pythonVersion: '3.11',
workingDirectory: resolve('./runtime/workspace'),
environmentVars: {},
resourceLimits: {
maxMemory: script.config.memory || 512,
maxCpu: 80,
maxDuration: script.config.timeout || 300000
}
},
statePod
};
}
private async getScript(scriptId: ScriptId): Promise<ScriptDefinition> {
// This would integrate with script storage
throw new Error('Script storage not implemented');
}
}
// =============================================
// STATE ENGINE - EVENT SOURCED PERSISTENCE
// =============================================
export class StateEngine {
private readonly statePods = new Map<ScriptId, StatePodImplementation>();
constructor(private readonly database: StateDatabase) {}
async initialize(): Promise<void> {
await this.database.initialize();
}
async createStatePod(scriptId: ScriptId): Promise<StatePodHandle> {
if (this.statePods.has(scriptId)) {
return this.statePods.get(scriptId)!;
}
const pod = new StatePodImplementation(scriptId, this.database);
await pod.initialize();
this.statePods.set(scriptId, pod);
return pod;
}
async getLastSnapshot(scriptId: ScriptId): Promise<StateSnapshot | undefined> {
return this.database.getLastSnapshot(scriptId);
}
async cleanupOldStates(maxAge: number): Promise<void> {
const cutoff = Date.now() - maxAge;
await this.database.deleteEventsBefore(cutoff as Timestamp);
}
}
class StatePodImplementation implements StatePodHandle {
private state: Map<string, unknown> = new Map();
private subscribers: Map<string, Set<(value: unknown) => void>> = new Map();
constructor(
public readonly scriptId: ScriptId,
private readonly database: StateDatabase
) {}
async initialize(): Promise<void> {
// Replay events to rebuild state
const events = await this.database.getEvents(this.scriptId);
for (const event of events) {
this.applyEvent(event);
}
}
async get<T>(key: string): Promise<T | undefined> {
return this.state.get(key) as T | undefined;
}
async set<T>(key: string, value: T): Promise<void> {
const oldValue = this.state.get(key);
this.state.set(key, value);
// Log event
await this.database.logEvent({
timestamp: Date.now() as Timestamp,
action: 'SET',
key,
value,
metadata: { scriptId: this.scriptId }
});
// Notify subscribers
const keySubscribers = this.subscribers.get(key);
if (keySubscribers) {
for (const callback of keySubscribers) {
callback(value);
}
}
}
async delete(key: string): Promise<void> {
this.state.delete(key);
await this.database.logEvent({
timestamp: Date.now() as Timestamp,
action: 'DELETE',
key,
metadata: { scriptId: this.scriptId }
});
}
subscribe<T>(key: string, callback: (value: T) => void): () => void {
if (!this.subscribers.has(key)) {
this.subscribers.set(key, new Set());
}
this.subscribers.get(key)!.add(callback as (value: unknown) => void);
// Return unsubscribe function
return () => {
this.subscribers.get(key)?.delete(callback as (value: unknown) => void);
};
}
async getHistory(key: string): Promise<StateEvent[]> {
return this.database.getEventsForKey(this.scriptId, key);
}
async createSnapshot(): Promise<StateSnapshot> {
const snapshot: StateSnapshot = {
scriptId: this.scriptId,
timestamp: Date.now() as Timestamp,
state: Object.fromEntries(this.state),
checksum: this.calculateChecksum()
};
await this.database.saveSnapshot(snapshot);
return snapshot;
}
async restoreFromSnapshot(snapshot: StateSnapshot): Promise<void> {
this.state.clear();
for (const [key, value] of Object.entries(snapshot.state)) {
this.state.set(key, value);
}
}
private applyEvent(event: StateEvent): void {
switch (event.action) {
case 'SET':
this.state.set(event.key, event.value);
break;
case 'DELETE':
this.state.delete(event.key);
break;
}
}
private calculateChecksum(): string {
// Simple checksum implementation
const stateString = JSON.stringify(Object.fromEntries(this.state));
return require('crypto').createHash('md5').update(stateString).digest('hex');
}
}
// =============================================
// CATEGORY-SPECIFIC EXECUTION MANAGERS
// =============================================
abstract class CategoryExecutionManager {
constructor(protected executionEngine: ExecutionEngine) {}
abstract initialize(): Promise<void>;
abstract execute(script: ScriptDefinition, executionId: ProcessId): Promise<ExecutionResult>;
validateScript?(script: ScriptDefinition): Promise<void>;
}
class ScheduledExecutionManager extends CategoryExecutionManager {
private scheduledJobs = new Map<ScriptId, NodeJS.Timeout>();
async initialize(): Promise<void> {
// Restore scheduled jobs from database
}
async execute(script: ScriptDefinition, executionId: ProcessId): Promise<ExecutionResult> {
// Direct execution for scheduled scripts
const context = await this.createContext(script, executionId);
const process = await this.executionEngine['processManager'].createProcess(script, context);
return new Promise((resolve) => {
process.on('exit', ({ code }: { code: number }) => {
resolve({
scriptId: script.id,
processId: executionId,
status: code === 0 ? 'completed' : 'failed',
startTime: context.startTime,
endTime: Date.now() as Timestamp,
exitCode: code,
stdout: '',
stderr: '',
metrics: {
memoryPeak: 0,
cpuTime: 0,
duration: Date.now() - context.startTime,
stateChanges: 0
}
});
});
});
}
async schedule(script: ScriptDefinition): Promise<void> {
if (!script.config.schedule) {
throw new Error('Scheduled script must have schedule configuration');
}
const interval = this.parseSchedule(script.config.schedule.expression);
const job = setInterval(async () => {
try {
await this.execute(script, this.generateExecutionId());
} catch (error) {
console.error(`Scheduled execution failed for ${script.id}:`, error);
}
}, interval);
this.scheduledJobs.set(script.id, job);
}
private parseSchedule(expression: string): number {
// Simple interval parsing - would be more sophisticated in production
const match = expression.match(/(\d+)\s*(minutes?|hours?|days?)/);
if (!match) throw new Error('Invalid schedule expression');
const value = parseInt(match[1]);
const unit = match[2];
switch (unit) {
case 'minute':
case 'minutes':
return value * 60 * 1000;
case 'hour':
case 'hours':
return value * 60 * 60 * 1000;
case 'day':
case 'days':
return value * 24 * 60 * 60 * 1000;
default:
throw new Error('Unsupported time unit');
}
}
private async createContext(script: ScriptDefinition, processId: ProcessId): Promise<ExecutionContext> {
// Implementation would be shared with main execution engine
throw new Error('Context creation not implemented');
}
private generateExecutionId(): ProcessId {
return `sched_${Date.now()}` as ProcessId;
}
}
class ReactiveExecutionManager extends CategoryExecutionManager {
private watchers = new Map<ScriptId, any>();
async initialize(): Promise<void> {
// Initialize file system watchers
}
async execute(script: ScriptDefinition, executionId: ProcessId): Promise<ExecutionResult> {
// Reactive scripts are triggered by events, not direct execution
throw new Error('Reactive scripts cannot be executed directly');
}
async startWatching(script: ScriptDefinition): Promise<void> {
if (!script.config.watchPaths) {
throw new Error('Reactive script must have watch paths configured');
}
// Implementation would use chokidar or similar
// For brevity, using a placeholder
console.log(`Starting to watch ${script.config.watchPaths} for ${script.id}`);
}
}
class InteractiveExecutionManager extends CategoryExecutionManager {
async initialize(): Promise<void> {
// No special initialization needed
}
async execute(script: ScriptDefinition, executionId: ProcessId): Promise<ExecutionResult> {
// Interactive scripts run once and return results
const context = await this.createContext(script, executionId);
const process = await this.executionEngine['processManager'].createProcess(script, context);
return this.waitForCompletion(process, context);
}
private async createContext(script: ScriptDefinition, processId: ProcessId): Promise<ExecutionContext> {
// Implementation details
throw new Error('Context creation not implemented');
}
private async waitForCompletion(process: ProcessHandle, context: ExecutionContext): Promise<ExecutionResult> {
// Implementation details
throw new Error('Completion waiting not implemented');
}
}
class PersistentExecutionManager extends CategoryExecutionManager {
async initialize(): Promise<void> {
// Restore persistent scripts that should be running
}
async execute(script: ScriptDefinition, executionId: ProcessId): Promise<ExecutionResult> {
// Persistent scripts run indefinitely
const context = await this.createContext(script, executionId);
const process = await this.executionEngine['processManager'].createProcess(script, context);
// Don't wait for completion - return immediately
return {
scriptId: script.id,
processId: executionId,
status: 'running',
startTime: context.startTime,
stdout: '',
stderr: '',
metrics: {
memoryPeak: 0,
cpuTime: 0,
duration: 0,
stateChanges: 0
}
};
}
private async createContext(script: ScriptDefinition, processId: ProcessId): Promise<ExecutionContext> {
// Implementation details
throw new Error('Context creation not implemented');
}
}
// =============================================
// SUPPORTING INTERFACES
// =============================================
interface ExecutionEngineConfig {
python: {
runtimePath: string;
version: string;
};
dependencyCache: any;
database: StateDatabase;
}
interface StateDatabase {
initialize(): Promise<void>;
logEvent(event: StateEvent): Promise<void>;
getEvents(scriptId: ScriptId): Promise<StateEvent[]>;
getEventsForKey(scriptId: ScriptId, key: string): Promise<StateEvent[]>;
saveSnapshot(snapshot: StateSnapshot): Promise<void>;
getLastSnapshot(scriptId: ScriptId): Promise<StateSnapshot | undefined>;
deleteEventsBefore(timestamp: Timestamp): Promise<void>;
}
// =============================================
// LIBRARY TEMPLATE SYSTEM
// =============================================
import { z } from 'zod';
import {
ScriptTemplate,
ScriptCategory,
LibraryDependency,
TemplateExample
} from './domain-contracts';
// =============================================
// TEMPLATE REGISTRY
// =============================================
export class TemplateRegistry {
private readonly templates = new Map<string, ScriptTemplate>();
constructor() {
this.registerCoreTemplates();
}
private registerCoreTemplates(): void {
// Scheduled Category Templates
this.register(ScheduleAutomationTemplate);
this.register(EmailAutomationTemplate);
this.register(ScheduledAnnouncementTemplate);
// Reactive Category Templates
this.register(FileWatcherTemplate);
this.register(WebScrapingTemplate);
// Interactive Category Templates
this.register(GUIAutomationTemplate);
this.register(OCRProcessingTemplate);
this.register(ExcelProcessingTemplate);
// Persistent Category Templates
this.register(VoiceInterfaceTemplate);
this.register(ContinuousMonitoringTemplate);
}
register(template: ScriptTemplate): void {
this.templates.set(template.id, template);
}
getTemplate(id: string): ScriptTemplate | undefined {
return this.templates.get(id);
}
getTemplatesByCategory(category: ScriptCategory): ScriptTemplate[] {
return Array.from(this.templates.values())
.filter(template => template.category === category);
}
getAllTemplates(): ScriptTemplate[] {
return Array.from(this.templates.values());
}
generateScript(templateId: string, config: unknown): string {
const template = this.getTemplate(templateId);
if (!template) {
throw new Error(`Template not found: ${templateId}`);
}
// Validate config against schema
const validatedConfig = template.configSchema.parse(config);
// Generate script from template
return this.interpolateTemplate(template.scaffold, validatedConfig);
}
private interpolateTemplate(scaffold: string, config: any): string {
let result = scaffold;
// Simple template interpolation - replace ${key} with config values
for (const [key, value] of Object.entries(config)) {
const placeholder = `\${${key}}`;
result = result.replace(new RegExp(placeholder, 'g'), String(value));
}
return result;
}
}
// =============================================
// SCHEDULED CATEGORY TEMPLATES
// =============================================
export const ScheduleAutomationTemplate: ScriptTemplate = {
id: 'schedule-automation',
name: 'Schedule-Based Automation',
category: 'scheduled',
description: 'Automate tasks to run at specific times or intervals',
dependencies: [
{ name: 'schedule', version: '1.2.0', systemDeps: [] }
],
configSchema: z.object({
interval: z.number().min(1),
unit: z.enum(['minutes', 'hours', 'days']),
taskName: z.string(),
notifyOnComplete: z.boolean().default(true)
}),
scaffold: `
import schedule
import time
from vibe_bridge import get_bridge
bridge = get_bridge()
def \${taskName}_task():
"""
Your automation task goes here.
This function will run every \${interval} \${unit}.
"""
bridge.notify_ui("Starting \${taskName} task", "info")
# TODO: Implement your task logic here
# Example:
# - File processing
# - Data backups
# - System maintenance
# - Report generation
result = "Task completed successfully"
bridge.set_state("last_run_result", result)
if \${notifyOnComplete}:
bridge.notify_ui(f"\${taskName} completed: {result}", "success")
def main():
bridge.notify_ui("Scheduler started for \${taskName}", "info")
# Schedule the task
schedule.every(\${interval}).\${unit}.do(\${taskName}_task)
# Keep the script running
while True:
schedule.run_pending()
bridge.heartbeat()
time.sleep(60) # Check every minute
if __name__ == "__main__":
main()
`,
examples: [
{
name: 'Daily Backup',
description: 'Run a backup task every day at a specific time',
config: {
interval: 1,
unit: 'days',
taskName: 'backup',
notifyOnComplete: true
},
expectedOutput: 'Backup task scheduled to run daily'
},
{
name: 'Hourly Health Check',
description: 'Check system health every hour',
config: {
interval: 1,
unit: 'hours',
taskName: 'health_check',
notifyOnComplete: false
},
expectedOutput: 'Health check scheduled hourly'
}
]
};
export const EmailAutomationTemplate: ScriptTemplate = {
id: 'email-automation',
name: 'Email Automation',
category: 'scheduled',
description: 'Send automated emails on a schedule',
dependencies: [
{ name: 'schedule', version: '1.2.0', systemDeps: [] }
],
configSchema: z.object({
smtpServer: z.string(),
smtpPort: z.number().default(587),
username: z.string().email(),
password: z.string(),
recipients: z.array(z.string().email()),
subject: z.string(),
schedule: z.string(),
reportType: z.enum(['daily', 'weekly', 'monthly'])
}),
scaffold: `
import smtplib
import schedule
import time
from email.message import EmailMessage
from datetime import datetime
from vibe_bridge import get_bridge
bridge = get_bridge()
def send_automated_email():
"""Send automated email report"""
bridge.notify_ui("Preparing \${reportType} email report", "info")
try:
# Create email message
msg = EmailMessage()
# Generate report content
report_content = generate_report_content()
msg.set_content(report_content)
msg['Subject'] = f"\${subject} - {datetime.now().strftime('%Y-%m-%d')}"
msg['From'] = "\${username}"
msg['To'] = ", ".join(\${recipients})
# Send email
with smtplib.SMTP('\${smtpServer}', \${smtpPort}) as smtp:
smtp.starttls()
smtp.login('\${username}', '\${password}')
smtp.send_message(msg)
bridge.set_state("last_email_sent", datetime.now().isoformat())
bridge.notify_ui("Email sent successfully", "success")
except Exception as e:
bridge.notify_ui(f"Email sending failed: {str(e)}", "error")
raise
def generate_report_content():
"""Generate the email report content"""
# Get data from state
last_run = bridge.get_state("last_run_result", "No previous data")
content = f"""
\${reportType.title()} Report - {datetime.now().strftime('%Y-%m-%d %H:%M')}
System Status: Operational
Last Task Result: {last_run}
Generated automatically by VibeCodeRehab
"""
return content
def main():
bridge.notify_ui("Email automation started", "info")
# Parse schedule expression
schedule.every().day.at("\${schedule}").do(send_automated_email)
while True:
schedule.run_pending()
bridge.heartbeat()
time.sleep(60)
if __name__ == "__main__":
main()
`,
examples: [
{
name: 'Daily Status Report',
description: 'Send daily status emails to team',
config: {
smtpServer: 'smtp.gmail.com',
smtpPort: 587,
username: '[email protected]',
password: 'app_password',
recipients: ['[email protected]'],
subject: 'Daily System Status',
schedule: '09:00',
reportType: 'daily'
},
expectedOutput: 'Daily email scheduled for 9:00 AM'
}
]
};
// =============================================
// REACTIVE CATEGORY TEMPLATES
// =============================================
export const FileWatcherTemplate: ScriptTemplate = {
id: 'file-watcher',
name: 'File System Watcher',
category: 'reactive',
description: 'Monitor files and folders for changes and react automatically',
dependencies: [
{ name: 'watchdog', version: '2.1.9', systemDeps: [] }
],
configSchema: z.object({
watchPath: z.string(),
fileTypes: z.array(z.string()),
actionType: z.enum(['move', 'copy', 'process', 'notify']),
targetPath: z.string().optional(),
recursive: z.boolean().default(true)
}),
scaffold: `
import os
import shutil
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from vibe_bridge import get_bridge
bridge = get_bridge()
class FileHandler(FileSystemEventHandler):
def __init__(self):
super().__init__()
self.processed_count = 0
def on_created(self, event):
if event.is_directory:
return
file_path = event.src_path
file_name = os.path.basename(file_path)
file_ext = os.path.splitext(file_name)[1].lower()
# Check if file type matches our filter
if file_ext in [\${fileTypes.map(t => `'${t}'`).join(', ')}]:
bridge.notify_ui(f"New file detected: {file_name}", "info")
self.process_file(file_path, file_name)
def process_file(self, file_path, file_name):
"""Process the detected file"""
try:
action = "\${actionType}"
if action == "move":
self.move_file(file_path, file_name)
elif action == "copy":
self.copy_file(file_path, file_name)
elif action == "process":
self.process_file_content(file_path, file_name)
elif action == "notify":
self.notify_only(file_path, file_name)
self.processed_count += 1
bridge.set_state("files_processed", self.processed_count)
except Exception as e:
bridge.notify_ui(f"Error processing {file_name}: {str(e)}", "error")
def move_file(self, file_path, file_name):
"""Move file to target directory"""
target_dir = "\${targetPath}"
if not os.path.exists(target_dir):
os.makedirs(target_dir)
target_path = os.path.join(target_dir, file_name)
shutil.move(file_path, target_path)
bridge.notify_ui(f"Moved {file_name} to {target_dir}", "success")
def copy_file(self, file_path, file_name):
"""Copy file to target directory"""
target_dir = "\${targetPath}"
if not os.path.exists(target_dir):
os.makedirs(target_dir)
target_path = os.path.join(target_dir, file_name)
shutil.copy2(file_path, target_path)
bridge.notify_ui(f"Copied {file_name} to {target_dir}", "success")
def process_file_content(self, file_path, file_name):
"""Process file content (implement your logic here)"""
# TODO: Add your file processing logic
# Examples:
# - Extract data from CSV/Excel files
# - Process images
# - Parse log files
# - Convert file formats
bridge.notify_ui(f"Processed content of {file_name}", "success")
def notify_only(self, file_path, file_name):
"""Just notify about the file"""
bridge.notify_ui(f"File detected: {file_name} at {file_path}", "info")
def main():
watch_path = "\${watchPath}"
if not os.path.exists(watch_path):
bridge.notify_ui(f"Watch path does not exist: {watch_path}", "error")
return
bridge.notify_ui(f"Starting file watcher on: {watch_path}", "info")
event_handler = FileHandler()
observer = Observer()
observer.schedule(event_handler, watch_path, recursive=\${recursive})
observer.start()
bridge.notify_ui("File watcher is running", "success")
try:
while True:
bridge.heartbeat()
time.sleep(10)
except KeyboardInterrupt:
observer.stop()
bridge.notify_ui("File watcher stopped", "info")
observer.join()
if __name__ == "__main__":
main()
`,
examples: [
{
name: 'PDF Organizer',
description: 'Automatically organize PDFs by moving them to a sorted folder',
config: {
watchPath: '/home/user/Downloads',
fileTypes: ['.pdf'],
actionType: 'move',
targetPath: '/home/user/Documents/PDFs',
recursive: false
},
expectedOutput: 'PDF files will be automatically moved to Documents/PDFs'
}
]
};
export const WebScrapingTemplate: ScriptTemplate = {
id: 'web-scraping',
name: 'Web Content Scraper',
category: 'reactive',
description: 'Scrape web content on schedule or trigger',
dependencies: [
{ name: 'requests', version: '2.28.1', systemDeps: [] },
{ name: 'beautifulsoup4', version: '4.11.1', systemDeps: [] },
{ name: 'schedule', version: '1.2.0', systemDeps: [] }
],
configSchema: z.object({
url: z.string().url(),
selector: z.string(),
interval: z.number().min(1),
saveToFile: z.boolean().default(true),
notifyOnChange: z.boolean().default(true)
}),
scaffold: `
import requests
import schedule
import time
import hashlib
from bs4 import BeautifulSoup
from datetime import datetime
from vibe_bridge import get_bridge
bridge = get_bridge()
def scrape_content():
"""Scrape content from the target URL"""
url = "\${url}"
selector = "\${selector}"
try:
bridge.notify_ui(f"Scraping content from {url}", "info")
# Fetch the page
response = requests.get(url, timeout=30)
response.raise_for_status()
# Parse with BeautifulSoup
soup = BeautifulSoup(response.text, 'html.parser')
# Extract content using selector
if selector:
elements = soup.select(selector)
content = "\\n".join([elem.get_text().strip() for elem in elements])
else:
content = soup.get_text().strip()
# Check for changes
content_hash = hashlib.md5(content.encode()).hexdigest()
last_hash = bridge.get_state("last_content_hash", "")
if content_hash != last_hash:
bridge.set_state("last_content_hash", content_hash)
bridge.set_state("last_content", content)
bridge.set_state("last_scraped", datetime.now().isoformat())
if \${saveToFile}:
save_content_to_file(content)
if \${notifyOnChange}:
bridge.notify_ui("Content changed! New data scraped.", "success")
return True
else:
bridge.notify_ui("No changes detected", "info")
return False
except Exception as e:
bridge.notify_ui(f"Scraping failed: {str(e)}", "error")
raise
def save_content_to_file(content):
"""Save scraped content to file"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"scraped_content_{timestamp}.txt"
with open(filename, 'w', encoding='utf-8') as f:
f.write(content)
bridge.notify_ui(f"Content saved to {filename}", "success")
def main():
bridge.notify_ui("Web scraper started", "info")
# Schedule scraping
schedule.every(\${interval}).minutes.do(scrape_content)
# Run initial scrape
scrape_content()
while True:
schedule.run_pending()
bridge.heartbeat()
time.sleep(60)
if __name__ == "__main__":
main()
`,
examples: [
{
name: 'News Monitor',
description: 'Monitor news website for new headlines',
config: {
url: 'https://news.ycombinator.com',
selector: '.storylink',
interval: 30,
saveToFile: true,
notifyOnChange: true
},
expectedOutput: 'News headlines will be monitored every 30 minutes'
}
]
};
// =============================================
// INTERACTIVE CATEGORY TEMPLATES
// =============================================
export const GUIAutomationTemplate: ScriptTemplate = {
id: 'gui-automation',
name: 'GUI Automation',
category: 'interactive',
description: 'Automate GUI interactions and workflows',
dependencies: [
{ name: 'pyautogui', version: '0.9.53', systemDeps: [] },
{ name: 'pygetwindow', version: '0.0.9', systemDeps: [] }
],
configSchema: z.object({
applicationName: z.string(),
actionSequence: z.array(z.object({
type: z.enum(['click', 'type', 'key', 'wait']),
value: z.string(),
x: z.number().optional(),
y: z.number().optional()
})),
safetyDelay: z.number().default(1),
failsafe: z.boolean().default(true)
}),
scaffold: `
import pyautogui
import pygetwindow as gw
import time
from vibe_bridge import get_bridge
bridge = get_bridge()
# Configure PyAutoGUI
pyautogui.FAILSAFE = \${failsafe}
pyautogui.PAUSE = \${safetyDelay}
def execute_automation():
"""Execute the GUI automation sequence"""
app_name = "\${applicationName}"
try:
bridge.notify_ui(f"Starting GUI automation for {app_name}", "info")
# Find and focus the application window
windows = gw.getWindowsWithTitle(app_name)
if not windows:
bridge.notify_ui(f"Application '{app_name}' not found", "error")
return False
window = windows[0]
window.activate()
time.sleep(2) # Wait for window to become active
# Execute action sequence
actions = \${JSON.stringify(actionSequence)}
for i, action in enumerate(actions):
bridge.log_progress(i + 1, len(actions), f"Executing {action['type']} action")
if action['type'] == 'click':
if 'x' in action and 'y' in action:
pyautogui.click(action['x'], action['y'])
else:
# Click on element by image or text (simplified)
bridge.notify_ui(f"Clicking on {action['value']}", "info")
# TODO: Implement image/text recognition clicking
elif action['type'] == 'type':
pyautogui.write(action['value'])
elif action['type'] == 'key':
pyautogui.press(action['value'])
elif action['type'] == 'wait':
time.sleep(float(action['value']))
time.sleep(\${safetyDelay})
bridge.notify_ui("GUI automation completed successfully", "success")
bridge.set_state("last_automation", time.time())
return True
except Exception as e:
bridge.notify_ui(f"GUI automation failed: {str(e)}", "error")
return False
def take_screenshot():
"""Take a screenshot for debugging"""
timestamp = time.strftime("%Y%m%d_%H%M%S")
filename = f"screenshot_{timestamp}.png"
pyautogui.screenshot(filename)
bridge.notify_ui(f"Screenshot saved: {filename}", "info")
def main():
bridge.notify_ui("GUI automation script ready", "info")
# Take initial screenshot
take_screenshot()
# Execute automation
success = execute_automation()
if success:
bridge.notify_ui("Automation completed", "success")
else:
bridge.notify_ui("Automation failed", "error")
if __name__ == "__main__":
main()
`,
examples: [
{
name: 'Browser Search',
description: 'Open browser and perform a search',
config: {
applicationName: 'Chrome',
actionSequence: [
{ type: 'key', value: 'ctrl+l' },
{ type: 'type', value: 'google.com' },
{ type: 'key', value: 'enter' },
{ type: 'wait', value: '3' },
{ type: 'type', value: 'python automation' },
{ type: 'key', value: 'enter' }
],
safetyDelay: 1,
failsafe: true
},
expectedOutput: 'Browser will open and perform a Google search'
}
]
};
export const OCRProcessingTemplate: ScriptTemplate = {
id: 'ocr-processing',
name: 'OCR Text Extraction',
category: 'interactive',
description: 'Extract text from images using OCR',
dependencies: [
{ name: 'pytesseract', version: '0.3.10', systemDeps: ['tesseract-ocr'] },
{ name: 'Pillow', version: '9.2.0', systemDeps: [] }
],
configSchema: z.object({
inputPath: z.string(),
outputPath: z.string(),
language: z.string().default('eng'),
confidence: z.number().min(0).max(100).default(50),
preprocessImage: z.boolean().default(true)
}),
scaffold: `
import os
import pytesseract
from PIL import Image, ImageEnhance, ImageFilter
from vibe_bridge import get_bridge
bridge = get_bridge()
def preprocess_image(image_path):
"""Preprocess image to improve OCR accuracy"""
bridge.notify_ui("Preprocessing image for better OCR", "info")
# Open image
img = Image.open(image_path)
# Convert to grayscale
img = img.convert('L')
# Enhance contrast
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(2.0)
# Apply slight blur to smooth text
img = img.filter(ImageFilter.MedianFilter())
return img
def extract_text_from_image(image_path):
"""Extract text from a single image"""
try:
bridge.notify_ui(f"Processing image: {os.path.basename(image_path)}", "info")
# Load and preprocess image
if \${preprocessImage}:
img = preprocess_image(image_path)
else:
img = Image.open(image_path)
# Configure tesseract
config = f'--oem 3 --psm 6 -l \${language}'
# Extract text
extracted_text = pytesseract.image_to_string(img, config=config)
# Get confidence scores
data = pytesseract.image_to_data(img, output_type=pytesseract.Output.DICT)
confidences = [int(conf) for conf in data['conf'] if int(conf) > 0]
avg_confidence = sum(confidences) / len(confidences) if confidences else 0
bridge.notify_ui(f"OCR completed with {avg_confidence:.1f}% confidence", "success")
if avg_confidence < \${confidence}:
bridge.notify_ui(f"Low confidence warning: {avg_confidence:.1f}%", "warning")
return extracted_text.strip(), avg_confidence
except Exception as e:
bridge.notify_ui(f"OCR failed for {image_path}: {str(e)}", "error")
return "", 0
def process_images():
"""Process all images in the input directory"""
input_path = "\${inputPath}"
output_path = "\${outputPath}"
# Ensure output directory exists
os.makedirs(output_path, exist_ok=True)
# Supported image formats
supported_formats = ('.png', '.jpg', '.jpeg', '.tiff', '.bmp', '.gif')
# Process images
processed_count = 0
if os.path.isfile(input_path):
# Single file
images = [input_path]
else:
# Directory
images = [
os.path.join(input_path, f)
for f in os.listdir(input_path)
if f.lower().endswith(supported_formats)
]
total_images = len(images)
bridge.notify_ui(f"Found {total_images} images to process", "info")
for i, image_path in enumerate(images):
bridge.log_progress(i + 1, total_images, f"Processing {os.path.basename(image_path)}")
text, confidence = extract_text_from_image(image_path)
if text:
# Save extracted text
base_name = os.path.splitext(os.path.basename(image_path))[0]
text_file = os.path.join(output_path, f"{base_name}_extracted.txt")
with open(text_file, 'w', encoding='utf-8') as f:
f.write(f"Source: {image_path}\\n")
f.write(f"Confidence: {confidence:.1f}%\\n")
f.write(f"Language: \${language}\\n")
f.write("=" * 50 + "\\n\\n")
f.write(text)
processed_count += 1
bridge.set_state(f"extracted_text_{base_name}", text)
bridge.set_state("total_processed", processed_count)
bridge.notify_ui(f"OCR processing complete: {processed_count}/{total_images} images", "success")
def main():
bridge.notify_ui("OCR text extraction started", "info")
# Verify tesseract installation
try:
pytesseract.get_tesseract_version()
bridge.notify_ui("Tesseract OCR engine detected", "success")
except Exception:
bridge.notify_ui("Tesseract not found. Please install tesseract-ocr", "error")
return
# Process images
process_images()
bridge.notify_ui("OCR extraction completed", "success")
if __name__ == "__main__":
main()
`,
examples: [
{
name: 'Document Scanner',
description: 'Extract text from scanned documents',
config: {
inputPath: '/path/to/scanned/documents',
outputPath: '/path/to/extracted/text',
language: 'eng',
confidence: 60,
preprocessImage: true
},
expectedOutput: 'Text will be extracted from all images and saved as .txt files'
}
]
};
// =============================================
// PERSISTENT CATEGORY TEMPLATES
// =============================================
export const VoiceInterfaceTemplate: ScriptTemplate = {
id: 'voice-interface',
name: 'Voice Interface',
category: 'persistent',
description: 'Continuous voice interaction and text-to-speech',
dependencies: [
{ name: 'pyttsx3', version: '2.90', systemDeps: ['espeak'] }
],
configSchema: z.object({
voice: z.enum(['male', 'female']).default('female'),
rate: z.number().min(50).max(300).default(200),
volume: z.number().min(0).max(1).default(0.9),
announcements: z.array(z.object({
message: z.string(),
trigger: z.string()
}))
}),
scaffold: `
import pyttsx3
import time
import threading
from datetime import datetime
from vibe_bridge import get_bridge
bridge = get_bridge()
class VoiceInterface:
def __init__(self):
self.engine = pyttsx3.init()
self.setup_voice()
self.is_running = True
def setup_voice(self):
"""Configure voice settings"""
voices = self.engine.getProperty('voices')
# Select voice based on preference
voice_type = "\${voice}"
for voice in voices:
if voice_type == 'female' and 'female' in voice.name.lower():
self.engine.setProperty('voice', voice.id)
break
elif voice_type == 'male' and 'male' in voice.name.lower():
self.engine.setProperty('voice', voice.id)
break
# Set rate and volume
self.engine.setProperty('rate', \${rate})
self.engine.setProperty('volume', \${volume})
bridge.notify_ui(f"Voice configured: {voice_type}, rate: \${rate}", "success")
def speak(self, text):
"""Convert text to speech"""
try:
self.engine.say(text)
self.engine.runAndWait()
bridge.set_state("last_spoken", {
"text": text,
"timestamp": datetime.now().isoformat()
})
except Exception as e:
bridge.notify_ui(f"Speech error: {str(e)}", "error")
def announce_time(self):
"""Announce current time"""
current_time = datetime.now().strftime("%I:%M %p")
self.speak(f"The current time is {current_time}")
def process_announcements(self):
"""Process scheduled announcements"""
announcements = \${JSON.stringify(announcements)}
for announcement in announcements:
# Simple trigger processing (could be enhanced)
trigger = announcement['trigger']
message = announcement['message']
if trigger == 'hourly':
if datetime.now().minute == 0:
self.speak(message)
elif trigger == 'startup':
self.speak(message)
def listen_for_commands(self):
"""Listen for voice commands (simplified)"""
# In a full implementation, this would use speech recognition
# For now, we'll listen for state changes that trigger speech
while self.is_running:
# Check for speech requests from other scripts
speech_request = bridge.get_state("speech_request", None)
if speech_request:
self.speak(speech_request)
bridge.delete("speech_request")
# Check for time announcement requests
if bridge.get_state("announce_time", False):
self.announce_time()
bridge.delete("announce_time")
# Process scheduled announcements
self.process_announcements()
bridge.heartbeat()
time.sleep(10) # Check every 10 seconds
def main():
bridge.notify_ui("Voice interface starting", "info")
try:
voice_interface = VoiceInterface()
# Initial greeting
greeting = f"Voice interface activated at {datetime.now().strftime('%I:%M %p')}"
voice_interface.speak(greeting)
bridge.notify_ui(greeting, "success")
# Start listening for commands
voice_interface.listen_for_commands()
except KeyboardInterrupt:
bridge.notify_ui("Voice interface stopped", "info")
except Exception as e:
bridge.notify_ui(f"Voice interface error: {str(e)}", "error")
if __name__ == "__main__":
main()
`,
examples: [
{
name: 'Personal Assistant',
description: 'Voice assistant for notifications and updates',
config: {
voice: 'female',
rate: 180,
volume: 0.8,
announcements: [
{ message: 'Voice assistant is now active', trigger: 'startup' },
{ message: 'Hourly reminder to take a break', trigger: 'hourly' }
]
},
expectedOutput: 'Voice assistant will run continuously and make announcements'
}
]
};
// Export the registry instance
export const templateRegistry = new TemplateRegistry();
// =============================================
// VIBECODREHAB MAIN APPLICATION
// =============================================
import { EventEmitter } from 'events';
import { resolve } from 'path';
import {
ScriptDefinition,
ScriptId,
ProcessId,
ExecutionResult,
SystemEvent,
ScriptCategory
} from './domain-contracts';
import { ExecutionEngine } from './execution-engine';
import { templateRegistry, TemplateRegistry } from './library-templates';
// =============================================
// MAIN APPLICATION CLASS
// =============================================
export class VibeCodeRehab extends EventEmitter {
private readonly executionEngine: ExecutionEngine;
private readonly templateRegistry: TemplateRegistry;
private readonly scriptStorage: ScriptStorage;
private readonly config: VibeConfig;
private isInitialized = false;
constructor(config: VibeConfig) {
super();
this.config = config;
this.executionEngine = new ExecutionEngine({
python: {
runtimePath: resolve(config.pythonRuntimePath),
version: '3.11'
},
dependencyCache: new FileDependencyCache(config.cacheDirectory),
database: new SQLiteStateDatabase(config.databasePath)
});
this.templateRegistry = templateRegistry;
this.scriptStorage = new FileScriptStorage(config.scriptsDirectory);
}
async initialize(): Promise<void> {
if (this.isInitialized) return;
try {
this.emit('system.initializing');
// Initialize core components
await this.executionEngine.initialize();
await this.scriptStorage.initialize();
// Setup event forwarding
this.executionEngine.on('script.execution', (event) => this.emit('script.execution', event));
this.executionEngine.on('script.error', (event) => this.emit('script.error', event));
this.executionEngine.on('system.ready', () => this.emit('system.ready'));
this.isInitialized = true;
this.emit('system.initialized');
} catch (error) {
this.emit('system.error', { error: error.message });
throw error;
}
}
// =============================================
// SCRIPT MANAGEMENT API
// =============================================
async createScriptFromTemplate(
templateId: string,
config: unknown,
metadata: Partial<ScriptMetadata>
): Promise<ScriptDefinition> {
const template = this.templateRegistry.getTemplate(templateId);
if (!template) {
throw new Error(`Template not found: ${templateId}`);
}
// Generate script code from template
const code = this.templateRegistry.generateScript(templateId, config);
// Create script definition
const script: ScriptDefinition = {
id: this.generateScriptId(),
name: metadata.name || template.name,
category: template.category,
code,
dependencies: template.dependencies,
config: {
timeout: 300000, // 5 minutes default
memory: 512, // 512MB default
restart: false,
...this.extractScriptConfig(config)
},
metadata: {
author: metadata.author || 'User',
description: metadata.description || template.description,
tags: metadata.tags || [template.category],
created: Date.now() as Timestamp,
modified: Date.now() as Timestamp,
version: '1.0.0'
}
};
// Save script
await this.scriptStorage.save(script);
this.emit('script.created', { scriptId: script.id, templateId });
return script;
}
async getScript(scriptId: ScriptId): Promise<ScriptDefinition | undefined> {
return this.scriptStorage.get(scriptId);
}
async listScripts(category?: ScriptCategory): Promise<ScriptDefinition[]> {
const scripts = await this.scriptStorage.list();
return category ? scripts.filter(s => s.category === category) : scripts;
}
async updateScript(scriptId: ScriptId, updates: Partial<ScriptDefinition>): Promise<void> {
const script = await this.scriptStorage.get(scriptId);
if (!script) {
throw new Error(`Script not found: ${scriptId}`);
}
const updatedScript = {
...script,
...updates,
metadata: {
...script.metadata,
...updates.metadata,
modified: Date.now() as Timestamp
}
};
await this.scriptStorage.save(updatedScript);
this.emit('script.updated', { scriptId });
}
async deleteScript(scriptId: ScriptId): Promise<void> {
await this.scriptStorage.delete(scriptId);
this.emit('script.deleted', { scriptId });
}
// =============================================
// EXECUTION API
// =============================================
async executeScript(scriptId: ScriptId): Promise<ExecutionResult> {
const script = await this.scriptStorage.get(scriptId);
if (!script) {
throw new Error(`Script not found: ${scriptId}`);
}
return this.executionEngine.executeScript(script);
}
async scheduleScript(scriptId: ScriptId): Promise<void> {
const script = await this.scriptStorage.get(scriptId);
if (!script) {
throw new Error(`Script not found: ${scriptId}`);
}
if (script.category !== 'scheduled') {
throw new Error('Only scheduled scripts can be scheduled');
}
await this.executionEngine.scheduleScript(script);
this.emit('script.scheduled', { scriptId });
}
async startReactiveScript(scriptId: ScriptId): Promise<void> {
const script = await this.scriptStorage.get(scriptId);
if (!script) {
throw new Error(`Script not found: ${scriptId}`);
}
if (script.category !== 'reactive') {
throw new Error('Only reactive scripts can be started as reactive');
}
await this.executionEngine.startReactiveScript(script);
this.emit('script.reactive.started', { scriptId });
}
async getExecutionStatus(processId: ProcessId): Promise<ExecutionState> {
return this.executionEngine.getExecutionStatus(processId);
}
async getActiveExecutions(): Promise<ProcessHandle[]> {
return this.executionEngine.getActiveExecutions();
}
// =============================================
// TEMPLATE API
// =============================================
getTemplates(category?: ScriptCategory): ScriptTemplate[] {
return category
? this.templateRegistry.getTemplatesByCategory(category)
: this.templateRegistry.getAllTemplates();
}
getTemplate(templateId: string): ScriptTemplate | undefined {
return this.templateRegistry.getTemplate(templateId);
}
// =============================================
// UTILITY METHODS
// =============================================
private generateScriptId(): ScriptId {
return `script_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` as ScriptId;
}
private extractScriptConfig(templateConfig: unknown): Partial<ScriptConfig> {
// Extract runtime configuration from template config
const config: any = templateConfig;
return {
schedule: config.schedule ? {
type: 'interval',
expression: `${config.interval} ${config.unit}`
} : undefined,
watchPaths: config.watchPath ? [config.watchPath] : undefined
};
}
async shutdown(): Promise<void> {
this.emit('system.shutting_down');
// Cleanup resources
this.removeAllListeners();
this.emit('system.shutdown');
}
}
// =============================================
// STORAGE IMPLEMENTATIONS
// =============================================
interface ScriptStorage {
initialize(): Promise<void>;
save(script: ScriptDefinition): Promise<void>;
get(scriptId: ScriptId): Promise<ScriptDefinition | undefined>;
list(): Promise<ScriptDefinition[]>;
delete(scriptId: ScriptId): Promise<void>;
}
class FileScriptStorage implements ScriptStorage {
constructor(private readonly directory: string) {}
async initialize(): Promise<void> {
const fs = require('fs/promises');
try {
await fs.access(this.directory);
} catch {
await fs.mkdir(this.directory, { recursive: true });
}
}
async save(script: ScriptDefinition): Promise<void> {
const fs = require('fs/promises');
const filePath = resolve(this.directory, `${script.id}.json`);
await fs.writeFile(filePath, JSON.stringify(script, null, 2));
}
async get(scriptId: ScriptId): Promise<ScriptDefinition | undefined> {
const fs = require('fs/promises');
const filePath = resolve(this.directory, `${scriptId}.json`);
try {
const content = await fs.readFile(filePath, 'utf-8');
return JSON.parse(content);
} catch {
return undefined;
}
}
async list(): Promise<ScriptDefinition[]> {
const fs = require('fs/promises');
const files = await fs.readdir(this.directory);
const scripts: ScriptDefinition[] = [];
for (const file of files) {
if (file.endsWith('.json')) {
const scriptId = file.replace('.json', '') as ScriptId;
const script = await this.get(scriptId);
if (script) scripts.push(script);
}
}
return scripts;
}
async delete(scriptId: ScriptId): Promise<void> {
const fs = require('fs/promises');
const filePath = resolve(this.directory, `${scriptId}.json`);
await fs.unlink(filePath);
}
}
// =============================================
// CONFIGURATION
// =============================================
export interface VibeConfig {
pythonRuntimePath: string;
scriptsDirectory: string;
cacheDirectory: string;
databasePath: string;
logLevel: 'debug' | 'info' | 'warn' | 'error';
}
export const defaultConfig: VibeConfig = {
pythonRuntimePath: './runtime/python',
scriptsDirectory: './data/scripts',
cacheDirectory: './data/cache',
databasePath: './data/state.db',
logLevel: 'info'
};
// =============================================
// USAGE EXAMPLE
// =============================================
export async function exampleUsage() {
// Initialize VibeCodeRehab
const vibe = new VibeCodeRehab(defaultConfig);
// Setup event listeners
vibe.on('system.ready', () => {
console.log('✅ VibeCodeRehab is ready!');
});
vibe.on('script.execution', (event: SystemEvent) => {
console.log(`🏃 Script execution: ${event.scriptId} - ${event.state}`);
});
vibe.on('script.error', (event: SystemEvent) => {
console.log(`❌ Script error: ${event.scriptId} - ${event.metadata?.error}`);
});
// Initialize the system
await vibe.initialize();
// Example 1: Create a scheduled email automation
const emailScript = await vibe.createScriptFromTemplate('email-automation', {
smtpServer: 'smtp.gmail.com',
smtpPort: 587,
username: '[email protected]',
password: 'app_password',
recipients: ['[email protected]'],
subject: 'Daily System Status',
schedule: '09:00',
reportType: 'daily'
}, {
name: 'Daily Status Email',
description: 'Send daily status reports to the team',
tags: ['email', 'automation', 'daily']
});
// Schedule the email script
await vibe.scheduleScript(emailScript.id);
console.log(`📧 Scheduled email automation: ${emailScript.id}`);
// Example 2: Create a file watcher
const fileWatcherScript = await vibe.createScriptFromTemplate('file-watcher', {
watchPath: '/home/user/Downloads',
fileTypes: ['.pdf', '.doc', '.docx'],
actionType: 'move',
targetPath: '/home/user/Documents/Organized',
recursive: false
}, {
name: 'Document Organizer',
description: 'Automatically organize downloaded documents',
tags: ['file-management', 'automation']
});
// Start the reactive file watcher
await vibe.startReactiveScript(fileWatcherScript.id);
console.log(`📁 Started file watcher: ${fileWatcherScript.id}`);
// Example 3: Create an OCR processing script
const ocrScript = await vibe.createScriptFromTemplate('ocr-processing', {
inputPath: '/home/user/scanned-docs',
outputPath: '/home/user/extracted-text',
language: 'eng',
confidence: 60,
preprocessImage: true
}, {
name: 'Document OCR Processor',
description: 'Extract text from scanned documents',
tags: ['ocr', 'text-extraction']
});
// Execute the OCR script (interactive)
const result = await vibe.executeScript(ocrScript.id);
console.log(`🔍 OCR processing result: ${result.status}`);
// Example 4: List all scripts by category
const scheduledScripts = await vibe.listScripts('scheduled');
const reactiveScripts = await vibe.listScripts('reactive');
const interactiveScripts = await vibe.listScripts('interactive');
console.log(`\n📊 Script Summary:`);
console.log(`Scheduled: ${scheduledScripts.length}`);
console.log(`Reactive: ${reactiveScripts.length}`);
console.log(`Interactive: ${interactiveScripts.length}`);
// Example 5: Monitor active executions
setInterval(async () => {
const activeExecutions = await vibe.getActiveExecutions();
if (activeExecutions.length > 0) {
console.log(`🏃 Active executions: ${activeExecutions.length}`);
for (const exec of activeExecutions) {
console.log(` - ${exec.scriptId}: ${exec.status}`);
}
}
}, 30000); // Check every 30 seconds
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('\n🛑 Shutting down VibeCodeRehab...');
await vibe.shutdown();
process.exit(0);
});
return vibe;
}
// =============================================
// APPLICATION ENTRY POINT
// =============================================
if (require.main === module) {
exampleUsage().catch(console.error);
}
export default VibeCodeRehab;
he user wants me to continue with the VibeCodeRehab application development. I've created the core architecture with:
Domain contracts
Python runtime foundation
Execution engine with state management
Library template system
Main application orchestrator
Now I should continue with the missing pieces to make this production-ready:
Testing strategy and implementation
UI/Frontend architecture (TypeScript React)
Error handling and recovery
Performance monitoring
Deployment and packaging
Documentation
Let me continue with the testing strategy first, then move to the UI architecture.
// =============================================
// COMPREHENSIVE TESTING STRATEGY
// =============================================
import { describe, test, expect, beforeEach, afterEach, jest } from '@jest/globals';
import {
ScriptDefinition,
ExecutionContext,
ProcessHandle,
ScriptId,
ProcessId,
ExecutionState
} from '../src/domain-contracts';
import { VibeCodeRehab } from '../src/main-application';
import { ExecutionEngine } from '../src/execution-engine';
import { templateRegistry } from '../src/library-templates';
// =============================================
// CONTRACT TESTING - DOMAIN MODELS
// =============================================
describe('Domain Contract Validation', () => {
test('ScriptDefinition contract enforces required fields', () => {
const validScript: ScriptDefinition = {
id: 'test-script' as ScriptId,
name: 'Test Script',
category: 'interactive',
code: 'print("hello world")',
dependencies: [],
config: {},
metadata: {
author: 'Test',
description: 'Test script',
tags: ['test'],
created: Date.now() as Timestamp,
modified: Date.now() as Timestamp,
version: '1.0.0'
}
};
expect(validScript.id).toBeDefined();
expect(validScript.category).toMatch(/^(scheduled|reactive|interactive|persistent)$/);
expect(validScript.code.length).toBeGreaterThan(0);
});
test('ExecutionState transitions are valid', () => {
const validStates: ExecutionState[] = [
'pending', 'starting', 'running', 'paused',
'completed', 'failed', 'terminated', 'recovering'
];
const validTransitions = new Map([
['pending', ['starting', 'terminated']],
['starting', ['running', 'failed', 'terminated']],
['running', ['paused', 'completed', 'failed', 'terminated']],
['paused', ['running', 'terminated']],
['completed', []],
['failed', ['recovering', 'terminated']],
['terminated', []],
['recovering', ['running', 'failed', 'terminated']]
]);
for (const [fromState, toStates] of validTransitions) {
expect(validStates).toContain(fromState);
toStates.forEach(toState => {
expect(validStates).toContain(toState);
});
}
});
});
// =============================================
// UNIT TESTING - CORE COMPONENTS
// =============================================
describe('PythonRuntime Unit Tests', () => {
let mockPythonRuntime: any;
beforeEach(() => {
mockPythonRuntime = {
initialize: jest.fn(),
executeScript: jest.fn(),
getProcess: jest.fn(),
terminate: jest.fn()
};
});
test('should initialize embedded Python interpreter', async () => {
mockPythonRuntime.initialize.mockResolvedValue(undefined);
await mockPythonRuntime.initialize();
expect(mockPythonRuntime.initialize).toHaveBeenCalledTimes(1);
});
test('should handle Python execution errors gracefully', async () => {
const badScript: ScriptDefinition = createTestScript({
code: 'import non_existent_module'
});
mockPythonRuntime.executeScript.mockRejectedValue(
new Error('ModuleNotFoundError: No module named \'non_existent_module\'')
);
await expect(mockPythonRuntime.executeScript(badScript))
.rejects.toThrow('ModuleNotFoundError');
});
test('should isolate script processes', async () => {
const script1 = createTestScript({ code: 'import sys; sys.exit(1)' });
const script2 = createTestScript({ code: 'print("success")' });
mockPythonRuntime.executeScript
.mockResolvedValueOnce({ status: 'failed', exitCode: 1 })
.mockResolvedValueOnce({ status: 'completed', exitCode: 0 });
const result1 = await mockPythonRuntime.executeScript(script1);
const result2 = await mockPythonRuntime.executeScript(script2);
expect(result1.status).toBe('failed');
expect(result2.status).toBe('completed');
});
});
describe('StateEngine Unit Tests', () => {
let stateEngine: any;
let mockDatabase: any;
beforeEach(() => {
mockDatabase = {
logEvent: jest.fn(),
getEvents: jest.fn(),
saveSnapshot: jest.fn(),
getLastSnapshot: jest.fn()
};
stateEngine = {
createStatePod: jest.fn(),
getLastSnapshot: jest.fn()
};
});
test('should persist state changes', async () => {
const scriptId = 'test-script' as ScriptId;
const mockStatePod = {
set: jest.fn(),
get: jest.fn(),
createSnapshot: jest.fn()
};
stateEngine.createStatePod.mockResolvedValue(mockStatePod);
mockStatePod.set.mockResolvedValue(undefined);
mockStatePod.get.mockResolvedValue('test-value');
const statePod = await stateEngine.createStatePod(scriptId);
await statePod.set('test-key', 'test-value');
const value = await statePod.get('test-key');
expect(mockStatePod.set).toHaveBeenCalledWith('test-key', 'test-value');
expect(value).toBe('test-value');
});
test('should create snapshots for recovery', async () => {
const scriptId = 'test-script' as ScriptId;
const mockSnapshot = {
scriptId,
timestamp: Date.now() as Timestamp,
state: { 'key1': 'value1', 'key2': 'value2' },
checksum: 'abc123'
};
stateEngine.getLastSnapshot.mockResolvedValue(mockSnapshot);
const snapshot = await stateEngine.getLastSnapshot(scriptId);
expect(snapshot).toBeDefined();
expect(snapshot.scriptId).toBe(scriptId);
expect(snapshot.state).toHaveProperty('key1', 'value1');
});
test('should handle concurrent state modifications', async () => {
const mockStatePod = {
set: jest.fn(),
get: jest.fn(),
subscribers: new Map()
};
// Simulate concurrent modifications
const promises = [
mockStatePod.set('counter', 1),
mockStatePod.set('counter', 2),
mockStatePod.set('counter', 3)
];
mockStatePod.set.mockResolvedValue(undefined);
await Promise.all(promises);
expect(mockStatePod.set).toHaveBeenCalledTimes(3);
});
});
describe('TemplateRegistry Unit Tests', () => {
test('should register and retrieve templates', () => {
const template = templateRegistry.getTemplate('schedule-automation');
expect(template).toBeDefined();
expect(template?.category).toBe('scheduled');
expect(template?.dependencies).toContain(
expect.objectContaining({ name: 'schedule' })
);
});
test('should generate valid scripts from templates', () => {
const config = {
interval: 5,
unit: 'minutes',
taskName: 'test_task',
notifyOnComplete: true
};
const script = templateRegistry.generateScript('schedule-automation', config);
expect(script).toContain('test_task_task()');
expect(script).toContain('every(5).minutes');
expect(script).toContain('import schedule');
});
test('should validate template configuration schemas', () => {
const template = templateRegistry.getTemplate('schedule-automation');
const validConfig = {
interval: 10,
unit: 'hours',
taskName: 'backup',
notifyOnComplete: false
};
const invalidConfig = {
interval: -1, // Invalid: negative interval
unit: 'invalid', // Invalid: unknown unit
taskName: '', // Invalid: empty task name
};
expect(() => template?.configSchema.parse(validConfig)).not.toThrow();
expect(() => template?.configSchema.parse(invalidConfig)).toThrow();
});
test('should filter templates by category', () => {
const scheduledTemplates = templateRegistry.getTemplatesByCategory('scheduled');
const reactiveTemplates = templateRegistry.getTemplatesByCategory('reactive');
expect(scheduledTemplates.length).toBeGreaterThan(0);
expect(reactiveTemplates.length).toBeGreaterThan(0);
scheduledTemplates.forEach(template => {
expect(template.category).toBe('scheduled');
});
reactiveTemplates.forEach(template => {
expect(template.category).toBe('reactive');
});
});
});
// =============================================
// INTEGRATION TESTING
// =============================================
describe('End-to-End Integration Tests', () => {
let vibe: VibeCodeRehab;
let testConfig: VibeConfig;
beforeEach(async () => {
testConfig = {
pythonRuntimePath: './test/runtime/python',
scriptsDirectory: './test/data/scripts',
cacheDirectory: './test/data/cache',
databasePath: './test/data/test.db',
logLevel: 'debug'
};
vibe = new VibeCodeRehab(testConfig);
await vibe.initialize();
});
afterEach(async () => {
await vibe.shutdown();
// Cleanup test files
await cleanupTestEnvironment(testConfig);
});
test('should create, execute, and monitor script lifecycle', async () => {
// Create script from template
const script = await vibe.createScriptFromTemplate('schedule-automation', {
interval: 1,
unit: 'minutes',
taskName: 'test_job',
notifyOnComplete: true
}, {
name: 'Test Integration Script',
description: 'End-to-end test script'
});
expect(script.id).toBeDefined();
expect(script.category).toBe('scheduled');
// Execute script
const executionResult = await vibe.executeScript(script.id);
expect(executionResult.scriptId).toBe(script.id);
expect(['completed', 'running']).toContain(executionResult.status);
// Monitor execution
const activeExecutions = await vibe.getActiveExecutions();
const thisExecution = activeExecutions.find(
exec => exec.scriptId === script.id
);
if (thisExecution) {
expect(thisExecution.status).toMatch(/^(running|completed)$/);
}
}, 30000); // 30 second timeout for integration test
test('should handle script failure and recovery', async () => {
// Create a script that will fail
const failingScript = await vibe.createScriptFromTemplate('schedule-automation', {
interval: 1,
unit: 'minutes',
taskName: 'failing_task',
notifyOnComplete: true
}, {
name: 'Failing Test Script',
description: 'Script designed to fail for testing'
});
// Modify script to include failing code
await vibe.updateScript(failingScript.id, {
code: failingScript.code + '\nraise Exception("Intentional test failure")'
});
// Execute and expect failure
const result = await vibe.executeScript(failingScript.id);
expect(result.status).toBe('failed');
expect(result.exitCode).not.toBe(0);
});
test('should persist state across script restarts', async () => {
// Create a script that sets state
const script = await vibe.createScriptFromTemplate('schedule-automation', {
interval: 1,
unit: 'minutes',
taskName: 'state_test',
notifyOnComplete: true
}, {
name: 'State Persistence Test',
description: 'Test state persistence'
});
// Modify script to set state
const stateTestCode = `
from vibe_bridge import get_bridge
bridge = get_bridge()
# Set some state
bridge.set_state("test_counter", 42)
bridge.set_state("test_message", "Hello from script")
print("State set successfully")
`;
await vibe.updateScript(script.id, { code: stateTestCode });
// Execute script
await vibe.executeScript(script.id);
// Create a new script that reads the state
const readerScript = await vibe.createScriptFromTemplate('schedule-automation', {
interval: 1,
unit: 'minutes',
taskName: 'state_reader',
notifyOnComplete: true
}, {
name: 'State Reader Test',
description: 'Read persisted state'
});
const readerCode = `
from vibe_bridge import get_bridge
bridge = get_bridge()
# Read state from other script
counter = bridge.get_state("test_counter", 0)
message = bridge.get_state("test_message", "")
print(f"Counter: {counter}")
print(f"Message: {message}")
assert counter == 42, f"Expected 42, got {counter}"
assert message == "Hello from script", f"Expected message, got {message}"
print("State read successfully")
`;
await vibe.updateScript(readerScript.id, { code: readerCode });
// Execute reader script
const result = await vibe.executeScript(readerScript.id);
expect(result.status).toBe('completed');
expect(result.exitCode).toBe(0);
});
});
// =============================================
// PERFORMANCE TESTING
// =============================================
describe('Performance Tests', () => {
let vibe: VibeCodeRehab;
beforeEach(async () => {
vibe = new VibeCodeRehab({
pythonRuntimePath: './test/runtime/python',
scriptsDirectory: './test/data/scripts',
cacheDirectory: './test/data/cache',
databasePath: './test/data/perf.db',
logLevel: 'error' // Reduce logging for performance tests
});
await vibe.initialize();
});
afterEach(async () => {
await vibe.shutdown();
});
test('should handle concurrent script executions', async () => {
const startTime = Date.now();
const scriptPromises: Promise<any>[] = [];
// Create and execute 10 scripts concurrently
for (let i = 0; i < 10; i++) {
const promise = vibe.createScriptFromTemplate('schedule-automation', {
interval: 1,
unit: 'minutes',
taskName: `concurrent_task_${i}`,
notifyOnComplete: false
}, {
name: `Concurrent Script ${i}`,
description: 'Performance test script'
}).then(script => vibe.executeScript(script.id));
scriptPromises.push(promise);
}
const results = await Promise.all(scriptPromises);
const endTime = Date.now();
const totalTime = endTime - startTime;
// All scripts should complete
results.forEach(result => {
expect(['completed', 'running']).toContain(result.status);
});
// Should complete within reasonable time (adjust based on system)
expect(totalTime).toBeLessThan(30000); // 30 seconds
console.log(`Concurrent execution test completed in ${totalTime}ms`);
}, 60000);
test('should handle rapid state updates', async () => {
const script = await vibe.createScriptFromTemplate('schedule-automation', {
interval: 1,
unit: 'minutes',
taskName: 'rapid_state_test',
notifyOnComplete: false
}, {
name: 'Rapid State Updates',
description: 'Test rapid state modifications'
});
const rapidStateCode = `
from vibe_bridge import get_bridge
import time
bridge = get_bridge()
# Rapid state updates
start_time = time.time()
for i in range(1000):
bridge.set_state(f"rapid_key_{i}", f"value_{i}")
if i % 100 == 0:
bridge.notify_ui(f"Progress: {i}/1000", "info")
end_time = time.time()
total_time = end_time - start_time
bridge.set_state("total_updates", 1000)
bridge.set_state("update_time", total_time)
bridge.notify_ui(f"Completed 1000 updates in {total_time:.2f}s", "success")
print(f"Rapid state updates completed: {total_time:.2f}s")
`;
await vibe.updateScript(script.id, { code: rapidStateCode });
const startTime = Date.now();
const result = await vibe.executeScript(script.id);
const endTime = Date.now();
expect(result.status).toBe('completed');
expect(endTime - startTime).toBeLessThan(10000); // Should complete within 10 seconds
console.log(`Rapid state update test completed in ${endTime - startTime}ms`);
});
});
// =============================================
// STRESS TESTING
// =============================================
describe('Stress Tests', () => {
test('should handle memory-intensive scripts', async () => {
// Test script that uses significant memory
const memoryTestCode = `
import sys
from vibe_bridge import get_bridge
bridge = get_bridge()
# Allocate memory in chunks
data = []
chunk_size = 1024 * 1024 # 1MB chunks
max_chunks = 100 # 100MB total
for i in range(max_chunks):
chunk = 'x' * chunk_size
data.append(chunk)
if i % 10 == 0:
bridge.notify_ui(f"Allocated {i+1}MB", "info")
bridge.notify_ui(f"Memory test completed: {len(data)}MB allocated", "success")
print("Memory stress test completed")
`;
// This test would need proper memory monitoring
// Implementation depends on specific memory constraints
expect(true).toBe(true); // Placeholder
});
test('should handle long-running scripts', async () => {
// Test script that runs for extended period
const longRunningCode = `
import time
from vibe_bridge import get_bridge
bridge = get_bridge()
# Run for 5 minutes (adjust for testing)
duration = 5 # seconds for testing
start_time = time.time()
while time.time() - start_time < duration:
bridge.heartbeat()
bridge.notify_ui(f"Running for {int(time.time() - start_time)}s", "info")
time.sleep(1)
bridge.notify_ui("Long-running test completed", "success")
print("Long-running stress test completed")
`;
// This test would verify script can run for extended periods
// Implementation depends on specific requirements
expect(true).toBe(true); // Placeholder
});
});
// =============================================
// HELPER FUNCTIONS
// =============================================
function createTestScript(overrides: Partial<ScriptDefinition> = {}): ScriptDefinition {
return {
id: `test-${Date.now()}` as ScriptId,
name: 'Test Script',
category: 'interactive',
code: 'print("Hello, World!")',
dependencies: [],
config: {},
metadata: {
author: 'Test',
description: 'Test script',
tags: ['test'],
created: Date.now() as Timestamp,
modified: Date.now() as Timestamp,
version: '1.0.0'
},
...overrides
};
}
async function cleanupTestEnvironment(config: VibeConfig): Promise<void> {
const fs = require('fs/promises');
try {
// Remove test directories
await fs.rm(config.scriptsDirectory, { recursive: true, force: true });
await fs.rm(config.cacheDirectory, { recursive: true, force: true });
// Remove test database
try {
await fs.unlink(config.databasePath);
} catch {
// Database file might not exist
}
} catch (error) {
console.warn('Cleanup warning:', error);
}
}
// =============================================
// TEST CONFIGURATION
// =============================================
export const testConfig = {
// Jest configuration
testEnvironment: 'node',
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
testMatch: ['**/__tests__/**/*.test.ts'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/*.test.ts'
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
},
testTimeout: 30000, // 30 seconds for integration tests
maxWorkers: 4, // Limit concurrent test workers
};
// Test runner script would be:
// npm test -- --coverage --verbose
// =============================================
// FRONTEND ARCHITECTURE - TYPESCRIPT REACT
// =============================================
import React, {
createContext,
useContext,
useReducer,
useEffect,
useState,
useCallback
} from 'react';
import {
ScriptDefinition,
ProcessHandle,
ExecutionResult,
ScriptCategory,
ExecutionState,
ScriptTemplate,
SystemEvent
} from '../shared/domain-contracts';
// =============================================
// STATE MANAGEMENT - CONTEXT + REDUCER
// =============================================
interface AppState {
scripts: ScriptDefinition[];
activeExecutions: ProcessHandle[];
templates: ScriptTemplate[];
systemStatus: 'initializing' | 'ready' | 'error';
notifications: NotificationMessage[];
selectedScript: ScriptDefinition | null;
executionHistory: ExecutionResult[];
}
interface NotificationMessage {
id: string;
level: 'info' | 'success' | 'warning' | 'error';
message: string;
timestamp: number;
scriptId?: string;
}
type AppAction =
| { type: 'SYSTEM_READY' }
| { type: 'SYSTEM_ERROR'; error: string }
| { type: 'SCRIPTS_LOADED'; scripts: ScriptDefinition[] }
| { type: 'SCRIPT_CREATED'; script: ScriptDefinition }
| { type: 'SCRIPT_UPDATED'; script: ScriptDefinition }
| { type: 'SCRIPT_DELETED'; scriptId: string }
| { type: 'EXECUTIONS_UPDATED'; executions: ProcessHandle[] }
| { type: 'EXECUTION_COMPLETED'; result: ExecutionResult }
| { type: 'NOTIFICATION_ADDED'; notification: NotificationMessage }
| { type: 'NOTIFICATION_REMOVED'; notificationId: string }
| { type: 'SCRIPT_SELECTED'; script: ScriptDefinition | null }
| { type: 'TEMPLATES_LOADED'; templates: ScriptTemplate[] };
const initialState: AppState = {
scripts: [],
activeExecutions: [],
templates: [],
systemStatus: 'initializing',
notifications: [],
selectedScript: null,
executionHistory: []
};
function appReducer(state: AppState, action: AppAction): AppState {
switch (action.type) {
case 'SYSTEM_READY':
return { ...state, systemStatus: 'ready' };
case 'SYSTEM_ERROR':
return {
...state,
systemStatus: 'error',
notifications: [...state.notifications, {
id: Date.now().toString(),
level: 'error',
message: action.error,
timestamp: Date.now()
}]
};
case 'SCRIPTS_LOADED':
return { ...state, scripts: action.scripts };
case 'SCRIPT_CREATED':
return {
...state,
scripts: [...state.scripts, action.script],
selectedScript: action.script
};
case 'SCRIPT_UPDATED':
return {
...state,
scripts: state.scripts.map(s =>
s.id === action.script.id ? action.script : s
),
selectedScript: state.selectedScript?.id === action.script.id
? action.script
: state.selectedScript
};
case 'SCRIPT_DELETED':
return {
...state,
scripts: state.scripts.filter(s => s.id !== action.scriptId),
selectedScript: state.selectedScript?.id === action.scriptId
? null
: state.selectedScript
};
case 'EXECUTIONS_UPDATED':
return { ...state, activeExecutions: action.executions };
case 'EXECUTION_COMPLETED':
return {
...state,
executionHistory: [action.result, ...state.executionHistory.slice(0, 99)], // Keep last 100
activeExecutions: state.activeExecutions.filter(
exec => exec.processId !== action.result.processId
)
};
case 'NOTIFICATION_ADDED':
return {
...state,
notifications: [action.notification, ...state.notifications.slice(0, 49)] // Keep last 50
};
case 'NOTIFICATION_REMOVED':
return {
...state,
notifications: state.notifications.filter(n => n.id !== action.notificationId)
};
case 'SCRIPT_SELECTED':
return { ...state, selectedScript: action.script };
case 'TEMPLATES_LOADED':
return { ...state, templates: action.templates };
default:
return state;
}
}
// =============================================
// CONTEXT PROVIDERS
// =============================================
interface AppContextValue {
state: AppState;
dispatch: React.Dispatch<AppAction>;
actions: AppActions;
}
const AppContext = createContext<AppContextValue | null>(null);
export function useAppContext(): AppContextValue {
const context = useContext(AppContext);
if (!context) {
throw new Error('useAppContext must be used within AppProvider');
}
return context;
}
// =============================================
// API CLIENT
// =============================================
class VibeAPIClient {
private baseUrl: string;
private eventSource: EventSource | null = null;
constructor(baseUrl = 'http://localhost:3001') {
this.baseUrl = baseUrl;
}
async getScripts(category?: ScriptCategory): Promise<ScriptDefinition[]> {
const url = category
? `${this.baseUrl}/api/scripts?category=${category}`
: `${this.baseUrl}/api/scripts`;
const response = await fetch(url);
if (!response.ok) throw new Error('Failed to fetch scripts');
return response.json();
}
async createScript(
templateId: string,
config: unknown,
metadata: Partial<ScriptMetadata>
): Promise<ScriptDefinition> {
const response = await fetch(`${this.baseUrl}/api/scripts`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ templateId, config, metadata })
});
if (!response.ok) throw new Error('Failed to create script');
return response.json();
}
async updateScript(scriptId: string, updates: Partial<ScriptDefinition>): Promise<void> {
const response = await fetch(`${this.baseUrl}/api/scripts/${scriptId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updates)
});
if (!response.ok) throw new Error('Failed to update script');
}
async deleteScript(scriptId: string): Promise<void> {
const response = await fetch(`${this.baseUrl}/api/scripts/${scriptId}`, {
method: 'DELETE'
});
if (!response.ok) throw new Error('Failed to delete script');
}
async executeScript(scriptId: string): Promise<ExecutionResult> {
const response = await fetch(`${this.baseUrl}/api/scripts/${scriptId}/execute`, {
method: 'POST'
});
if (!response.ok) throw new Error('Failed to execute script');
return response.json();
}
async scheduleScript(scriptId: string): Promise<void> {
const response = await fetch(`${this.baseUrl}/api/scripts/${scriptId}/schedule`, {
method: 'POST'
});
if (!response.ok) throw new Error('Failed to schedule script');
}
async getActiveExecutions(): Promise<ProcessHandle[]> {
const response = await fetch(`${this.baseUrl}/api/executions/active`);
if (!response.ok) throw new Error('Failed to fetch active executions');
return response.json();
}
async getTemplates(category?: ScriptCategory): Promise<ScriptTemplate[]> {
const url = category
? `${this.baseUrl}/api/templates?category=${category}`
: `${this.baseUrl}/api/templates`;
const response = await fetch(url);
if (!response.ok) throw new Error('Failed to fetch templates');
return response.json();
}
subscribeToEvents(onEvent: (event: SystemEvent) => void): () => void {
this.eventSource = new EventSource(`${this.baseUrl}/api/events`);
this.eventSource.onmessage = (event) => {
try {
const systemEvent = JSON.parse(event.data);
onEvent(systemEvent);
} catch (error) {
console.error('Failed to parse event:', error);
}
};
this.eventSource.onerror = (error) => {
console.error('EventSource error:', error);
};
return () => {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
};
}
}
// =============================================
// APP ACTIONS
// =============================================
interface AppActions {
loadScripts: (category?: ScriptCategory) => Promise<void>;
createScript: (templateId: string, config: unknown, metadata: Partial<ScriptMetadata>) => Promise<void>;
updateScript: (scriptId: string, updates: Partial<ScriptDefinition>) => Promise<void>;
deleteScript: (scriptId: string) => Promise<void>;
executeScript: (scriptId: string) => Promise<void>;
scheduleScript: (scriptId: string) => Promise<void>;
selectScript: (script: ScriptDefinition | null) => void;
loadTemplates: (category?: ScriptCategory) => Promise<void>;
removeNotification: (notificationId: string) => void;
}
function useAppActions(
dispatch: React.Dispatch<AppAction>,
apiClient: VibeAPIClient
): AppActions {
const addNotification = useCallback((
level: NotificationMessage['level'],
message: string,
scriptId?: string
) => {
dispatch({
type: 'NOTIFICATION_ADDED',
notification: {
id: Date.now().toString(),
level,
message,
timestamp: Date.now(),
scriptId
}
});
}, [dispatch]);
return {
loadScripts: useCallback(async (category?: ScriptCategory) => {
try {
const scripts = await apiClient.getScripts(category);
dispatch({ type: 'SCRIPTS_LOADED', scripts });
} catch (error) {
addNotification('error', `Failed to load scripts: ${error.message}`);
}
}, [apiClient, dispatch, addNotification]),
createScript: useCallback(async (templateId, config, metadata) => {
try {
const script = await apiClient.createScript(templateId, config, metadata);
dispatch({ type: 'SCRIPT_CREATED', script });
addNotification('success', `Script "${script.name}" created successfully`);
} catch (error) {
addNotification('error', `Failed to create script: ${error.message}`);
}
}, [apiClient, dispatch, addNotification]),
updateScript: useCallback(async (scriptId, updates) => {
try {
await apiClient.updateScript(scriptId, updates);
const scripts = await apiClient.getScripts();
const updatedScript = scripts.find(s => s.id === scriptId);
if (updatedScript) {
dispatch({ type: 'SCRIPT_UPDATED', script: updatedScript });
addNotification('success', 'Script updated successfully');
}
} catch (error) {
addNotification('error', `Failed to update script: ${error.message}`);
}
}, [apiClient, dispatch, addNotification]),
deleteScript: useCallback(async (scriptId) => {
try {
await apiClient.deleteScript(scriptId);
dispatch({ type: 'SCRIPT_DELETED', scriptId });
addNotification('success', 'Script deleted successfully');
} catch (error) {
addNotification('error', `Failed to delete script: ${error.message}`);
}
}, [apiClient, dispatch, addNotification]),
executeScript: useCallback(async (scriptId) => {
try {
const result = await apiClient.executeScript(scriptId);
addNotification('info', `Script execution started`, scriptId);
// Execution completion will be handled by event stream
} catch (error) {
addNotification('error', `Failed to execute script: ${error.message}`, scriptId);
}
}, [apiClient, addNotification]),
scheduleScript: useCallback(async (scriptId) => {
try {
await apiClient.scheduleScript(scriptId);
addNotification('success', 'Script scheduled successfully', scriptId);
} catch (error) {
addNotification('error', `Failed to schedule script: ${error.message}`, scriptId);
}
}, [apiClient, addNotification]),
selectScript: useCallback((script) => {
dispatch({ type: 'SCRIPT_SELECTED', script });
}, [dispatch]),
loadTemplates: useCallback(async (category?: ScriptCategory) => {
try {
const templates = await apiClient.getTemplates(category);
dispatch({ type: 'TEMPLATES_LOADED', templates });
} catch (error) {
addNotification('error', `Failed to load templates: ${error.message}`);
}
}, [apiClient, dispatch, addNotification]),
removeNotification: useCallback((notificationId) => {
dispatch({ type: 'NOTIFICATION_REMOVED', notificationId });
}, [dispatch])
};
}
// =============================================
// APP PROVIDER
// =============================================
interface AppProviderProps {
children: React.ReactNode;
apiClient?: VibeAPIClient;
}
export function AppProvider({ children, apiClient = new VibeAPIClient() }: AppProviderProps) {
const [state, dispatch] = useReducer(appReducer, initialState);
const actions = useAppActions(dispatch, apiClient);
// Initialize app
useEffect(() => {
const initializeApp = async () => {
try {
// Load initial data
await Promise.all([
actions.loadScripts(),
actions.loadTemplates()
]);
dispatch({ type: 'SYSTEM_READY' });
} catch (error) {
dispatch({ type: 'SYSTEM_ERROR', error: error.message });
}
};
initializeApp();
}, [actions]);
// Subscribe to real-time events
useEffect(() => {
const unsubscribe = apiClient.subscribeToEvents((event) => {
switch (event.type) {
case 'script.execution':
if (event.state === 'completed' || event.state === 'failed') {
// Refresh executions
apiClient.getActiveExecutions().then(executions => {
dispatch({ type: 'EXECUTIONS_UPDATED', executions });
});
}
break;
case 'state.change':
// Handle state changes if needed
break;
case 'system.health':
if (event.status !== 'healthy') {
dispatch({
type: 'NOTIFICATION_ADDED',
notification: {
id: Date.now().toString(),
level: event.status === 'unhealthy' ? 'error' : 'warning',
message: `System ${event.component}: ${event.message}`,
timestamp: event.timestamp
}
});
}
break;
}
});
return unsubscribe;
}, [apiClient]);
// Poll active executions
useEffect(() => {
const pollExecutions = () => {
apiClient.getActiveExecutions().then(executions => {
dispatch({ type: 'EXECUTIONS_UPDATED', executions });
}).catch(error => {
console.error('Failed to poll executions:', error);
});
};
// Poll every 5 seconds
const interval = setInterval(pollExecutions, 5000);
// Initial poll
pollExecutions();
return () => clearInterval(interval);
}, [apiClient]);
const contextValue: AppContextValue = {
state,
dispatch,
actions
};
return (
<AppContext.Provider value={contextValue}>
{children}
</AppContext.Provider>
);
}
// =============================================
// CORE COMPONENTS
// =============================================
// Dashboard Component
export function Dashboard() {
const { state } = useAppContext();
const stats = {
totalScripts: state.scripts.length,
activeExecutions: state.activeExecutions.length,
scheduledScripts: state.scripts.filter(s => s.category === 'scheduled').length,
recentExecutions: state.executionHistory.slice(0, 5)
};
return (
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<StatCard
title="Total Scripts"
value={stats.totalScripts}
icon="📝"
color="blue"
/>
<StatCard
title="Active Executions"
value={stats.activeExecutions}
icon="🏃"
color="green"
/>
<StatCard
title="Scheduled Scripts"
value={stats.scheduledScripts}
icon="⏰"
color="orange"
/>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<ActiveExecutionsPanel executions={state.activeExecutions} />
<RecentActivityPanel
executions={state.executionHistory}
notifications={state.notifications}
/>
</div>
</div>
);
}
// Script Library Component
export function ScriptLibrary() {
const { state, actions } = useAppContext();
const [selectedCategory, setSelectedCategory] = useState<ScriptCategory | 'all'>('all');
const [searchTerm, setSearchTerm] = useState('');
const filteredScripts = state.scripts.filter(script => {
const matchesCategory = selectedCategory === 'all' || script.category === selectedCategory;
const matchesSearch = script.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
script.metadata.description.toLowerCase().includes(searchTerm.toLowerCase());
return matchesCategory && matchesSearch;
});
return (
<div className="space-y-6">
<div className="flex flex-col md:flex-row md:items-center md:justify-between space-y-4 md:space-y-0">
<div className="flex items-center space-x-4">
<CategoryFilter
value={selectedCategory}
onChange={setSelectedCategory}
/>
<SearchInput
value={searchTerm}
onChange={setSearchTerm}
placeholder="Search scripts..."
/>
</div>
<CreateScriptButton />
</div>
<ScriptGrid
scripts={filteredScripts}
onScriptSelect={actions.selectScript}
onScriptExecute={actions.executeScript}
onScriptSchedule={actions.scheduleScript}
onScriptDelete={actions.deleteScript}
/>
</div>
);
}
// Script Editor Component
export function ScriptEditor() {
const { state, actions } = useAppContext();
const [isEditing, setIsEditing] = useState(false);
const [editedScript, setEditedScript] = useState<ScriptDefinition | null>(null);
useEffect(() => {
setEditedScript(state.selectedScript);
setIsEditing(false);
}, [state.selectedScript]);
const handleSave = async () => {
if (editedScript) {
await actions.updateScript(editedScript.id, editedScript);
setIsEditing(false);
}
};
if (!state.selectedScript) {
return (
<div className="flex items-center justify-center h-96">
<div className="text-center">
<div className="text-6xl mb-4">📝</div>
<h3 className="text-xl font-semibold mb-2">No Script Selected</h3>
<p className="text-gray-600">Select a script from the library to view or edit</p>
</div>
</div>
);
}
return (
<div className="space-y-6">
<ScriptHeader
script={state.selectedScript}
isEditing={isEditing}
onEdit={() => setIsEditing(true)}
onSave={handleSave}
onCancel={() => setIsEditing(false)}
onExecute={() => actions.executeScript(state.selectedScript!.id)}
/>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2">
<CodeEditor
script={editedScript || state.selectedScript}
isEditable={isEditing}
onChange={setEditedScript}
/>
</div>
<div className="space-y-6">
<ScriptMetadataPanel
script={editedScript || state.selectedScript}
isEditable={isEditing}
onChange={setEditedScript}
/>
<ScriptConfigPanel
script={editedScript || state.selectedScript}
isEditable={isEditing}
onChange={setEditedScript}
/>
</div>
</div>
</div>
);
}
// Template Gallery Component
export function TemplateGallery() {
const { state, actions } = useAppContext();
const [selectedCategory, setSelectedCategory] = useState<ScriptCategory | 'all'>('all');
const [selectedTemplate, setSelectedTemplate] = useState<ScriptTemplate | null>(null);
const filteredTemplates = state.templates.filter(template =>
selectedCategory === 'all' || template.category === selectedCategory
);
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-bold">Script Templates</h2>
<CategoryFilter
value={selectedCategory}
onChange={setSelectedCategory}
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredTemplates.map(template => (
<TemplateCard
key={template.id}
template={template}
onSelect={setSelectedTemplate}
/>
))}
</div>
{selectedTemplate && (
<TemplateConfigModal
template={selectedTemplate}
onClose={() => setSelectedTemplate(null)}
onCreate={actions.createScript}
/>
)}
</div>
);
}
// =============================================
// UTILITY COMPONENTS
// =============================================
interface StatCardProps {
title: string;
value: number;
icon: string;
color: 'blue' | 'green' | 'orange' | 'red';
}
function StatCard({ title, value, icon, color }: StatCardProps) {
const colorClasses = {
blue: 'bg-blue-500 border-blue-600',
green: 'bg-green-500 border-green-600',
orange: 'bg-orange-500 border-orange-600',
red: 'bg-red-500 border-red-600'
};
return (
<div className={`${colorClasses[color]} border-4 border-black shadow-brutal p-6 text-white`}>
<div className="flex items-center justify-between">
<div>
<p className="text-3xl font-black mb-2">{value}</p>
<p className="font-bold uppercase tracking-wider">{title}</p>
</div>
<div className="text-4xl">{icon}</div>
</div>
</div>
);
}
// Additional component implementations would follow...
// This provides the foundation for the complete frontend architecture
// =============================================
// ERROR HANDLING & MONITORING SYSTEM
// =============================================
import { EventEmitter } from 'events';
import { createWriteStream, WriteStream } from 'fs';
import { join } from 'path';
import {
VibeCodeError,
ScriptId,
ProcessId,
SystemEvent,
ExecutionState,
Timestamp
} from './domain-contracts';
// =============================================
// ERROR CLASSIFICATION SYSTEM
// =============================================
export enum ErrorSeverity {
LOW = 'low',
MEDIUM = 'medium',
HIGH = 'high',
CRITICAL = 'critical'
}
export enum ErrorCategory {
USER_ERROR = 'user_error', // Script syntax, config errors
SYSTEM_ERROR = 'system_error', // Runtime, memory, disk issues
DEPENDENCY_ERROR = 'dependency_error', // Missing libraries, system deps
NETWORK_ERROR = 'network_error', // API calls, web scraping failures
PERMISSION_ERROR = 'permission_error', // File access, system permissions
TIMEOUT_ERROR = 'timeout_error', // Script execution timeouts
RECOVERY_ERROR = 'recovery_error' // Crash recovery failures
}
interface ErrorContext {
readonly errorId: string;
readonly timestamp: Timestamp;
readonly severity: ErrorSeverity;
readonly category: ErrorCategory;
readonly message: string;
readonly stack?: string;
readonly scriptId?: ScriptId;
readonly processId?: ProcessId;
readonly metadata: Record<string, unknown>;
readonly recoverable: boolean;
readonly retryCount: number;
}
// =============================================
// ERROR RECOVERY STRATEGIES
// =============================================
interface RecoveryStrategy {
canRecover(error: ErrorContext): boolean;
recover(error: ErrorContext): Promise<RecoveryResult>;
getPriority(): number;
}
interface RecoveryResult {
success: boolean;
message: string;
nextRetryDelay?: number;
preventFutureRetries?: boolean;
}
class DependencyRecoveryStrategy implements RecoveryStrategy {
canRecover(error: ErrorContext): boolean {
return error.category === ErrorCategory.DEPENDENCY_ERROR
&& error.retryCount < 3;
}
async recover(error: ErrorContext): Promise<RecoveryResult> {
try {
// Attempt to reinstall missing dependencies
const missingDep = error.metadata.dependency as string;
if (missingDep) {
// Trigger dependency reinstallation
await this.reinstallDependency(missingDep);
return {
success: true,
message: `Successfully reinstalled dependency: ${missingDep}`,
nextRetryDelay: 5000 // 5 seconds
};
}
return {
success: false,
message: 'Could not identify missing dependency'
};
} catch (recoveryError) {
return {
success: false,
message: `Recovery failed: ${recoveryError.message}`,
preventFutureRetries: true
};
}
}
getPriority(): number {
return 100; // High priority
}
private async reinstallDependency(dependency: string): Promise<void> {
// Implementation would trigger dependency manager
console.log(`Reinstalling dependency: ${dependency}`);
}
}
class ResourceRecoveryStrategy implements RecoveryStrategy {
canRecover(error: ErrorContext): boolean {
return error.category === ErrorCategory.SYSTEM_ERROR
&& error.metadata.resourceType === 'memory'
&& error.retryCount < 2;
}
async recover(error: ErrorContext): Promise<RecoveryResult> {
try {
// Clear caches and reduce memory usage
await this.freeMemoryResources();
// Reduce script resource limits
const newMemoryLimit = (error.metadata.memoryLimit as number) * 0.8;
return {
success: true,
message: `Reduced memory limit to ${newMemoryLimit}MB`,
nextRetryDelay: 10000 // 10 seconds
};
} catch (recoveryError) {
return {
success: false,
message: `Memory recovery failed: ${recoveryError.message}`
};
}
}
getPriority(): number {
return 80; // Medium-high priority
}
private async freeMemoryResources(): Promise<void> {
// Force garbage collection if available
if (global.gc) {
global.gc();
}
// Clear internal caches
// Implementation would clear various caches
}
}
class TimeoutRecoveryStrategy implements RecoveryStrategy {
canRecover(error: ErrorContext): boolean {
return error.category === ErrorCategory.TIMEOUT_ERROR
&& error.retryCount < 1;
}
async recover(error: ErrorContext): Promise<RecoveryResult> {
const currentTimeout = error.metadata.timeout as number;
const newTimeout = Math.min(currentTimeout * 1.5, 600000); // Max 10 minutes
return {
success: true,
message: `Increased timeout from ${currentTimeout}ms to ${newTimeout}ms`,
nextRetryDelay: 2000 // 2 seconds
};
}
getPriority(): number {
return 60; // Medium priority
}
}
// =============================================
// ERROR MONITORING & RECOVERY ENGINE
// =============================================
export class ErrorMonitoringEngine extends EventEmitter {
private readonly recoveryStrategies: RecoveryStrategy[];
private readonly errorHistory: Map<string, ErrorContext[]> = new Map();
private readonly recoveryAttempts: Map<string, number> = new Map();
private readonly logStream: WriteStream;
constructor(private readonly config: MonitoringConfig) {
super();
this.recoveryStrategies = [
new DependencyRecoveryStrategy(),
new ResourceRecoveryStrategy(),
new TimeoutRecoveryStrategy()
].sort((a, b) => b.getPriority() - a.getPriority());
this.logStream = createWriteStream(
join(config.logsDirectory, 'errors.log'),
{ flags: 'a' }
);
}
async handleError(
error: Error | VibeCodeError,
context: Partial<ErrorContext> = {}
): Promise<void> {
const errorContext = this.createErrorContext(error, context);
// Log the error
await this.logError(errorContext);
// Store in history
this.addToHistory(errorContext);
// Emit monitoring event
this.emit('error.detected', errorContext);
// Attempt recovery if applicable
if (errorContext.recoverable) {
await this.attemptRecovery(errorContext);
}
// Check for error patterns
this.analyzeErrorPatterns(errorContext);
}
private createErrorContext(
error: Error | VibeCodeError,
context: Partial<ErrorContext>
): ErrorContext {
const isVibeError = error instanceof VibeCodeError;
return {
errorId: this.generateErrorId(),
timestamp: Date.now() as Timestamp,
severity: this.determineSeverity(error, context),
category: this.categorizeError(error, context),
message: error.message,
stack: error.stack,
scriptId: context.scriptId,
processId: context.processId,
metadata: {
...context.metadata,
errorType: error.constructor.name,
...(isVibeError && { vibeErrorCode: error.code })
},
recoverable: isVibeError ? error.recoverable : this.isRecoverable(error, context),
retryCount: this.getRetryCount(context.scriptId, context.processId),
...context
};
}
private determineSeverity(
error: Error | VibeCodeError,
context: Partial<ErrorContext>
): ErrorSeverity {
// Critical: System-wide failures
if (error.message.includes('ENOSPC') || error.message.includes('out of memory')) {
return ErrorSeverity.CRITICAL;
}
// High: Process crashes, data corruption
if (error.message.includes('SEGFAULT') ||
error.message.includes('corruption') ||
context.metadata?.cascadeFailure) {
return ErrorSeverity.HIGH;
}
// Medium: Script failures, dependency issues
if (error instanceof VibeCodeError ||
error.message.includes('ModuleNotFoundError')) {
return ErrorSeverity.MEDIUM;
}
// Low: User configuration errors
return ErrorSeverity.LOW;
}
private categorizeError(
error: Error | VibeCodeError,
context: Partial<ErrorContext>
): ErrorCategory {
const message = error.message.toLowerCase();
if (message.includes('modulenotfound') ||
message.includes('import') ||
message.includes('dependency')) {
return ErrorCategory.DEPENDENCY_ERROR;
}
if (message.includes('timeout') ||
message.includes('timed out')) {
return ErrorCategory.TIMEOUT_ERROR;
}
if (message.includes('permission') ||
message.includes('access denied') ||
message.includes('eacces')) {
return ErrorCategory.PERMISSION_ERROR;
}
if (message.includes('network') ||
message.includes('connection') ||
message.includes('fetch')) {
return ErrorCategory.NETWORK_ERROR;
}
if (message.includes('memory') ||
message.includes('disk') ||
message.includes('enospc')) {
return ErrorCategory.SYSTEM_ERROR;
}
if (message.includes('syntax') ||
message.includes('invalid') ||
context.metadata?.userGenerated) {
return ErrorCategory.USER_ERROR;
}
return ErrorCategory.SYSTEM_ERROR; // Default
}
private isRecoverable(error: Error, context: Partial<ErrorContext>): boolean {
const nonRecoverablePatterns = [
'SEGFAULT',
'out of memory',
'disk full',
'permission denied'
];
return !nonRecoverablePatterns.some(pattern =>
error.message.toLowerCase().includes(pattern.toLowerCase())
);
}
private async attemptRecovery(errorContext: ErrorContext): Promise<void> {
for (const strategy of this.recoveryStrategies) {
if (strategy.canRecover(errorContext)) {
try {
this.emit('recovery.attempting', {
errorId: errorContext.errorId,
strategy: strategy.constructor.name
});
const result = await strategy.recover(errorContext);
if (result.success) {
this.emit('recovery.success', {
errorId: errorContext.errorId,
strategy: strategy.constructor.name,
message: result.message
});
// Schedule retry if applicable
if (result.nextRetryDelay && errorContext.scriptId) {
setTimeout(() => {
this.emit('retry.scheduled', {
scriptId: errorContext.scriptId,
delay: result.nextRetryDelay
});
}, result.nextRetryDelay);
}
return; // Recovery successful, stop trying other strategies
} else {
this.emit('recovery.failed', {
errorId: errorContext.errorId,
strategy: strategy.constructor.name,
message: result.message
});
if (result.preventFutureRetries) {
break; // Stop all recovery attempts
}
}
} catch (recoveryError) {
this.emit('recovery.error', {
errorId: errorContext.errorId,
strategy: strategy.constructor.name,
error: recoveryError.message
});
}
}
}
}
private async logError(errorContext: ErrorContext): Promise<void> {
const logEntry = {
timestamp: new Date(errorContext.timestamp).toISOString(),
errorId: errorContext.errorId,
severity: errorContext.severity,
category: errorContext.category,
message: errorContext.message,
scriptId: errorContext.scriptId,
processId: errorContext.processId,
metadata: errorContext.metadata,
stack: errorContext.stack
};
this.logStream.write(JSON.stringify(logEntry) + '\n');
// Also log to console in development
if (this.config.environment === 'development') {
console.error('VibeCodeRehab Error:', logEntry);
}
}
private addToHistory(errorContext: ErrorContext): void {
const key = errorContext.scriptId || 'system';
if (!this.errorHistory.has(key)) {
this.errorHistory.set(key, []);
}
const history = this.errorHistory.get(key)!;
history.unshift(errorContext);
// Keep only last 100 errors per script
if (history.length > 100) {
history.splice(100);
}
}
private analyzeErrorPatterns(errorContext: ErrorContext): void {
const key = errorContext.scriptId || 'system';
const history = this.errorHistory.get(key) || [];
// Check for rapid failure pattern (5 failures in 5 minutes)
const recentErrors = history.filter(e =>
e.timestamp > Date.now() - 300000 // 5 minutes
);
if (recentErrors.length >= 5) {
this.emit('pattern.rapid_failure', {
scriptId: errorContext.scriptId,
errorCount: recentErrors.length,
timeWindow: '5m'
});
}
// Check for recurring error pattern (same error type 3+ times)
const sameTypeErrors = history.filter(e =>
e.category === errorContext.category &&
e.message === errorContext.message
).slice(0, 10); // Check last 10 errors
if (sameTypeErrors.length >= 3) {
this.emit('pattern.recurring_error', {
scriptId: errorContext.scriptId,
errorCategory: errorContext.category,
occurrences: sameTypeErrors.length
});
}
}
private generateErrorId(): string {
return `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
private getRetryCount(scriptId?: ScriptId, processId?: ProcessId): number {
const key = processId || scriptId || 'unknown';
return this.recoveryAttempts.get(key) || 0;
}
getErrorHistory(scriptId?: ScriptId): ErrorContext[] {
if (scriptId) {
return this.errorHistory.get(scriptId) || [];
}
// Return all errors
const allErrors: ErrorContext[] = [];
for (const errors of this.errorHistory.values()) {
allErrors.push(...errors);
}
return allErrors.sort((a, b) => b.timestamp - a.timestamp);
}
getErrorStats(): ErrorStats {
const allErrors = this.getErrorHistory();
const last24h = allErrors.filter(e =>
e.timestamp > Date.now() - 86400000 // 24 hours
);
const bySeverity = {
[ErrorSeverity.CRITICAL]: last24h.filter(e => e.severity === ErrorSeverity.CRITICAL).length,
[ErrorSeverity.HIGH]: last24h.filter(e => e.severity === ErrorSeverity.HIGH).length,
[ErrorSeverity.MEDIUM]: last24h.filter(e => e.severity === ErrorSeverity.MEDIUM).length,
[ErrorSeverity.LOW]: last24h.filter(e => e.severity === ErrorSeverity.LOW).length
};
const byCategory = Object.values(ErrorCategory).reduce((acc, category) => {
acc[category] = last24h.filter(e => e.category === category).length;
return acc;
}, {} as Record<ErrorCategory, number>);
const recoveryRate = allErrors.length > 0
? (allErrors.filter(e => e.recoverable).length / allErrors.length) * 100
: 100;
return {
totalErrors: allErrors.length,
last24h: last24h.length,
bySeverity,
byCategory,
recoveryRate: Math.round(recoveryRate * 100) / 100
};
}
async shutdown(): Promise<void> {
return new Promise((resolve) => {
this.logStream.end(() => {
this.removeAllListeners();
resolve();
});
});
}
}
// =============================================
// HEALTH MONITORING SYSTEM
// =============================================
export class HealthMonitor extends EventEmitter {
private healthChecks: Map<string, HealthCheck> = new Map();
private healthStatus: Map<string, HealthStatus> = new Map();
private monitoringInterval?: NodeJS.Timeout;
constructor(private readonly config: HealthConfig) {
super();
this.setupCoreHealthChecks();
}
private setupCoreHealthChecks(): void {
// System resource health check
this.addHealthCheck({
name: 'system_resources',
description: 'Monitor CPU, memory, and disk usage',
interval: 30000, // 30 seconds
check: async () => {
const usage = await this.getSystemResourceUsage();
if (usage.memory > 90 || usage.cpu > 95 || usage.disk > 95) {
return {
healthy: false,
message: `High resource usage: CPU ${usage.cpu}%, Memory ${usage.memory}%, Disk ${usage.disk}%`,
details: usage
};
}
return {
healthy: true,
message: `Resources normal: CPU ${usage.cpu}%, Memory ${usage.memory}%, Disk ${usage.disk}%`,
details: usage
};
}
});
// Python runtime health check
this.addHealthCheck({
name: 'python_runtime',
description: 'Verify Python interpreter is functional',
interval: 60000, // 1 minute
check: async () => {
try {
// Test Python interpreter
const testResult = await this.testPythonRuntime();
return {
healthy: testResult.success,
message: testResult.message,
details: testResult
};
} catch (error) {
return {
healthy: false,
message: `Python runtime check failed: ${error.message}`,
details: { error: error.message }
};
}
}
});
// Database health check
this.addHealthCheck({
name: 'database',
description: 'Check database connectivity and performance',
interval: 45000, // 45 seconds
check: async () => {
try {
const dbHealth = await this.checkDatabaseHealth();
return {
healthy: dbHealth.responsive && dbHealth.queryTime < 1000,
message: dbHealth.responsive
? `Database responsive (${dbHealth.queryTime}ms)`
: 'Database not responding',
details: dbHealth
};
} catch (error) {
return {
healthy: false,
message: `Database health check failed: ${error.message}`,
details: { error: error.message }
};
}
}
});
}
addHealthCheck(healthCheck: HealthCheck): void {
this.healthChecks.set(healthCheck.name, healthCheck);
this.healthStatus.set(healthCheck.name, {
name: healthCheck.name,
healthy: true,
lastCheck: Date.now() as Timestamp,
message: 'Health check added',
details: {}
});
}
start(): void {
if (this.monitoringInterval) {
return; // Already running
}
this.monitoringInterval = setInterval(async () => {
await this.runHealthChecks();
}, this.config.checkInterval);
// Run initial health checks
this.runHealthChecks();
this.emit('monitoring.started');
}
stop(): void {
if (this.monitoringInterval) {
clearInterval(this.monitoringInterval);
this.monitoringInterval = undefined;
}
this.emit('monitoring.stopped');
}
private async runHealthChecks(): Promise<void> {
const promises = Array.from(this.healthChecks.values()).map(async (check) => {
try {
const result = await check.check();
const status: HealthStatus = {
name: check.name,
healthy: result.healthy,
lastCheck: Date.now() as Timestamp,
message: result.message,
details: result.details
};
const previousStatus = this.healthStatus.get(check.name);
this.healthStatus.set(check.name, status);
// Emit status change events
if (!previousStatus || previousStatus.healthy !== status.healthy) {
this.emit('health.status_change', {
name: check.name,
healthy: status.healthy,
previouslyHealthy: previousStatus?.healthy,
message: status.message
});
}
} catch (error) {
const status: HealthStatus = {
name: check.name,
healthy: false,
lastCheck: Date.now() as Timestamp,
message: `Health check error: ${error.message}`,
details: { error: error.message }
};
this.healthStatus.set(check.name, status);
this.emit('health.check_error', {
name: check.name,
error: error.message
});
}
});
await Promise.all(promises);
}
getOverallHealth(): OverallHealth {
const statuses = Array.from(this.healthStatus.values());
const unhealthyChecks = statuses.filter(s => !s.healthy);
let status: 'healthy' | 'degraded' | 'unhealthy';
if (unhealthyChecks.length === 0) {
status = 'healthy';
} else if (unhealthyChecks.length <= statuses.length * 0.3) {
status = 'degraded';
} else {
status = 'unhealthy';
}
return {
status,
totalChecks: statuses.length,
healthyChecks: statuses.length - unhealthyChecks.length,
unhealthyChecks: unhealthyChecks.length,
lastUpdate: Math.max(...statuses.map(s => s.lastCheck)),
checks: Object.fromEntries(
statuses.map(s => [s.name, s])
)
};
}
private async getSystemResourceUsage(): Promise<SystemResourceUsage> {
// Implementation would use appropriate system monitoring library
// For now, returning mock data
return {
cpu: Math.random() * 100,
memory: Math.random() * 100,
disk: Math.random() * 100,
timestamp: Date.now()
};
}
private async testPythonRuntime(): Promise<PythonRuntimeTest> {
// Implementation would test Python interpreter
// For now, returning mock data
return {
success: true,
message: 'Python runtime operational',
version: '3.11.0',
testDuration: 150
};
}
private async checkDatabaseHealth(): Promise<DatabaseHealth> {
// Implementation would check database
// For now, returning mock data
return {
responsive: true,
queryTime: Math.random() * 500,
connectionCount: 5,
diskUsage: Math.random() * 100
};
}
}
// =============================================
// INTERFACES & TYPES
// =============================================
interface MonitoringConfig {
logsDirectory: string;
environment: 'development' | 'production';
retentionDays: number;
}
interface HealthConfig {
checkInterval: number;
alertThresholds: {
cpu: number;
memory: number;
disk: number;
};
}
interface HealthCheck {
name: string;
description: string;
interval: number;
check: () => Promise<HealthCheckResult>;
}
interface HealthCheckResult {
healthy: boolean;
message: string;
details: Record<string, unknown>;
}
interface HealthStatus {
name: string;
healthy: boolean;
lastCheck: Timestamp;
message: string;
details: Record<string, unknown>;
}
interface OverallHealth {
status: 'healthy' | 'degraded' | 'unhealthy';
totalChecks: number;
healthyChecks: number;
unhealthyChecks: number;
lastUpdate: Timestamp;
checks: Record<string, HealthStatus>;
}
interface ErrorStats {
totalErrors: number;
last24h: number;
bySeverity: Record<ErrorSeverity, number>;
byCategory: Record<ErrorCategory, number>;
recoveryRate: number;
}
interface SystemResourceUsage {
cpu: number;
memory: number;
disk: number;
timestamp: number;
}
interface PythonRuntimeTest {
success: boolean;
message: string;
version: string;
testDuration: number;
}
interface DatabaseHealth {
responsive: boolean;
queryTime: number;
connectionCount: number;
diskUsage: number;
}
# =============================================
# PRODUCTION DEPLOYMENT CONFIGURATION
# =============================================
# Multi-stage Dockerfile for VibeCodeRehab
# Stage 1: Build embedded Python runtime
FROM python:3.11-slim as python-builder
WORKDIR /python-build
# Install system dependencies for Python libraries
RUN apt-get update && apt-get install -y \
build-essential \
tesseract-ocr \
tesseract-ocr-eng \
espeak \
portaudio19-dev \
gcc \
g++ \
&& rm -rf /var/lib/apt/lists/*
# Create portable Python installation
RUN python -m venv /opt/vibe-python
ENV PATH="/opt/vibe-python/bin:$PATH"
# Install core Python dependencies
COPY requirements.txt .
RUN pip install --upgrade pip && \
pip install -r requirements.txt
# Stage 2: Build Node.js frontend
FROM node:18-alpine as frontend-builder
WORKDIR /app
# Copy package files
COPY package*.json ./
COPY tsconfig.json ./
COPY tailwind.config.js ./
COPY postcss.config.js ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY src/ ./src/
COPY public/ ./public/
# Build frontend
RUN npm run build
# Stage 3: Build TypeScript backend
FROM node:18-alpine as backend-builder
WORKDIR /app
# Copy package files
COPY package*.json ./
COPY tsconfig.json ./
# Install dependencies including dev dependencies for build
RUN npm ci
# Copy source code
COPY src/ ./src/
# Build backend
RUN npm run build:backend
# Stage 4: Production runtime
FROM ubuntu:22.04
LABEL maintainer="VibeCodeRehab Team"
LABEL version="1.0.0"
LABEL description="Python automation platform with embedded runtime"
# Install system dependencies
RUN apt-get update && apt-get install -y \
curl \
tesseract-ocr \
tesseract-ocr-eng \
espeak \
portaudio19-dev \
sqlite3 \
supervisor \
nginx \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# Install Node.js
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs
# Create application user
RUN groupadd -r vibe && useradd -r -g vibe vibe
# Set up application directories
WORKDIR /app
RUN mkdir -p /app/data/{scripts,cache,logs,state} \
&& mkdir -p /app/runtime \
&& chown -R vibe:vibe /app
# Copy embedded Python runtime
COPY --from=python-builder /opt/vibe-python /app/runtime/python
# Copy built backend
COPY --from=backend-builder /app/dist /app/dist
COPY --from=backend-builder /app/node_modules /app/node_modules
# Copy built frontend
COPY --from=frontend-builder /app/build /app/public
# Copy configuration files
COPY docker/nginx.conf /etc/nginx/nginx.conf
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY docker/entrypoint.sh /entrypoint.sh
# Copy application configuration
COPY config/ /app/config/
# Set permissions
RUN chmod +x /entrypoint.sh \
&& chown -R vibe:vibe /app
# Expose ports
EXPOSE 3000 3001
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Switch to application user
USER vibe
# Set environment variables
ENV NODE_ENV=production
ENV VIBE_CONFIG_PATH=/app/config/production.json
ENV PYTHONPATH=/app/runtime/python/lib/python3.11/site-packages
ENTRYPOINT ["/entrypoint.sh"]
CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
# =============================================
# DOCKER COMPOSE CONFIGURATION
# =============================================
---
# docker-compose.yml
version: '3.8'
services:
vibe-app:
build:
context: .
dockerfile: Dockerfile
container_name: vibe-codrehab
restart: unless-stopped
ports:
- "3000:3000"
- "3001:3001"
volumes:
- vibe-data:/app/data
- vibe-logs:/app/logs
- /var/run/docker.sock:/var/run/docker.sock:ro # For container management
environment:
- NODE_ENV=production
- VIBE_LOG_LEVEL=info
- VIBE_MAX_MEMORY=2048
- VIBE_MAX_SCRIPTS=100
labels:
- "traefik.enable=true"
- "traefik.http.routers.vibe.rule=Host(`vibe.yourdomain.com`)"
- "traefik.http.routers.vibe.tls=true"
- "traefik.http.routers.vibe.tls.certresolver=letsencrypt"
depends_on:
- redis
- postgres
networks:
- vibe-network
postgres:
image: postgres:15-alpine
container_name: vibe-postgres
restart: unless-stopped
environment:
POSTGRES_DB: vibecodrehab
POSTGRES_USER: vibe
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
- ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- vibe-network
redis:
image: redis:7-alpine
container_name: vibe-redis
restart: unless-stopped
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
volumes:
- redis-data:/data
networks:
- vibe-network
traefik:
image: traefik:v3.0
container_name: vibe-traefik
restart: unless-stopped
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.email=admin@yourdomain.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-ssl:/letsencrypt
networks:
- vibe-network
volumes:
vibe-data:
driver: local
vibe-logs:
driver: local
postgres-data:
driver: local
redis-data:
driver: local
traefik-ssl:
driver: local
networks:
vibe-network:
driver: bridge
# =============================================
# KUBERNETES DEPLOYMENT
# =============================================
---
# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: vibecodrehab
---
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: vibe-config
namespace: vibecodrehab
data:
production.json: |
{
"server": {
"port": 3000,
"host": "0.0.0.0"
},
"python": {
"runtimePath": "/app/runtime/python",
"maxProcesses": 50,
"timeout": 300000
},
"database": {
"type": "sqlite",
"path": "/app/data/state.db"
},
"monitoring": {
"enabled": true,
"logLevel": "info",
"healthCheckInterval": 30000
},
"security": {
"scriptSandbox": true,
"maxMemoryPerScript": 512,
"allowedNetworkAccess": false
}
}
---
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vibe-app
namespace: vibecodrehab
labels:
app: vibe-app
spec:
replicas: 2
selector:
matchLabels:
app: vibe-app
template:
metadata:
labels:
app: vibe-app
spec:
containers:
- name: vibe-app
image: vibecodrehab/app:latest
ports:
- containerPort: 3000
- containerPort: 3001
env:
- name: NODE_ENV
value: "production"
- name: VIBE_CONFIG_PATH
value: "/app/config/production.json"
volumeMounts:
- name: app-data
mountPath: /app/data
- name: config-volume
mountPath: /app/config
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: app-data
persistentVolumeClaim:
claimName: vibe-data-pvc
- name: config-volume
configMap:
name: vibe-config
---
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: vibe-service
namespace: vibecodrehab
spec:
selector:
app: vibe-app
ports:
- name: web
port: 80
targetPort: 3000
- name: api
port: 3001
targetPort: 3001
type: ClusterIP
---
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vibe-ingress
namespace: vibecodrehab
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- vibe.yourdomain.com
secretName: vibe-tls
rules:
- host: vibe.yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vibe-service
port:
number: 80
---
# k8s/pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: vibe-data-pvc
namespace: vibecodrehab
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
# =============================================
# ENVIRONMENT CONFIGURATION
# =============================================
# .env.production
NODE_ENV=production
VIBE_LOG_LEVEL=info
VIBE_CONFIG_PATH=/app/config/production.json
# Database
POSTGRES_PASSWORD=your-secure-postgres-password
REDIS_PASSWORD=your-secure-redis-password
# Security
JWT_SECRET=your-jwt-secret-key
ENCRYPTION_KEY=your-encryption-key
# Monitoring
SENTRY_DSN=your-sentry-dsn
METRICS_ENABLED=true
# Resource Limits
VIBE_MAX_MEMORY=2048
VIBE_MAX_SCRIPTS=100
VIBE_MAX_EXECUTION_TIME=600000
# Network
ALLOWED_ORIGINS=https://vibe.yourdomain.com
CORS_ENABLED=true
# =============================================
# BUILD SCRIPTS
# =============================================
#!/bin/bash
# scripts/build.sh
set -e
echo "🏗️ Building VibeCodeRehab for production..."
# Clean previous builds
echo "🧹 Cleaning previous builds..."
rm -rf dist/ build/
# Install dependencies
echo "📦 Installing dependencies..."
npm ci --only=production
# Build TypeScript backend
echo "🔧 Building backend..."
npm run build:backend
# Build React frontend
echo "⚛️ Building frontend..."
npm run build:frontend
# Create embedded Python runtime
echo "🐍 Preparing Python runtime..."
python3 -m venv runtime/python
source runtime/python/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
# Package for distribution
echo "📦 Creating distribution package..."
mkdir -p dist/
cp -r build/ dist/public/
cp -r runtime/ dist/
cp package.json dist/
cp -r node_modules/ dist/
echo "✅ Build completed successfully!"
#!/bin/bash
# scripts/deploy.sh
set -e
ENVIRONMENT=${1:-production}
VERSION=${2:-latest}
echo "🚀 Deploying VibeCodeRehab to $ENVIRONMENT..."
case $ENVIRONMENT in
"docker")
echo "🐳 Building Docker image..."
docker build -t vibecodrehab/app:$VERSION .
docker-compose up -d
;;
"kubernetes")
echo "☸️ Deploying to Kubernetes..."
kubectl apply -f k8s/
kubectl set image deployment/vibe-app vibe-app=vibecodrehab/app:$VERSION -n vibecodrehab
kubectl rollout status deployment/vibe-app -n vibecodrehab
;;
"production")
echo "🌐 Deploying to production server..."
./scripts/build.sh
# Upload to server
rsync -avz --delete dist/ user@production-server:/opt/vibecodrehab/
# Restart services
ssh user@production-server "sudo systemctl restart vibecodrehab"
;;
*)
echo "❌ Unknown environment: $ENVIRONMENT"
echo "Usage: $0 [docker|kubernetes|production] [version]"
exit 1
;;
esac
echo "✅ Deployment to $ENVIRONMENT completed!"
# =============================================
# MONITORING & LOGGING
# =============================================
# docker/nginx.conf
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /app/logs/nginx-access.log main;
error_log /app/logs/nginx-error.log;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml;
upstream backend {
server localhost:3001;
}
server {
listen 3000;
server_name _;
root /app/public;
index index.html;
# Frontend static files
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "public, max-age=31536000" always;
}
# API routes
location /api/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# WebSocket support for real-time events
location /api/events {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Health check
location /health {
proxy_pass http://backend/health;
}
}
}
# docker/supervisord.conf
[supervisord]
nodaemon=true
user=vibe
[program:nginx]
command=nginx -g "daemon off;"
autostart=true
autorestart=true
user=root
stdout_logfile=/app/logs/nginx-stdout.log
stderr_logfile=/app/logs/nginx-stderr.log
[program:vibe-backend]
command=node /app/dist/server.js
directory=/app
autostart=true
autorestart=true
user=vibe
environment=NODE_ENV="production",VIBE_CONFIG_PATH="/app/config/production.json"
stdout_logfile=/app/logs/backend-stdout.log
stderr_logfile=/app/logs/backend-stderr.log
# docker/entrypoint.sh
#!/bin/bash
set -e
# Initialize data directories
mkdir -p /app/data/{scripts,cache,logs,state}
mkdir -p /app/logs
# Initialize database if it doesn't exist
if [ ! -f /app/data/state.db ]; then
echo "Initializing database..."
node /app/dist/scripts/init-db.js
fi
# Set proper permissions
chown -R vibe:vibe /app/data /app/logs
# Start supervisord
exec "$@"
# =============================================
# CI/CD PIPELINE
# =============================================
# .github/workflows/deploy.yml
name: Deploy VibeCodeRehab
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
npm ci
pip install -r requirements.txt
- name: Run tests
run: |
npm run test:unit
npm run test:integration
npm run test:e2e
- name: Code coverage
run: npm run test:coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
vibecodrehab/app:latest
vibecodrehab/app:${{ github.sha }}
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Deploy to production
run: |
echo "Deploying to production..."
# Add your deployment commands here
Summary
I've completed the VibeCodeRehab architecture and provided a comprehensive foundation for production-ready Python automation platform.
What We've Built
Complete Architecture spanning:
Domain contracts with full TypeScript type safety
Embedded Python runtime with dependency management
Process isolation with IPC communication bridge
Event-sourced state management for crash recovery
8 automation library templates covering all major use cases
Production deployment with Docker, Kubernetes, and CI/CD
Error monitoring with automated recovery strategies
The Key Insight
Execution isolation is the primary constraint that solves Python automation reliability. Every design decision flows from this: embedded runtime eliminates dependency conflicts, process-per-script contains failures, event sourcing enables recovery.
Critical Path Forward
Week 1: Implement IPC bridge between TypeScript ↔ Python
Week 2-4: Build core runtime with process management
Week 5-6: Integrate template system
Week 7-12: Frontend, monitoring, and production hardening
The Leverage Point
The template system multiplies capability exponentially. Each new template adds automation patterns that compound user value. Users think in workflows, not technical implementation.
Result: A constraint-driven architecture that transforms unreliable Python scripting into a production automation platform.
The foundation is architected. Time to build the runtime.
User Question:
Questions -- Should the rest of this development take place in the IDE or a containerized environment vs claude.ai chat/artifacts? 1. Implement vibe_bridge.py Python module 2. Build TypeScript IPC message parser 3. Create basic process spawn and communication test Please explain the vibe_bride.py Python module and the TypeScript IPC message parser to me?
Great questions. Let me think through this systematically:
Development Environment Question: This is a critical decision point. The user is asking whether to continue development in a traditional IDE/containerized environment vs continuing in Claude.ai chat/artifacts.
For the next phase of development (implementing the core runtime), we need to consider:
Artifacts limitations: No real file system access, no process spawning, no actual Python execution
IDE advantages: Real debugging, actual Python execution, proper TypeScript compilation, git integration
Containerized advantages: Consistent environment, embedded Python setup, dependency management
The IPC bridge and process management are core system-level operations that need real execution environments. Artifacts are great for architecture and UI components, but not for the deep runtime work.
vibe_bridge.py explanation: This is the Python-side communication module that gets injected into every user script. It handles:
State persistence calls
UI notifications
Progress reporting
Heartbeat signals
Communication with the TypeScript runtime
TypeScript IPC parser: This parses structured messages from Python stdout to enable bidirectional communication between isolated Python processes and the main TypeScript application.
Let me explain these clearly and recommend the right development approach.
Development Environment Recommendation
Switch to IDE/Containerized Environment for the next phase.
Why:
IPC bridge requires real process spawning - can't test in browser sandbox
Python execution needs actual interpreter - artifacts can't run Python
File system operations - state persistence, script storage, logs
Debugging process communication - need real stdout/stderr streams
Recommended Setup:
bash
# Containerized development environment
docker-compose -f docker-compose.dev.yml up
Keep using Claude for:
Architecture decisions and system design
Code review and optimization suggestions
Complex debugging when you hit constraint issues
UI component development (React artifacts work great)
The vibe_bridge.py Python Module Explained
This is the communication lifeline between isolated Python scripts and the TypeScript runtime.
The Constraint It Solves
Python scripts run in isolated processes. They need to:
Store persistent state that survives crashes
Send progress updates to the UI
Report errors and status
Receive commands from the main application
How It Works
python
# vibe_bridge.py - Gets injected into every script
class VibeBridge:
def __init__(self, script_id: str):
self.script_id = script_id
# No sockets, no files - just stdout communication
def set_state(self, key: str, value: Any) -> None:
"""Persist data that survives script restarts"""
self._send_ipc('STATE_SET', {'key': key, 'value': value})
def notify_ui(self, message: str, level: str = 'info') -> None:
"""Send notification to user interface"""
self._send_ipc('UI_NOTIFY', {
'message': message,
'level': level,
'timestamp': datetime.now().isoformat()
})
def _send_ipc(self, command: str, data: dict) -> None:
"""The magic: structured messages via stdout"""
message = {
'script_id': self.script_id,
'command': command,
'data': data,
'timestamp': datetime.now().isoformat()
}
# Special markers for TypeScript to parse
print(f"__VIBE_IPC__{json.dumps(message)}__END_IPC__", flush=True)
User Script Example
python
# User's automation script
from vibe_bridge import get_bridge
bridge = get_bridge()
# Script can now communicate with the system
bridge.notify_ui("Starting file processing", "info")
for i, file in enumerate(files):
# Process file
result = process_file(file)
# Store results persistently
bridge.set_state(f"processed_file_{i}", result)
# Update UI with progress
bridge.log_progress(i + 1, len(files), f"Processed {file}")
bridge.notify_ui("All files processed successfully", "success")
TypeScript IPC Message Parser Explained
This is the message decoder that extracts structured communication from Python stdout.
The Challenge
Python scripts output both:
Regular stdout (print statements, library output)
IPC messages (bridge communication)
We need to separate these streams without breaking either.
The Solution
typescript
class ProcessManager {
private setupProcessMonitoring(childProcess: ChildProcess): void {
childProcess.stdout?.on('data', (data) => {
this.handleStdout(data.toString());
});
}
private handleStdout(data: string): void {
// The regex magic: find our special markers
const ipcRegex = /__VIBE_IPC__(.+?)__END_IPC__/g;
let match;
let cleanOutput = data;
// Extract all IPC messages
while ((match = ipcRegex.exec(data)) !== null) {
try {
const message = JSON.parse(match[1]);
this.handleIPCMessage(message);
// Remove IPC message from regular output
cleanOutput = cleanOutput.replace(match[0], '');
} catch (error) {
console.error('Failed to parse IPC message:', error);
}
}
// Send clean output to logs (user's print statements)
if (cleanOutput.trim()) {
this.emit('stdout', cleanOutput);
}
}
private handleIPCMessage(message: IPCMessage): void {
switch (message.command) {
case 'STATE_SET':
// Store in database via StatePod
this.statePod.set(message.data.key, message.data.value);
break;
case 'UI_NOTIFY':
// Send to frontend via WebSocket
this.emit('notification', {
scriptId: message.script_id,
level: message.data.level,
message: message.data.message,
timestamp: message.timestamp
});
break;
case 'PROGRESS_UPDATE':
// Update execution progress
this.emit('progress', {
scriptId: message.script_id,
current: message.data.current,
total: message.data.total,
percentage: message.data.percentage
});
break;
}
}
}
Message Flow Example
Python script calls:
bridge.set_state("counter", 42)Python stdout outputs:
Regular log message __VIBE_IPC__{"script_id":"script_123","command":"STATE_SET","data":{"key":"counter","value":42}}__END_IPC__ More log output-
TypeScript parser:
Extracts IPC message with regex
Calls
statePod.set("counter", 42)Sends clean output to logs:
Regular log message More log output
Implementation Strategy
Week 1 Development Approach
Day 1: Environment Setup
bash
# Create development container
mkdir vibecodrehab-dev && cd vibecodrehab-dev
# Set up embedded Python in container
# Configure TypeScript development environment
Day 2: Bridge Module
Implement
vibe_bridge.pywith basic IPC functionsTest message formatting and JSON serialization
Verify stdout doesn't interfere with normal output
Day 3: IPC Parser
Build TypeScript regex parser for IPC messages
Test with mock Python output containing IPC + regular output
Implement message routing (state, notifications, progress)
Day 4: Process Integration
Combine bridge injection with process spawning
Test bidirectional communication
Verify message isolation and parsing
Day 5: First Working Script
Execute simple Python script with bridge communication
Verify state persistence across script runs
Test error handling and process cleanup
Critical Success Criteria
Python script can store/retrieve persistent state
UI receives real-time notifications from script
Regular Python output (print statements) still works
Process isolation prevents one script from affecting others
The IPC bridge is the foundational constraint - everything else builds on reliable process communication. Get this right, and the rest follows naturally.
Ready to move to IDE/container development?
Retry
JS
User Quesion --
The user is asking about two specific things:
Podman vs Docker differences for their setup
IDE/development environment choice between Windows (WSL2 + VS Code + Claude Code), Windows (Kimi K2 Code), or Mac (Claude Code)
Let me think through these:
Podman vs Docker
Podman is designed to be a drop-in replacement for Docker, but there are some key differences:
Rootless by default - Podman runs without root privileges
No daemon - Docker runs a daemon, Podman doesn't
Pod concept - Podman has native pod support (like Kubernetes pods)
Systemd integration - Better systemd integration
Compose compatibility - podman-compose works with docker-compose files, but there can be subtle differences
For our VibeCodeRehab project, the main considerations:
Process spawning: Python processes need to be spawned from within containers
File system access: Need to mount volumes for persistent state
IPC communication: stdout/stderr capture between processes
Resource limits: Memory/CPU limits per script process
IDE Environment Analysis
Windows WSL2 + VS Code + Claude Code
Pros:
Excellent Docker/Podman integration through WSL2
Native Linux environment for Python development
Claude Code works great in Linux environment
VS Code has excellent remote development support
File system performance is good in WSL2
Can test both Windows and Linux deployment scenarios
Cons:
Some overhead from WSL2 layer
Occasional file system sync issues between Windows/WSL2
Resource usage slightly higher
Windows + Kimi K2 Code
Pros:
Native Windows performance
Direct access to Windows Python if needed
No virtualization overhead
Familiar Windows development environment
Cons:
Python subprocess spawning behaves differently on Windows
Path handling differences (Windows vs Unix paths)
Container integration becomes more intricate
Process isolation requires specialized configuration
IPC bridging needs targeted Windows-specific testing
Mac + Claude Code
Pros:
Unix-like environment mirrors production Linux setup
Superior container performance
Robust signal handling and process management
Ideal for deepening Unix/Linux system administration skills
Claude Code integrates seamlessly
Production environment most closely replicated
Cons:
Potential learning curve for Mac-unfamiliar developers
Unique package management approach (brew vs apt)
Occasional library compatibility challenges, especially with audio processing libraries
The core project challenge centers on intricate process isolation and inter-process communication. Critical technical considerations involve sophisticated subprocess management, including real-time output parsing, nuanced signal handling, and comprehensive resource control mechanisms. Windows introduces significant architectural variations in process management, requiring careful cross-platform adaptation strategies. The Linux-based production environment underscores the importance of developing in a Unix-like ecosystem to minimize potential deployment complexities.
Podman vs Docker Changes
Good news: Minimal changes needed. Podman is designed as a drop-in replacement.
Key Differences for Our Project
yaml
# docker-compose.yml → podman-compose.yml (same file)
version: '3.8'
services:
vibe-app:
build: .
# Podman handles rootless containers better
user: "1000:1000" # Add explicit user mapping
security_opt:
- "label=disable" # SELinux compatibility
Podman-Specific Advantages
Rootless by default - Better security for script execution
No daemon - Lower resource overhead
Systemd integration - Better for production deployment
Pod concepts - Natural fit for our isolated script processes
Commands Stay the Same
bash
# These work identically
podman-compose up -d
podman-compose build
podman-compose logs
One Important Change
bash
# Add to your development setup
podman system service --time=0 unix:///tmp/podman.sock &
export DOCKER_HOST=unix:///tmp/podman.sock
IDE Environment Recommendation: Mac + Claude Code
Why Mac is the Strategic Choice
Critical Constraint: Our production environment is Linux. The closer your development environment matches production, the fewer surprises you'll encounter.
Development Implications Analysis
AspectWindows + WSL2Windows + KimiMac + Claude CodeProcess ManagementGood (Linux-like)Complex (Windows semantics)Excellent (Unix native)Signal HandlingGood (POSIX)Limited (Windows signals)Excellent (POSIX native)Container IntegrationGood (through WSL2)Complex (Windows containers)Excellent (native)Python SubprocessGood (Linux behavior)Different (Windows behavior)Excellent (Unix behavior)File Path HandlingMixed (WSL2 vs Windows)Windows-specificUnix-consistentProduction SimilarityHigh (Linux in WSL2)Low (Windows-specific)Highest (Unix-like)
The IPC Bridge Constraint
Our core constraint - Python ↔ TypeScript communication - depends heavily on:
python
# This works differently across platforms
import subprocess
import signal
import os
# Unix signals (Mac/Linux)
process.send_signal(signal.SIGTERM) # Graceful shutdown
process.send_signal(signal.SIGKILL) # Force kill
# Windows equivalent is different
# Requires different process management code
Mac Development Setup Strategy
Learning Path to Minimize Friction:
bash
# Day 1: Basic Mac setup
brew install podman podman-compose
brew install node [email protected]
brew install --cask visual-studio-code
# Day 2: Claude Code setup
# Install Claude Code CLI
# Configure development environment
# Day 3: Container development
podman-compose up -f docker-compose.dev.yml
Mac-Specific Advantages for Our Project
-
Native Unix Process Model
fork()andexec()work as expectedSignal handling matches Linux production
Process groups and session management identical
-
Python Library Compatibility
Libraries like
tesseract,espeakinstall similarly to LinuxAudio/GUI automation libraries behave consistently
Dependency management closer to production
-
Container Performance
No virtualization layer (unlike WSL2)
Native Docker socket communication
Better file system performance
-
Production Debugging
Same shell commands and utilities
Identical path structures and permissions
Similar networking and process behavior
Overcoming Mac Learning Curve
Essential Mac Commands for This Project:
bash
# Package management (replaces apt-get)
brew install <package>
brew services start <service>
# File system (same as Linux)
ls -la, cd, mkdir, chmod, chown
# Process management (identical to Linux)
ps aux, kill -9, jobs, bg, fg
# Development tools
code . # VS Code
which python3 # Find Python path
Recommended Learning Approach:
Day 1: Basic navigation and Homebrew
Day 2: Terminal and command line tools
Day 3: Development environment setup
Week 2: Advanced Unix concepts as needed
Alternative: WSL2 Compromise
If Mac learning curve is too steep initially:
WSL2 + VS Code gives you:
Linux development environment
Windows familiarity
Excellent Claude Code integration
Easy transition to Mac later
But you'll need to handle:
File system sync between Windows/WSL2
Slightly different container behavior
Some performance overhead
Recommendation: Start with Mac
Why This Constraint Drives Everything
The process isolation and IPC communication is the hardest technical challenge. Getting it working smoothly on Mac means:
It will work identically in Linux production
No platform-specific bugs to debug later
Learning Unix/Linux skills that compound over time
Risk Mitigation
If Mac proves too challenging in first week:
Fallback to WSL2 immediately
All architecture and code transfers directly
Switch back to Mac later when more comfortable
The Learning Investment
Short-term cost: 2-3 days learning Mac basics Long-term benefit: Unix expertise that applies to:
Linux server management
Production debugging
DevOps and deployment
Most development environments
Bottom Line: The constraint-driven architecture we've built is Unix-centric. Developing in the target environment eliminates a whole class of platform-specific issues.