# Project export: Cloudist

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 12.0
- Tagline: An all-in-one block-based solution to build complex cloud architecture
- Devpost: https://devpost.com/software/cloudist
- GitHub: https://github.com/RishabhK9/Cloudist
- Video: https://www.youtube.com/embed/6hJr_Uh5Vpo?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 6 GitHub contributor(s) — Rytxxx (49 commits), Rishabh Kanodia (23 commits), Copilot (15 commits), Abhay Sudhir (6 commits), Dzhamal Chapanov (5 commits), coderabbitai[bot] (1 commits)

## Devpost submission (written by the team)

### Inspiration

As developers, we often spend countless hours setting up cloud infrastructure, databases, and authentication systems, leaving little time to focus on actually building the innovations that make a difference. Cloudist was born to solve this problem.

### What it does

Cloudist provides a block-based interface that lets users connect AWS instances, Supabase, and Stripe. With just a click, these components can be deployed into a fully functional infrastructure ready for development.

### How we built it

We built Cloudist using a Next.js frontend paired with a backend that integrates both Terraform and native APIs. This combination allows users (even beginners) to launch complex cloud infrastructure effortlessly through an intuitive visual interface.

### Challenges we ran into

Our initial approach relied solely on Terraform to integrate AWS with services like Stripe and Supabase. However, the documentation for these integrations was inconsistent and incomplete, leading to unreliable deployments. To overcome this, we combined Terraform with native APIs to achieve both flexibility and stability.

### Accomplishments we're proud of

We successfully distilled a complex field like DevOps—one that often takes years to master—into an accessible, beginner-friendly platform. Whether you're launching a startup or building a side project, Cloudist empowers you to deploy with confidence.

### What we learned

We deepened our understanding of AWS, GCP, and Terraform, gaining valuable insight into how these tools can work together to create seamless, automated cloud environments.

### What's next

We plan to extend support to additional cloud providers such as GCP, DigitalOcean, and Azure, giving developers the freedom to choose their preferred platforms while maintaining Cloudist’s simple and intuitive interface.

## README (from the GitHub repository)

# Cloudist ☁️

> AI-powered visual infrastructure designer with intelligent code review

Cloudist is a next-generation infrastructure design tool that combines visual drag-and-drop canvas with AI-powered assistance and professional code review. Built for Cal Hacks 2025.

## ✨ Features

- 🤖 **AI Infrastructure Assistant (Rex)** - Powered by Claude 3.5 Sonnet
- 🐰 **CodeRabbit Integration** - Professional Terraform code review via CLI
- 🎨 **Visual Canvas** - Drag-and-drop infrastructure designer
- 🌐 **Multi-Cloud Support** - AWS, Azure, GCP services
- 📦 **Third-Party Integrations** - Supabase, Stripe, and more
- 🚀 **Export Terraform** - Production-ready infrastructure code

## 🚀 Quick Start

See [SETUP.md](./SETUP.md) for detailed setup instructions.

```bash
# Install dependencies
npm install

# Set up environment variables
cp .env.example .env.local

# Install CodeRabbit CLI
curl -fsSL https://cli.coderabbit.ai/install.sh | sh
coderabbit auth login

# Run development server
npm run dev
```

## 🏆 Technologies

- **AI**: Claude 3.5 Sonnet (Anthropic)
- **Code Review**: CodeRabbit CLI
- **Frontend**: Next.js 14, React, TailwindCSS
- **Infrastructure**: React Flow, Terraform

## 📄 License

MIT

---

Built for Cal Hacks 2025 🐻

## Detected evidence (automated analysis)

Indexed codebase: 196 recognized source files, 1322 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- AWS (technology) — claimed on Devpost, not found in the code
- Supabase (technology) — claimed on Devpost, not found in the code
- AI coding agent: GitHub Copilot — evidence: commit authorship or trailers

## Codebase structure (from repository index)

### Files (120 of 229)

