vibes
π―Skillfrom popmechanic/vibes-cli
Generates React web apps with Fireproof, enabling local-first data sync and quick prototyping.
Part of
popmechanic/vibes-cli(8 items)
Installation
git clone --branch selem/docker-for-all https://github.com/fireproof-storage/fireproof.git ./fireproofSkill Details
Generate React web apps with Fireproof database. Use when creating new web applications, adding components, or working with local-first databases. Ideal for quick prototypes and single-page apps that need real-time data sync.
Overview
Display this ASCII art immediately when starting:
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββ βββββββ
ββββββββββββββββββββββββββββββββββββββ βββββββ
βββββββββββββββββββββββββββββββββββββββββββ ββββββββββββ
ββββββββββ ββββββββββββββββββββββββββ βββββββ
ββββββββββ ββββββββββββββββββββββββββ βββββββ
ββββββββ βββββββββββββββββββββββββββββββββββββββββββββ
```
Quick Navigation
- [Pre-Flight Check](#pre-flight-check-connect-status) - Validate Connect setup before coding
- [Core Rules](#core-rules) - Essential guidelines for app generation
- [Generation Process](#generation-process) - Design reasoning and code output
- [Assembly Workflow](#assembly-workflow) - Build the final app
- [UI Style & Theming](#ui-style--theming) - OKLCH colors and design patterns
- [Fireproof API](#fireproof-api) - Database operations and hooks
- [AI Features](#ai-features-optional) - Optional AI integration
- [Common Mistakes](#common-mistakes-to-avoid) - Avoid these pitfalls
- [Deployment Options](#deployment-options) - Where to deploy
---
# Vibes DIY App Generator
Generate React web applications using Fireproof for local-first data persistence.
Pre-Flight Check: Connect Status
MANDATORY: Complete these steps BEFORE generating any app code.
Step 0: Check Connect Status
Run this command first to validate all required credentials:
```bash
if test -f "./.env" && \
grep -qE "^VITE_CLERK_PUBLISHABLE_KEY=pk_(test|live)_" ./.env 2>/dev/null && \
grep -qE "^VITE_API_URL=" ./.env 2>/dev/null && \
grep -qE "^VITE_CLOUD_URL=" ./.env 2>/dev/null; then
echo "CONNECT_READY"
else
echo "CONNECT_NOT_READY"
fi
```
If output is "CONNECT_NOT_READY", Connect setup is required. Inform the user:
> Connect with Clerk authentication is required for Vibes apps. Let me guide you through the setup.
Then proceed with Connect setup below.
Connect Setup Steps:
Step 1: Clone Fireproof Repository (if not already present)
```bash
# Check if repo exists
if [ ! -d "./fireproof" ]; then
git clone --branch selem/docker-for-all https://github.com/fireproof-storage/fireproof.git ./fireproof
fi
```
Step 2: Choose Credential Mode
Use AskUserQuestion:
```
Question: "How would you like to set up credentials?"
Header: "Credentials"
Options:
- Label: "Fresh credentials (Recommended)"
Description: "Generate all new session tokens and CA keys. Use this for new projects."
- Label: "Import from file"
Description: "Load credentials from a colleague's exported file. Use for team sharing."
- Label: "Quick local dev"
Description: "Use preset dev tokens. Fastest setup but not for production."
```
Step 3: Gather Clerk Keys
Collect credentials using AskUserQuestion. Users enter values via the "Other" option.
3a. Publishable Key:
```
Question: "Enter your Clerk Publishable Key (paste via 'Other')"
Header: "Publishable"
Options:
- Label: "I need to create a Clerk app first"
Description: "Go to clerk.com β Create application β Configure β API Keys"
```
After receiving via "Other", validate: must start with pk_test_ or pk_live_.
If invalid, inform the user and ask again.
3b. Secret Key:
```
Question: "Enter your Clerk Secret Key (paste via 'Other')"
Header: "Secret Key"
Options:
- Label: "Where do I find this?"
Description: "Clerk Dashboard β Configure β API Keys β Secret keys section"
```
After receiving via "Other", validate: must start with sk_test_ or sk_live_.
If invalid, inform the user and ask again.
3c. JWT URL (Auto-derived):
Extract the Clerk domain from the publishable key - no user input needed:
```javascript
// Publishable key format: pk_test_
const payload = publishableKey.replace(/^pk_(test|live)_/, '');
const domain = atob(payload).replace('$', '');
const jwtUrl = https://${domain}/.well-known/jwks.json;
```
Example: pk_test_aW50ZXJuYWwtZGluZ28tMjguY2xlcmsuYWNjb3VudHMuZGV2JA
β decodes to internal-dingo-28.clerk.accounts.dev
β JWT URL: https://internal-dingo-28.clerk.accounts.dev/.well-known/jwks.json
Tell the user what was derived so they can verify it looks correct.
Step 3b: Configure JWT Template
Instruct the user to set up a JWT template in Clerk:
> In your Clerk Dashboard, go to Configure β Sessions β JWT templates.
> Click "Add a new template" and paste this under Claims:
>
> ```json
> {
> "role": "authenticated",
> "params": {
> "last": "{{user.last_name}}",
> "name": "{{user.username}}",
> "email": "{{user.primary_email_address}}",
> "first": "{{user.first_name}}",
> "image_url": "{{user.image_url}}",
> "external_id": "{{user.external_id}}",
> "public_meta": "{{user.public_metadata}}",
> "email_verified": "{{user.email_verified}}"
> },
> "userId": "{{user.id}}"
> }
> ```
>
> Save the template. This ensures Fireproof receives the user info it needs for sync.
Step 4: Run Setup Script
```bash
# Fresh credentials (default)
node "${CLAUDE_PLUGIN_ROOT}/scripts/setup-connect.js" \
--clerk-publishable-key "pk_test_..." \
--clerk-secret-key "sk_test_..." \
--clerk-jwt-url "https://your-app.clerk.accounts.dev" \
--mode fresh
# Import from file
node "${CLAUDE_PLUGIN_ROOT}/scripts/setup-connect.js" \
--clerk-publishable-key "pk_test_..." \
--clerk-secret-key "sk_test_..." \
--clerk-jwt-url "https://your-app.clerk.accounts.dev" \
--mode import --import-file ./team-credentials.txt
# Quick dev (uses preset tokens)
node "${CLAUDE_PLUGIN_ROOT}/scripts/setup-connect.js" \
--clerk-publishable-key "pk_test_..." \
--clerk-secret-key "sk_test_..." \
--clerk-jwt-url "https://your-app.clerk.accounts.dev" \
--mode quick-dev
```
Step 5: Show Docker Instructions
After successful setup, tell the user:
```
Connect setup complete!
To start the Fireproof services:
cd fireproof/core && docker compose up --build
Services will be available at:
- Token API: http://localhost:8080/api/
- Cloud Sync: fpcloud://localhost:8080?protocol=ws
Your .env file has been created. Apps you generate will auto-detect Connect.
```
If Connect IS set up (CONNECT_READY), proceed directly to app generation. The assemble script will populate Connect config from .env.
Platform Name vs User Intent: "Vibes" is the name of this app platform (Vibes DIY). When users say "vibe" or "vibes" in their prompt, interpret it as:
- Their project/brand name ("my vibes tracker")
- A positive descriptor ("good vibes app")
- NOT as "mood/atmosphere" literally
Do not default to ambient mood generators, floating orbs, or meditation apps unless explicitly requested.
Import Map Note: The import map aliases use-fireproof to @necrodome/fireproof-clerk. Your code uses import { useFireproofClerk } from "use-fireproof" and the browser resolves this to the @necrodome/fireproof-clerk package, which provides Clerk authentication integration and cloud sync support. This is intentionalβit ensures compatible versions and enables auth when Connect is configured.
Core Rules
- Use JSX - Standard React syntax with Babel transpilation
- Single HTML file - App code assembled into template
- Fireproof for data - Use
useFireproofClerkfor database + sync - Auto-detect Connect - Template handles Clerk auth when Connect is configured
- Tailwind for styling - Mobile-first, responsive design
Generation Process
Step 1: Design Reasoning
Before writing code, reason about the design in tags:
```
- What is the core functionality and user flow?
- What OKLCH colors fit this theme? (dark/light, warm/cool, vibrant/muted)
- What layout best serves the content? (cards, list, dashboard, single-focus)
- What micro-interactions would feel satisfying? (hover states, transitions)
- What visual style matches the purpose? (minimal, bold, playful, professional)
```
Step 2: Output Code
After reasoning, output the complete JSX in tags:
```
import React, { useState } from "react";
import { useFireproofClerk } from "use-fireproof";
export default function App() {
const { database, useLiveQuery, useDocument, syncStatus } = useFireproofClerk("app-name-db");
// ... component logic
return (
{/ Sync status indicator (optional) /} {/ Your app UI /}
);
}
```
β οΈ CRITICAL: Fireproof Hook Pattern
The @necrodome/fireproof-clerk package exports ONLY useFireproofClerk. Always use this pattern:
```jsx
// β CORRECT - This is the ONLY pattern that works
import { useFireproofClerk } from "use-fireproof";
const { database, useDocument, useLiveQuery, syncStatus } = useFireproofClerk("my-db");
const { doc, merge } = useDocument({ _id: "doc1" });
// β WRONG - DO NOT USE (old use-vibes API)
import { toCloud, useFireproof } from "use-fireproof"; // WRONG - old API
import { useDocument } from "use-fireproof"; // WRONG - standalone import
const { attach } = useFireproof("db", { attach: toCloud() }); // WRONG - old pattern
```
Sync Status: syncStatus provides the current sync state as a string. Display it for user feedback.
Connect Configuration: Generated apps require Clerk authentication and cloud sync.
The assemble.js script populates window.__VIBES_CONFIG__ from your .env file.
Apps will show a configuration error if credentials are missing.
Assembly Workflow
- Extract the code from
tags and write toapp.jsx - Optionally save
content todesign.mdfor documentation - Run assembly:
```bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/assemble.js" app.jsx index.html
```
- Tell user: "Open
index.htmlin your browser to view your app."
---
> β οΈ DEPRECATED API: Never use the old useFireproof with toCloud() pattern. See [references/DEPRECATED.md](references/DEPRECATED.md) for migration details if you encounter legacy code.
---
UI Style & Theming
OKLCH Colors (Recommended)
Use OKLCH for predictable, vibrant colors. Unlike RGB/HSL, OKLCH has perceptual lightness - changing L by 10% looks the same across all hues.
```css
oklch(L C H)
/ L = Lightness (0-1): 0 black, 1 white /
/ C = Chroma (0-0.4): 0 gray, higher = more saturated /
/ H = Hue (0-360): color wheel degrees /
```
Theme-appropriate palettes:
```jsx
{/ Dark/moody theme /}
className="bg-[oklch(0.15_0.02_250)]" / Deep blue-black /
{/ Warm/cozy theme /}
className="bg-[oklch(0.25_0.08_30)]" / Warm brown /
{/ Fresh/bright theme /}
className="bg-[oklch(0.95_0.03_150)]" / Mint white /
{/ Vibrant accent /}
className="bg-[oklch(0.7_0.2_145)]" / Vivid green /
```
Better Gradients with OKLCH
Use in oklch for smooth gradients without muddy middle zones:
```jsx
{/ Smooth gradient - no gray middle /}
className="bg-[linear-gradient(in_oklch,oklch(0.6_0.2_250),oklch(0.6_0.2_150))]"
{/ Sunset gradient /}
className="bg-[linear-gradient(135deg_in_oklch,oklch(0.7_0.25_30),oklch(0.5_0.2_330))]"
{/ Dark glass effect /}
className="bg-[linear-gradient(180deg_in_oklch,oklch(0.2_0.05_270),oklch(0.1_0.02_250))]"
```
Neobrute Style (Optional)
For bold, graphic UI:
- Borders: thick 4px, dark
border-[#0f172a] - Shadows: hard offset
shadow-[6px_6px_0px_#0f172a] - Corners: square (0px) OR pill (rounded-full) - no in-between
```jsx
Click Me
```
Glass Morphism (Dark themes)
```jsx
{/ content /}
```
Color Modifications
Lighten/darken using L value:
- Hover: increase L by 0.05-0.1
- Active/pressed: decrease L by 0.05
- Disabled: reduce C to near 0
---
Fireproof API
Fireproof is a local-first database - no loading or error states required, just empty data states. Data persists across sessions and syncs in real-time when Connect is configured.
Setup
```jsx
import { useFireproofClerk } from "use-fireproof";
const { database, useLiveQuery, useDocument, syncStatus } = useFireproofClerk("my-app-db");
```
Note: When Connect is configured (via .env), the template wraps your App in ClerkFireproofProvider, enabling authenticated cloud sync automatically. Your code just uses useFireproofClerk.
Choosing Your Pattern
useDocument = Form-like editing. Accumulate changes with merge(), then save with submit() or save(). Best for: text inputs, multi-field forms, editing workflows.
database.put() + useLiveQuery = Immediate state changes. Each action writes directly. Best for: counters, toggles, buttons, any single-action updates.
```jsx
// FORM PATTERN: User types, then submits
const { doc, merge, submit } = useDocument({ title: "", body: "", type: "post" });
// merge({ title: "..." }) on each keystroke, submit() when done
// IMMEDIATE PATTERN: Each click is a complete action
const { docs } = useLiveQuery("_id", { key: "counter" });
const count = docs[0]?.value || 0;
const increment = () => database.put({ _id: "counter", value: count + 1 });
```
useDocument - Form State (NOT useState)
IMPORTANT: Don't use useState() for form data. Use merge() and submit() from useDocument. Only use useState for ephemeral UI state (active tabs, open/closed panels).
```jsx
// Create new documents (auto-generated _id recommended)
const { doc, merge, submit, reset } = useDocument({ text: "", type: "item" });
// Edit existing document by known _id
const { doc, merge, save } = useDocument({ _id: "user-profile:abc@example.com" });
// Methods:
// - merge(updates) - update fields: merge({ text: "new value" })
// - submit(e) - save + reset (for forms creating new items)
// - save() - save without reset (for editing existing items)
// - reset() - discard changes
```
useLiveQuery - Real-time Lists
```jsx
// Simple: query by field value
const { docs } = useLiveQuery("type", { key: "item" });
// Recent items (_id is roughly temporal - great for simple sorting)
const { docs } = useLiveQuery("_id", { descending: true, limit: 100 });
// Range query
const { docs } = useLiveQuery("rating", { range: [3, 5] });
```
CRITICAL: Custom index functions are SANDBOXED and CANNOT access external variables. Query all, filter in render:
```jsx
// GOOD: Query all, filter in render
const { docs: allItems } = useLiveQuery("type", { key: "item" });
const filtered = allItems.filter(d => d.category === selectedCategory);
```
Direct Database Operations
```jsx
// Create/update
const { id } = await database.put({ text: "hello", type: "item" });
// Delete
await database.del(item._id);
```
Common Pattern - Form + List
```jsx
import React from "react";
import { useFireproofClerk } from "use-fireproof";
export default function App() {
const { database, useLiveQuery, useDocument, syncStatus } = useFireproofClerk("my-db");
// Form for new items (submit resets for next entry)
const { doc, merge, submit } = useDocument({ text: "", type: "item" });
// Live list of all items of type "item"
const { docs } = useLiveQuery("type", { key: "item" });
return (
{/ Optional sync status indicator /} value={doc.text} onChange={(e) => merge({ text: e.target.value })} className="w-full px-4 py-3 border-4 border-[#0f172a]" /> Add {docs.map(item => ( {item.text} Delete ))}
);
}
```
---
AI Features (Optional)
If the user's prompt suggests AI-powered features (chatbot, summarization, content generation, etc.), the app needs AI capabilities via the useAI hook.
Detecting AI Requirements
Look for these patterns in the user's prompt:
- "chatbot", "chat with AI", "ask AI"
- "summarize", "generate", "write", "create content"
- "analyze", "classify", "recommend"
- "AI-powered", "intelligent", "smart" (in context of features)
Collecting OpenRouter Key
When AI is needed, ask the user:
> This app needs AI capabilities. Please provide your OpenRouter API key.
> Get one at: https://openrouter.ai/keys
Store the key for use with the --ai-key flag during deployment.
Using the useAI Hook
The useAI hook is automatically included in the template when AI features are detected:
```jsx
import React from "react";
import { useFireproofClerk } from "use-fireproof";
export default function App() {
const { database, useLiveQuery, syncStatus } = useFireproofClerk("ai-chat-db");
const { callAI, loading, error } = useAI();
const handleSend = async (message) => {
// Save user message
await database.put({ role: "user", content: message, type: "message" });
// Call AI
const response = await callAI({
model: "anthropic/claude-sonnet-4",
messages: [{ role: "user", content: message }]
});
// Save AI response
const aiMessage = response.choices[0].message.content;
await database.put({ role: "assistant", content: aiMessage, type: "message" });
};
// Handle limit exceeded
if (error?.code === 'LIMIT_EXCEEDED') {
return (
AI usage limit reached. Please wait for monthly reset or upgrade your plan.
);
}
// ... rest of UI
}
```
useAI API
```jsx
const { callAI, loading, error, clearError } = useAI();
// callAI options
await callAI({
model: "anthropic/claude-sonnet-4", // or other OpenRouter models
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Hello!" }
],
temperature: 0.7, // optional
max_tokens: 1000 // optional
});
// error structure
error = {
code: "LIMIT_EXCEEDED" | "API_ERROR" | "NETWORK_ERROR",
message: "Human-readable error message"
}
```
Deployment with AI
When deploying AI-enabled apps, include the OpenRouter key:
```bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/deploy-exe.js" \
--name myapp \
--file index.html \
--ai-key "sk-or-v1-your-key"
```
---
Common Mistakes to Avoid
- DON'T use
useStatefor form fields - useuseDocument - DON'T use
Fireproof.fireproof()- useuseFireproofClerk()hook - DON'T use the old
useFireproofwithtoCloud()- useuseFireproofClerkinstead - DON'T use white text on light backgrounds
- DON'T use
call-aidirectly - useuseAIhook instead (it handles proxying and limits)
---
Deployment Options
After generating your app, you can deploy it:
- exe.dev - VM hosting with nginx. Use
/vibes:exeto deploy. - Local - Just open
index.htmlin your browser. Works offline with Fireproof.
---
What's Next?
After generating and assembling the app, present these options using AskUserQuestion:
```
Question: "Your app is ready! What would you like to do next?"
Header: "Next"
Options:
- Label: "Keep improving this app"
Description: "Continue iterating on what you've built. Add new features, refine the styling, or adjust functionality. Great when you have a clear vision and want to polish it further."
- Label: "Apply a design reference (/design-reference)"
Description: "Have a design.html or mockup file? This skill mechanically transforms your app to match it exactly - pixel-perfect fidelity with your Fireproof data binding preserved."
- Label: "Explore variations (/riff)"
Description: "Not sure if this is the best approach? Riff generates 3-10 completely different interpretations of your idea in parallel. You'll get ranked variations with business model analysis to help you pick the winner."
- Label: "Make it a SaaS (/sell)"
Description: "Ready to monetize? Sell transforms your app into a multi-tenant SaaS with Clerk authentication, subscription billing, and isolated databases per customer. Each user gets their own subdomain."
- Label: "Deploy to exe.dev (/exe)"
Description: "Go live right now. Deploy creates a persistent VM at yourapp.exe.xyz with HTTPS, nginx, and Claude pre-installed for remote development. Your app stays online even when you close your laptop."
- Label: "I'm done for now"
Description: "Wrap up this session. Your files are saved locally - come back anytime to continue."
```
After user responds:
- "Keep improving" β Acknowledge and stay ready for iteration prompts
- "Apply a design reference" β Auto-invoke /vibes:design-reference skill
- "Explore variations" β Auto-invoke /vibes:riff skill
- "Make it a SaaS" β Auto-invoke /vibes:sell skill
- "Deploy" β Auto-invoke /vibes:exe skill
- "I'm done" β Confirm files saved, wish them well
Do NOT proceed to code generation until:
Connect setup is complete with valid Clerk credentials in .env (pre-flight check returns CONNECT_READY).
More from this repository7
Vibes is the vibe coding web stack made for AI agents.
Deploys Vibes apps to exe.dev VM hosting with automatic nginx setup, SSH automation, and multi-tenant subdomain isolation.
Transforms a Vibes app into a multi-tenant SaaS platform with subdomain-based tenancy, authentication, and subscription management.
Deploys a personal Fireproof Connect sync backend to a dedicated Studio VM on exe.dev, configuring cloud synchronization for Vibes apps.
Generates random music riffs and chord progressions using machine learning models for creative musical inspiration.
Generates design reference images by extracting and transforming visual elements from source images using AI-powered techniques.
Vibes is the vibe coding web stack made for AI agents.