```
.dockerignore
.env.example
.gitignore
app/api/agent/route.ts
app/api/ai-review/route.ts
app/api/config/[provider]/[serviceId]/route.ts
app/api/config/route.ts
app/api/supabase/deploy/route.ts
app/api/supabase/status/route.ts
app/api/supabase/test/route.ts
app/api/terraform/apply/route.ts
app/api/terraform/destroy/route.ts
app/api/terraform/generate/route.ts
app/api/terraform/init/route.ts
app/api/terraform/output/route.ts
app/api/terraform/plan/route.ts
app/api/terraform/validate/route.ts
app/api/workspaces/[id]/files/route.ts
app/api/workspaces/[id]/route.ts
app/api/workspaces/route.ts
app/editor/page.tsx
app/globals.css
app/layout.tsx
app/page.tsx
components.json
components/canvas.tsx
components/canvas/canvas.tsx
components/canvas/cloud-service-node.tsx
components/canvas/connection-edge.tsx
components/canvas/infrastructure-canvas.tsx
components/dialogs/ai-review-dialog.tsx
components/dialogs/console-output-dialog.tsx
components/dialogs/create-new-project-dialog.tsx
components/dialogs/create-project-dialog.tsx
components/dialogs/deployment-dialog.tsx
components/dialogs/edit-project-dialog.tsx
components/dialogs/open-project-dialog.tsx
components/dialogs/plan-preview-dialog.tsx
components/dialogs/settings-dialog.tsx
components/dialogs/terraform-export-dialog.tsx
components/dialogs/terraform-preview.tsx
components/features/agent-chat.tsx
components/features/provider-selection.tsx
components/features/save-status-indicator.tsx
components/features/undo-redo-controls.tsx
components/infrastructure-builder.tsx
components/infrastructure-canvas.tsx
components/layout/dashboard.tsx
components/layout/project-title-bar.tsx
components/layout/project-view.tsx
components/layout/toolbar.tsx
components/panels/component-palette.tsx
components/panels/configuration-panel.tsx
components/panels/deployment-status-panel.tsx
components/panels/properties-panel.tsx
components/panels/providers-pane.tsx
components/provider-selection.tsx
components/test/supabase-deployment-test.tsx
components/test/terraform-loading-test.tsx
components/theme-provider.tsx
components/toolbar.tsx
components/ui/accordion.tsx
components/ui/alert-dialog.tsx
components/ui/alert.tsx
components/ui/aspect-ratio.tsx
components/ui/avatar.tsx
components/ui/badge.tsx
components/ui/breadcrumb.tsx
components/ui/button-group.tsx
components/ui/button.tsx
components/ui/calendar.tsx
components/ui/card.tsx
components/ui/carousel.tsx
components/ui/chart.tsx
components/ui/checkbox.tsx
components/ui/collapsible.tsx
components/ui/command.tsx
components/ui/context-menu.tsx
components/ui/dialog.tsx
components/ui/drawer.tsx
components/ui/dropdown-menu.tsx
components/ui/empty.tsx
components/ui/field.tsx
components/ui/form.tsx
components/ui/hover-card.tsx
components/ui/input-group.tsx
components/ui/input-otp.tsx
components/ui/input.tsx
components/ui/item.tsx
components/ui/kbd.tsx
components/ui/label.tsx
components/ui/menubar.tsx
components/ui/navigation-menu.tsx
components/ui/pagination.tsx
components/ui/popover.tsx
components/ui/progress.tsx
components/ui/radio-group.tsx
components/ui/resizable.tsx
components/ui/scroll-area.tsx
components/ui/select.tsx
components/ui/separator.tsx
components/ui/sheet.tsx
components/ui/sidebar.tsx
components/ui/skeleton.tsx
components/ui/slider.tsx
components/ui/sonner.tsx
components/ui/spinner.tsx
components/ui/switch.tsx
components/ui/table.tsx
components/ui/tabs.tsx
components/ui/terraform-loading-bar.tsx
components/ui/textarea.tsx
components/ui/toast.tsx
components/ui/toaster.tsx
components/ui/toggle-group.tsx
components/ui/toggle.tsx
components/ui/tooltip.tsx
components/ui/use-mobile.tsx
components/ui/use-toast.ts
components/utils/connection-validator.ts
[109 more files omitted for size]
```

### Dependencies

- package.json: @anthropic-ai/sdk@^0.61.0, @hookform/resolvers@^3.10.0, @radix-ui/react-accordion@1.2.2, @radix-ui/react-alert-dialog@1.1.4, @radix-ui/react-aspect-ratio@1.1.1, @radix-ui/react-avatar@1.1.2, @radix-ui/react-checkbox@1.1.3, @radix-ui/react-collapsible@1.1.2, @radix-ui/react-context-menu@2.2.4, @radix-ui/react-dialog@1.1.4, @radix-ui/react-dropdown-menu@2.1.4, @radix-ui/react-hover-card@1.1.4, @radix-ui/react-label@2.1.1, @radix-ui/react-menubar@1.1.4, @radix-ui/react-navigation-menu@1.2.3, @radix-ui/react-popover@1.1.4, @radix-ui/react-progress@1.1.1, @radix-ui/react-radio-group@1.2.2, @radix-ui/react-scroll-area@1.2.2, @radix-ui/react-select@2.1.4, @radix-ui/react-separator@1.1.1, @radix-ui/react-slider@1.2.2, @radix-ui/react-slot@1.1.1, @radix-ui/react-switch@1.1.2, @radix-ui/react-tabs@1.1.2, @radix-ui/react-toast@1.2.4, @radix-ui/react-toggle@1.1.1, @radix-ui/react-toggle-group@1.1.1, @radix-ui/react-tooltip@1.1.6, @types/jszip@^3.4.0, @types/node@^22, @types/react@^18, @types/react-dom@^18, @vercel/analytics@latest, @xyflow/react@latest, autoprefixer@^10.4.20, class-variance-authority@^0.7.1, clsx@^2.1.1, cmdk@1.0.4, date-fns@4.1.0, embla-carousel-react@8.5.1, framer-motion@^12.23.22, geist@latest, input-otp@1.4.1, jszip@^3.10.1, lucide-react@^0.454.0, motion@^12.23.22, motion-plus-react@^1.5.4, next@^14.2.33, next-themes@^0.4.6, openai@^5.23.1, postcss@^8.5, react@^18, react-day-picker@9.8.0, react-dom@^18, react-hook-form@^7.60.0, react-icons@^5.5.0, react-resizable-panels@^2.1.7, recharts@2.15.4, sonner@^1.7.4, tailwind-merge@^2.5.5, tailwindcss@^3.4.1, tailwindcss-animate@^1.0.7, typescript@^5, vaul@^0.9.9, zod@3.25.67, zustand@^5.0.8

### Recent commits (newest first)

- Merge branch 'fixes'
- Changes
- Edits to supabase
- Working wip demo
- Merge pull request #12 from RishabhK9/ui
- Merge branch 'ui' of https://github.com/RishabhK9/Cloudist into ui
- fix: prevent memory leaks in console output streaming
- Update components/dialogs/console-output-dialog.tsx
- Update components/dialogs/terraform-preview.tsx
- Update components/dialogs/console-output-dialog.tsx
- Update components/dialogs/console-output-dialog.tsx
- fix: preserve code indentation in Terraform preview
- feat: enhance console output dialog with streaming and controls
- Merge pull request #11 from RishabhK9/feat/deployment-process
- Update components/infrastructure-canvas.tsx
- Update components/dialogs/terraform-preview.tsx
- Merge branch 'feat/deployment-process' of https://github.com/RishabhK9/Cloudist into feat/deployment-process
- feat: enhance terraform execution security and reliability
- Update components/infrastructure-canvas.tsx
- feat: enhance terraform API endpoints with dynamic code generation

## Key source files (fetched from GitHub, selected and truncated for size)

### MIGRATION_TO_CLAUDE.md

```markdown
# Migration to Claude (Anthropic) - Complete ✅

Your project has been successfully migrated from OpenAI GPT-4.1-mini to Claude 3.5 Sonnet!

## What Changed

### Files Modified:
1. **`package.json`** - Replaced `openai` package with `@anthropic-ai/sdk`
2. **`app/api/ai-review/route.ts`** - Updated to use Claude for Terraform code review
3. **`app/api/agent/route.ts`** - Updated to use Claude for infrastructure agent chat

### Key Differences:
- **Model**: Now using `claude-3-5-sonnet-20241022` (Claude 3.5 Sonnet)
- **API Format**: Claude uses a different message structure with content blocks
- **Tools**: Converted from OpenAI's function calling to Claude's tool use format
- **Max Tokens**: Increased to 4096 tokens for better responses

## Setup Instructions

### 1. Install Dependencies
```bash
npm install
# or
pnpm install
# or
yarn install
```

### 2. Get Your Anthropic API Key
1. Go to [https://console.anthropic.com/](https://console.anthropic.com/)
2. Sign up or log in
3. Navigate to API Keys section
4. Create a new API key

### 3. Set Environment Variable
Create a `.env.local` file in the project root:
```bash
ANTHROPIC_API_KEY=your_actual_api_key_here
```

**Note**: Never commit your `.env.local` file. It should already be in `.gitignore`.

### 4. Start Development Server
```bash
npm run dev
```

## Features Now Using Claude

### 1. **AI Code Review** 
- Analyzes Terraform configurations
- Provides security, cost, and best practice recommendations
- Generates comprehensive infrastructure reviews

### 2. **Infrastructure Agent (Rex)**
- Interactive chat for cloud architecture questions
- Can create and modify infrastructure on canvas
- Provides troubleshooting and best practices

## Benefits of Claude

✅ **Better Code Understanding** - Claude excels at analyzing complex code  
✅ **Longer Context** - 200K token context window  
✅ **More Accurate** - Improved accuracy for technical tasks  
✅ **Better at Following Instructions** - More reliable tool/function calling  
✅ **Cost Effective** - Competitive pricing compared to GPT-4

## Workflow

Your main workflow remains the same:
1. User creates cloud architecture diagram on canvas
2. **Claude reviews** the configuration
3. **Claude generates** Terraform files
4. Deploy to actual cloud services

## Troubleshooting

### TypeScript Errors
The TypeScript errors about `@anthropic-ai/sdk` will disappear after running `npm install`.

### API Key Issues
- Make sure your API key is valid and active
- Check that `.env.local` is in the project root
- Restart your dev server after adding the API key

### Rate Limits
Claude has different rate limits than OpenAI. Check your plan at [https://console.anthropic.com/settings/limits](https://console.anthropic.com/settings/limits)

## Need Help?

- [Anthropic Documentation](https://docs.anthropic.com/)
- [Claude API Reference](https://docs.anthropic.com/en/api/messages)
- [Pricing Information](https://www.anthropic.com/pricing)

---

**Migration completed successfull
[truncated — 55 more characters]
```

### SETUP.md

```markdown
# Cloudist Setup Guide

## 🚀 Quick Start

### 1. Install Dependencies

```bash
npm install
```

### 2. Set Up Environment Variables

Copy the example environment file:

```bash
cp .env.example .env.local
```

### 3. Configure Required Services

#### Claude API (Required for AI Agent)

Cloudist uses **Claude 3.5 Sonnet** for the AI infrastructure assistant (Rex).

1. Get your API key from [Anthropic Console](https://console.anthropic.com/)
2. Add to `.env.local`:
   ```
   ANTHROPIC_API_KEY=your_api_key_here
   ```

#### CodeRabbit CLI (Required for Code Review)

CodeRabbit provides AI-powered code reviews for your Terraform configurations.

1. Install the CLI:
   ```bash
   curl -fsSL https://cli.coderabbit.ai/install.sh | sh
   ```

2. Restart your shell:
   ```bash
   source ~/.zshrc  # or ~/.bashrc
   ```

3. Authenticate:
   ```bash
   coderabbit auth login
   ```

4. Verify installation:
   ```bash
   coderabbit --version
   ```

**Note**: CodeRabbit CLI must be installed on your system. The application calls it as a subprocess.

### 4. Run Development Server

```bash
npm run dev
```

Visit [http://localhost:3000](http://localhost:3000) to see Cloudist in action!

---

## 🛠️ Technologies Used

- **AI Agent**: Claude 3.5 Sonnet (Anthropic) - Powers the "Rex" infrastructure assistant
- **Code Review**: CodeRabbit CLI - Provides professional-grade Terraform code analysis
- **Frontend**: Next.js 14, React, TailwindCSS
- **Infrastructure Visualization**: React Flow
- **Cloud Providers**: AWS, Azure, GCP support

---

## 📝 Optional Configurations

### Cloud Provider Credentials

For actual infrastructure deployment (optional):

#### AWS
```env
AWS_ACCESS_KEY_ID=your_key
AWS_SECRET_ACCESS_KEY=your_secret
AWS_REGION=us-east-1
```

#### Azure
```env
AZURE_SUBSCRIPTION_ID=your_id
AZURE_CLIENT_ID=your_client_id
AZURE_CLIENT_SECRET=your_secret
AZURE_TENANT_ID=your_tenant_id
```

#### GCP
```env
GCP_PROJECT_ID=your_project
GCP_SERVICE_ACCOUNT_KEY=your_json_key
```

### Third-Party Integrations

#### Supabase
```env
NEXT_PUBLIC_SUPABASE_URL=your_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_key
```

#### Stripe
```env
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=your_key
STRIPE_SECRET_KEY=your_secret
```

---

## 🏆 Cal Hacks 2025 Integration

This project integrates:

- **🧊 Claude (Anthropic)**: AI infrastructure assistant with function calling
- **👓 CodeRabbit**: CLI-based code review for Terraform configurations
- **🚀 Y Combinator**: B2B developer tool with clear product-market fit

### Features Showcased

1. **AI-Powered Infrastructure Design**: Chat with Rex to create cloud infrastructure
2. **CodeRabbit Code Review**: Click "Code Review 🐰" to analyze Terraform with CodeRabbit CLI
3. **Visual Canvas**: Drag-and-drop infrastructure designer
4. **Multi-Cloud Support**: AWS, Azure, GCP service integration

---

## 🐛 Troubleshooting

### CodeRabbit CLI Not Found

If you see "CodeRabbit CLI not installed":

1. Verify installation: `which coderabbit`
2. If not foun
[truncated — 1097 more characters]
```

### Dockerfile

```
# Cloudist Dockerfile with Terraform CLI
# This builds a production image with Next.js + Terraform

# Stage 1: Build the Next.js app
FROM node:20-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./
COPY pnpm-lock.yaml* ./

# Install dependencies (prefer pnpm if available)
RUN npm install -g pnpm && pnpm install --frozen-lockfile || npm ci

# Copy source code
COPY . .

# Build Next.js app
RUN npm run build

# Stage 2: Production image with Terraform CLI
FROM node:20-alpine AS runner

WORKDIR /app

# Install Terraform CLI
RUN apk add --no-cache \
    wget \
    unzip \
    && wget https://releases.hashicorp.com/terraform/1.9.5/terraform_1.9.5_linux_amd64.zip \
    && unzip terraform_1.9.5_linux_amd64.zip \
    && mv terraform /usr/local/bin/ \
    && rm terraform_1.9.5_linux_amd64.zip \
    && terraform version

# Install production dependencies only
COPY package*.json ./
RUN npm ci --only=production

# Copy built app from builder
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/next.config.mjs ./
COPY --from=builder /app/package.json ./

# Create directory for Terraform workspaces
RUN mkdir -p /app/terraform-workspaces && chmod 777 /app/terraform-workspaces

# Set environment variables
ENV NODE_ENV=production
ENV PORT=3000

# Expose port
EXPOSE 3000

# Start the app
CMD ["npm", "start"]


```

### package.json

```
{
  "name": "infrablocks",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "build": "next build",
    "dev": "next dev",
    "lint": "next lint",
    "start": "next start"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.61.0",
    "@hookform/resolvers": "^3.10.0",
    "@radix-ui/react-accordion": "1.2.2",
    "@radix-ui/react-alert-dialog": "1.1.4",
    "@radix-ui/react-aspect-ratio": "1.1.1",
    "@radix-ui/react-avatar": "1.1.2",
    "@radix-ui/react-checkbox": "1.1.3",
    "@radix-ui/react-collapsible": "1.1.2",
    "@radix-ui/react-context-menu": "2.2.4",
    "@radix-ui/react-dialog": "1.1.4",
    "@radix-ui/react-dropdown-menu": "2.1.4",
    "@radix-ui/react-hover-card": "1.1.4",
    "@radix-ui/react-label": "2.1.1",
    "@radix-ui/react-menubar": "1.1.4",
    "@radix-ui/react-navigation-menu": "1.2.3",
    "@radix-ui/react-popover": "1.1.4",
    "@radix-ui/react-progress": "1.1.1",
    "@radix-ui/react-radio-group": "1.2.2",
    "@radix-ui/react-scroll-area": "1.2.2",
    "@radix-ui/react-select": "2.1.4",
    "@radix-ui/react-separator": "1.1.1",
    "@radix-ui/react-slider": "1.2.2",
    "@radix-ui/react-slot": "1.1.1",
    "@radix-ui/react-switch": "1.1.2",
    "@radix-ui/react-tabs": "1.1.2",
    "@radix-ui/react-toast": "1.2.4",
    "@radix-ui/react-toggle": "1.1.1",
    "@radix-ui/react-toggle-group": "1.1.1",
    "@radix-ui/react-tooltip": "1.1.6",
    "@types/jszip": "^3.4.0",
    "@vercel/analytics": "latest",
    "@xyflow/react": "latest",
    "autoprefixer": "^10.4.20",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "1.0.4",
    "date-fns": "4.1.0",
    "embla-carousel-react": "8.5.1",
    "framer-motion": "^12.23.22",
    "geist": "latest",
    "input-otp": "1.4.1",
    "jszip": "^3.10.1",
    "lucide-react": "^0.454.0",
    "motion": "^12.23.22",
    "motion-plus-react": "^1.5.4",
    "next": "^14.2.33",
    "next-themes": "^0.4.6",
    "openai": "^5.23.1",
    "react": "^18",
    "react-day-picker": "9.8.0",
    "react-dom": "^18",
    "react-hook-form": "^7.60.0",
    "react-icons": "^5.5.0",
    "react-resizable-panels": "^2.1.7",
    "recharts": "2.15.4",
    "sonner": "^1.7.4",
    "tailwind-merge": "^2.5.5",
    "tailwindcss-animate": "^1.0.7",
    "vaul": "^0.9.9",
    "zod": "3.25.67",
    "zustand": "^5.0.8"
  },
  "devDependencies": {
    "@types/node": "^22",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "postcss": "^8.5",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
  }
}

```

### app/page.tsx

```typescript
"use client"

import { Dashboard } from "@/components/layout/dashboard"

export default function HomePage() {
  return (
    <main className="min-h-screen bg-background">
      <Dashboard />
    </main>
  )
}

```

### app/layout.tsx

```typescript
import type { Metadata } from "next";
import { GeistSans } from "geist/font/sans";
import { GeistMono } from "geist/font/mono";
import { Analytics } from "@vercel/analytics/next";
import { Toaster } from "@/components/ui/toaster";
import "./globals.css";

export const metadata: Metadata = {
  title: "Cloudist - Infrastructure Designer",
  description: "Visual infrastructure design and deployment tool",
  generator: "v0.app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={`${GeistSans.variable} ${GeistMono.variable} font-sans antialiased`}>
        {children}
        <Toaster />
        <Analytics />
      </body>
    </html>
  );
}

```

### app/editor/page.tsx

```typescript
"use client"

import { InfrastructureBuilder } from "@/components/infrastructure-builder"
import { useRouter } from "next/navigation"

export default function EditorPage() {
  const router = useRouter()
  
  const handleBackToHome = () => {
    router.push("/")
  }
  
  return <InfrastructureBuilder onBackToHome={handleBackToHome} />
}


```

### app/api/config/route.ts

```typescript
import { NextRequest, NextResponse } from 'next/server'
import fs from 'fs/promises'
import path from 'path'

// Static configuration for available providers and services
const AVAILABLE_PROVIDERS = ['aws', 'azure', 'gcp', 'supabase', 'stripe'] as const

const PROVIDER_SERVICES: Record<string, string[]> = {
  aws: [
    'api_gateway',
    'cloudwatch',
    'cognito',
    'dynamodb',
    'ec2',
    'fargate',
    'lambda',
    'rds',
    's3',
    'secrets_manager',
    'sqs',
    'step_functions',
    'vpc'
  ],
  azure: ['vm'],
  gcp: ['compute'],
  supabase: ['database', 'auth'],
  stripe: ['payment']
}

// GET /api/config - Load all configs
export async function GET(request: NextRequest) {
  const url = new URL(request.url)
  const provider = url.searchParams.get('provider')
  const serviceId = url.searchParams.get('serviceId')

  try {
    // If specific provider and service requested
    if (provider && serviceId) {
      const config = await loadServiceConfig(provider, serviceId)
      if (!config) {
        return NextResponse.json({ error: 'Config not found' }, { status: 404 })
      }
      return NextResponse.json(config)
    }

    // Load all configs
    const result: Record<string, Record<string, any>> = {}

    for (const provider of AVAILABLE_PROVIDERS) {
      result[provider] = {}
      const services = PROVIDER_SERVICES[provider] || []

      for (const serviceId of services) {
        const config = await loadServiceConfig(provider, serviceId)
        if (config) {
          result[provider][serviceId] = config
        }
      }
    }

    return NextResponse.json(result)
  } catch (error) {
    console.error('Failed to load configs:', error)
    return NextResponse.json({ error: 'Failed to load configs' }, { status: 500 })
  }
}

async function loadServiceConfig(provider: string, serviceId: string) {
  try {
    const filePath = path.join(process.cwd(), 'config', provider, `${serviceId}.json`)
    const fileContent = await fs.readFile(filePath, 'utf8')
    return JSON.parse(fileContent)
  } catch (error) {
    console.warn(`Failed to load config for ${provider}/${serviceId}:`, error)
    return null
  }
}

```

### app/api/workspaces/route.ts

```typescript
import { TerraformGenerator } from '@/components/utils/terraform-generator'
import type { Edge, Node } from '@xyflow/react'
import { promises as fs } from 'fs'
import { NextRequest, NextResponse } from 'next/server'
import path from 'path'

export interface TerraformWorkspace {
  id: string
  name: string
  provider: string
  workingDirectory: string
  createdAt: string
  status: 'active' | 'destroyed' | 'failed'
  lastDeployed?: string
}

export interface GeneratedFiles {
  mainTf: string
  variablesTf: string
  outputsTf: string
  terraformTf: string
  additionalFiles: Record<string, string>
}

// In-memory storage for workspaces (in production, use a database)
const workspaces: Map<string, TerraformWorkspace> = new Map()
const baseDirectory = path.join(process.cwd(), 'terraform-workspaces')

/**
 * Create a new Terraform workspace
 */
export async function POST(request: NextRequest) {
  try {
    const body = await request.json()
    const { name, provider, nodes, edges } = body

    if (!name || !provider || !nodes || !edges) {
      return NextResponse.json(
        { error: 'Missing required fields: name, provider, nodes, edges' },
        { status: 400 }
      )
    }

    const workspaceId = `workspace_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
    const workingDirectory = path.join(baseDirectory, workspaceId)

    // Create workspace directory
    await fs.mkdir(workingDirectory, { recursive: true })

    const workspace: TerraformWorkspace = {
      id: workspaceId,
      name,
      provider,
      workingDirectory,
      createdAt: new Date().toISOString(),
      status: 'active'
    }

    workspaces.set(workspaceId, workspace)

    // Generate Terraform files
    await generateTerraformFiles(workspace, nodes, edges)

    return NextResponse.json({ workspace })
  } catch (error) {
    console.error('Error creating workspace:', error)
    return NextResponse.json(
      { error: 'Failed to create workspace' },
      { status: 500 }
    )
  }
}

/**
 * Get all workspaces
 */
export async function GET() {
  try {
    const allWorkspaces = Array.from(workspaces.values()).sort((a, b) => 
      new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
    )
    return NextResponse.json({ workspaces: allWorkspaces })
  } catch (error) {
    console.error('Error getting workspaces:', error)
    return NextResponse.json(
      { error: 'Failed to get workspaces' },
      { status: 500 }
    )
  }
}

/**
 * Generate all Terraform files for a workspace
 */
async function generateTerraformFiles(
  workspace: TerraformWorkspace,
  nodes: Node[],
  edges: Edge[]
): Promise<GeneratedFiles> {
  const generator = new TerraformGenerator(workspace.provider, nodes, edges)
  const output = await generator.generate()

  // Generate main.tf with only resources (no variables, outputs, or provider config)
  const mainTf = generateMainTfOnly(output.resources)
  await fs.writeFile(path.join(workspace.workingDirectory, 'main.tf'), mainTf, 'utf8')

  // Generate variables.tf
  const variablesTf = generateVariablesFile(output.variables)
  await fs.writeFile(path.join(workspace.workingDirectory, 'variables.tf'), variablesTf, 'utf8')

  // Generate outputs.tf
  const outputsTf = generateOutputsFile(output.outputs)
  await fs.writeFile(path.join(workspace.workingDirectory, 'outputs.tf'), outputsTf, 'utf8')

  // Generate terraform.tf (provider configuration)
  const terraformTf = generateTerraformConfigFile(workspace.provider)
  await fs.writeFile(path.join(workspace.workingDirectory, 'terraform.tf'), terraformTf, 'utf8')

  // Generate additional provider-specific files
  const additionalFiles = await generateAdditionalFiles(workspace, nodes)

  const generatedFiles: GeneratedFiles = {
    mainTf,
    variablesTf,
    outputsTf,
    terraformTf,
    additionalFiles
  }

  // Save generated files info
  await fs.writeFile(
    path.join(workspace.workingDirectory, 'generated-files.json'),
    JSON.stringify(generatedFiles, null, 2),
    'utf8'
  )

  return generatedFiles
}

/**
 * Generate main.tf file with only resources
 */
function generateMainTfOnly(resources: any[]): string {
  let content = '# Resources\n\n'
  
  resources.forEach((resource) => {
    content += `resource "${resource.type}" "${resource.name}" {\n`
    
    // Special handling for DynamoDB tables - add attribute blocks for keys
    if (resource.type === 'aws_dynamodb_table') {
      const hashKey = resource.config.hash_key
      const rangeKey = resource.config.range_key
      
      if (hashKey) {
        content += `  attribute {\n`
        content += `    name = "${hashKey}"\n`
        content += `    type = "S"\n`
        content += `  }\n`
      }
      
      if (rangeKey) {
        content += `  attribute {\n`
        content += `    name = "${rangeKey}"\n`
        content += `    type = "S"\n`
        content += `  }\n`
      }
    }
    
    // Format resource configuration
    Object.entries(resource.config).forEach(([key, value]) => {
      if (value === null || value === undefined) {
        return
      }
      
      if (typeof value === 'string') {
        if (value.startsWith('var.') || value.startsWith('aws_') || value.startsWith('google_') || value.startsWith('azurerm_')) {
          content += `  ${key} = ${value}\n`
        } else {
          content += `  ${key} = "${value}"\n`
        }
      } else if (typeof value === 'number' || typeof value === 'boolean') {
        content += `  ${key} = ${value}\n`
      } else if (Array.isArray(value)) {
        content += `  ${key} = [${value.map(v => {
          if (typeof v === 'string') {
            // Check if it's a Terraform reference (like aws_security_group.default.id)
            if (v.startsWith('var.') || v.startsWith('aws_') || v.startsWith('google_') || v.startsWith('azurerm_')) {
              return v
            } else {
              return `"${v}"`
            }
          }
          return v
        }).join(', ')}]\n`
      } else 
[truncated — 10690 more characters]
```

### app/api/ai-review/route.ts

```typescript
import { NextRequest, NextResponse } from 'next/server';
import { exec } from 'child_process';
import { promisify } from 'util';
import fs from 'fs/promises';
import path from 'path';
import os from 'os';
import OpenAI from 'openai';

const execAsync = promisify(exec);

// Initialize OpenAI client
const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY || '',
});

export async function POST(request: NextRequest) {
  console.log('🚀 AI Review API called');
  
  try {
    console.log('📥 Parsing request body...');
    const { terraformFiles, provider } = await request.json();
    console.log('✅ Request body parsed:', {
      fileCount: Object.keys(terraformFiles || {}).length,
      provider,
      fileNames: Object.keys(terraformFiles || {})
    });

    if (!terraformFiles || Object.keys(terraformFiles).length === 0) {
      console.error('❌ No Terraform files provided');
      return NextResponse.json(
        { error: 'No Terraform files provided' },
        { status: 400 }
      );
    }

    // Validate that all file contents are strings
    for (const [filename, content] of Object.entries(terraformFiles)) {
      if (typeof content !== 'string') {
        console.error(`❌ Invalid content type for ${filename}:`, typeof content);
        return NextResponse.json(
          { error: `Invalid content type for ${filename}. Expected string, got ${typeof content}` },
          { status: 400 }
        );
      }
    }

    console.log('🔍 Starting CodeRabbit review for', Object.keys(terraformFiles).length, 'files');
    console.log('📄 File contents preview:', Object.entries(terraformFiles).map(([name, content]) => 
      `${name}: ${typeof content === 'string' ? content.substring(0, 100) : 'invalid'}...`
    ));

    // Create temporary directory for review
    const tempDir = path.join(os.tmpdir(), `terraform-review-${Date.now()}`);
    console.log('📁 Creating temp directory:', tempDir);
    await fs.mkdir(tempDir, { recursive: true });
    console.log('✅ Temp directory created');

    try {
      // Write Terraform files to temp directory
      console.log('📝 Writing Terraform files to temp directory...');
      for (const [filename, content] of Object.entries(terraformFiles)) {
        const filePath = path.join(tempDir, filename as string);
        console.log(`  - Writing ${filename} (${typeof content === 'string' ? content.length : 0} chars)`);
        await fs.writeFile(filePath, content as string, 'utf-8');
        console.log(`  ✅ Wrote ${filename}`);
      }

      // Initialize git repo (CodeRabbit needs it)
      console.log('🔧 Initializing Git repository...');
      await execAsync('git init', { cwd: tempDir });
      console.log('✅ Git init complete');
      
      await execAsync('git config user.email "cloudist@coderabbit.ai"', { cwd: tempDir });
      console.log('✅ Git config email set');
      
      await execAsync('git config user.name "Cloudist Review Bot"', { cwd: tempDir });
      console.log('✅ Git config name set');
      
      // Add and commit files to make them reviewable
      console.log('📦 Adding files to git...');
      const gitAddResult = await execAsync('git add .', { cwd: tempDir });
      console.log('✅ Git add complete:', gitAddResult.stdout || 'no output');
      
      console.log('💾 Committing files...');
      const gitCommitResult = await execAsync('git commit -m "Initial Terraform configuration"', { cwd: tempDir });
      console.log('✅ Git commit complete:', gitCommitResult.stdout?.substring(0, 100) || 'no output');

      // Check if CodeRabbit is installed
      console.log('🔍 Checking if CodeRabbit CLI is installed...');
      let useCodeRabbit = false;
      try {
        const { stdout: versionOutput } = await execAsync('which coderabbit');
        console.log('✅ CodeRabbit CLI found at:', versionOutput.trim());
        useCodeRabbit = true;
      } catch (whichError) {
        console.log('⚠️ CodeRabbit CLI not found, falling back to OpenAI API');
      }

      let analysis;
      
      if (useCodeRabbit) {
        console.log('🤖 Running CodeRabbit CLI...');
        console.log('   Command: coderabbit --plain --type committed --no-color');
        console.log('   Working directory:', tempDir);

        // Run CodeRabbit CLI in plain mode
        const startTime = Date.now();
        const { stdout, stderr } = await execAsync(
          'coderabbit --plain --type committed --no-color',
          {
            cwd: tempDir,
            timeout: 60000, // 60 second timeout
            maxBuffer: 1024 * 1024 * 10, // 10MB buffer
          }
        );
        const duration = Date.now() - startTime;

        console.log('✅ CodeRabbit review completed in', duration, 'ms');
        console.log('📊 CodeRabbit stderr:', stderr || '(empty)');
        console.log('📊 CodeRabbit stdout length:', stdout?.length || 0);
        console.log('📊 CodeRabbit stdout preview:', stdout?.substring(0, 200) || '(empty)');

        // Parse CodeRabbit plain text output
        console.log('🔍 Parsing CodeRabbit output...');
        analysis = parseCodeRabbitOutput(stdout || '', terraformFiles);
        console.log('✅ Analysis complete:', {
          score: analysis.overallScore,
          issueCount: analysis.issues?.length || 0,
          summary: analysis.summary?.substring(0, 100)
        });
      } else {
        // Use OpenAI API as fallback
        console.log('🤖 Using OpenAI API for infrastructure review...');
        analysis = await reviewWithOpenAI(terraformFiles);
        console.log('✅ OpenAI analysis complete:', {
          score: analysis.overallScore,
          issueCount: analysis.issues?.length || 0,
          summary: analysis.summary?.substring(0, 100)
        });
      }

      return NextResponse.json(analysis);

    } finally {
      // Cleanup: Remove temp directory
      try {
        await fs.rm(tempDir, { recursive: true, force: true });
        console.log('🧹 Cleaned up temp directory');
      } catch (cleanupError
[truncated — 13613 more characters]
```

[185 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]