Skip to content

vivek100/zuuma

Repository files navigation

Open‑source AI App Builder Template (Tambo + Freestyle)

This repository is a minimal, end‑to‑end AI app builder template combining:

  • Tambo.ai for agentic UI, tools, and “interactable” components the AI can render and control
  • Freestyle Sandboxes for on‑demand dev servers and Git repo management
Screenshot 2025-09-12 045110

It ships with a side‑by‑side Chat + App Viewer experience, dev‑server tooling (create/download), and a simple AI‑controlled To‑Do list component.

Get Started

  1. Run npm create-tambo@latest my-tambo-app for a new project (or clone this repo)

  2. npm install

  3. npx tambo init

  • or rename example.env.local to .env.local and add your Tambo API key from the Tambo dashboard
  1. Add your Freestyle API key to the server environment:
FREESTYLE_API_KEY=sk_... (server only)
NEXT_PUBLIC_TAMBO_API_KEY=pk_... (client OK)
NEXT_PUBLIC_TAMBO_URL=https://api.tambo.co (optional if default)
  1. Run npm run dev and go to http://localhost:3000

You’ll see a chat on the left and an App Viewer on the right. The App Viewer shows the ephemeral URL of a Freestyle dev server if one exists.


What’s Included

  • Chat thread built with Tambo’s providers and thread components
  • App Viewer side panel (interactable) that renders the live app URL and a toolbar
  • Tools to create a dev server, update/clear preview URL, and download repo zip
  • A simple, AI‑controlled To‑Do component (no backend state; the AI re-renders it with updates)

Key Features

App Viewer (Interactable)

  • File: src/components/tambo/AppViewer.tsx
  • Registered in: src/lib/tambo.ts (as a Tambo component)
  • Reads current { repoId, ephemeralUrl } from localStorage.freestyleDevServer via a dynamic context controller
  • Toolbar actions when a URL is present:
    • New App (create a new Freestyle dev server)
    • Download (zip of the current repoId)
    • Reload / Open / Copy URL
  • Empty state with a CTA to create a development sandbox

How the URL stays in sync

  • On successful create_dev_server, we persist { repoId, ephemeralUrl } into localStorage.freestyleDevServer and dispatch a devserver:updated event
  • AppViewer and the dynamic context helper react to this and update immediately

Tools the model can use to affect the viewer

  • set_app_viewer_url({ url, repoId? }) — persist a URL and (optionally) repo id
  • clear_app_viewer_url({}) — clear the URL (keeps repo id if present)

Download route

  • File: src/app/api/tambo-tools/devserver/download/route.ts
  • Uses Freestyle Git API:
    • GET https://api.freestyle.sh/git/v1/repo/{repo}/zip?ref=HEAD
    • Requires FREESTYLE_API_KEY

Dev Server tool (Freestyle)

  • File: src/lib/tambo-tools/devserver.ts
  • API route: src/app/api/tambo-tools/devserver/create/route.ts
  • Accepts { repoId?, gitUrl?, name? }
    • If no repoId, it creates/imports from gitUrl (or uses a default template)
    • Requests a dev server and returns { repoId, ephemeralUrl, success }
  • On success, the tool stores to localStorage.freestyleDevServer and dispatches devserver:updated

To‑Do component (AI‑controlled)

  • File: src/components/tambo/TodoList.tsx
  • Registered in: src/lib/tambo.ts
  • Props (zod schema):
    • title?: string
    • items: { text: string; completed?: boolean; id?: string }[]
    • showCounts?: boolean, dense?: boolean
  • There is no mutation API; the AI “marks complete” by re‑rendering TodoList with updated completed flags. This keeps the flow extremely simple and transparent.

Dynamic context helper

  • File: src/components/tambo/DevServerContextController.tsx
  • Registers a context helper at runtime using useTamboContextHelpers()
  • Publishes devServer data read from localStorage and listens to storage + devserver:updated

Filesystem & Process Tools

These tools let the agent modify files and run commands inside the Freestyle sandbox. They are registered in src/lib/tambo.ts and implemented in:

  • Filesystem tools: src/lib/tambo-tools/filesystem.ts

    • write_file({ repoId, path, content }) — Write content to an absolute path (e.g., /template/src/index.ts) creating directories if needed.
    • read_file({ repoId, path }) — Read text content from a file in the sandbox.
    • list_directory({ repoId, path }) — List file names in a directory.
    • edit_file({ repoId, path, edits: [{ oldText, newText, dryRun? }] }) — Apply find/replace style edits. Use dryRun to preview.
    • search_files({ repoId, pattern, path?, excludePatterns? }) — Grep-like search (regex supported) within a directory.
  • Process tools: src/lib/tambo-tools/process.ts

    • exec_command({ repoId, command, cwd?, background?, timeoutMs? }) — Run a command. Use background: true for long running tasks (e.g., npm run dev).
    • npm_install({ repoId }) — Install dependencies with npm in the sandbox.
    • git_commit_and_push({ repoId, message }) — Commit all changes and push to the repo’s remote.

Typical workflow:

  1. Create a sandbox with create_dev_server().
  2. Use write_file / edit_file to modify code under /template.
  3. npm_install then exec_command to build/run.
  4. Preview in App Viewer; iterate.

Project Structure

Key directories:

  • src/lib/ — Tambo registration (tambo.ts), tools (tambo-tools/*)
  • src/components/tambo/ — UI components for the chat, App Viewer, To‑Do list
  • src/app/api/tambo-tools/* — API routes used by tools/UI (devserver create/download)
  • src/app/ — Next.js app routes, global styles

Note: If you’re organizing a separate template app inside a template/ directory (monorepo pattern), you can still point your Freestyle import/create to that Git URL. This repo’s default sample deploys a Freestyle template when gitUrl is omitted.


Environment & Configuration

  • FREESTYLE_API_KEY — Server‑side key for Freestyle API (required for sandbox create/download)
  • NEXT_PUBLIC_TAMBO_API_KEY — Client‑side key for Tambo SDK
  • NEXT_PUBLIC_TAMBO_URL — Optional base URL for Tambo

Make sure FREESTYLE_API_KEY is not exposed on the client. The code uses API routes for server‑side calls.


Using the To‑Do Tool

Ask the agent to render a TodoList with items. The AI will include the component in its response. To “check off” items, the AI re-renders the same component with completed: true for the appropriate items.

Example props the AI might use:

{
  "title": "Project Setup",
  "items": [
    { "text": "Create dev server", "completed": true },
    { "text": "Open App Viewer", "completed": true },
    { "text": "Render TodoList", "completed": false }
  ],
  "dense": true
}

Interactables & Updating the Preview URL

  • The App Viewer is registered as a Tambo component and also listens to the runtime devServer context helper.
  • After a dev server is created via the tool, the preview URL is stored to localStorage and the viewer updates immediately.
  • You can also update or clear the URL directly via tools:
    • set_app_viewer_url({ url, repoId? })
    • clear_app_viewer_url({})

Agent Prompt (paste in your Tambo Dashboard)

Use the following prompt when creating your agent in the Tambo dashboard so it uses the tools/components effectively:

You are an AI App Builder agent working in a Next.js project integrated with Tambo and Freestyle.

Goals:
- Build and iterate on a web app while keeping the user in the loop via components you render.
- Use tools to manage sandboxes and preview URLs.
- Track work with a simple To‑Do list you re‑render as progress changes.

Capabilities:
1) Components you can render
   - AppViewer(url?) — shows a live app preview (ephemeral URL). If not provided, it reads the URL from context.
   - TodoList(title?, items[], dense?) — a simple list where you mark items complete by re‑rendering with updated props.

2) Tools you can call
   - create_dev_server({ repoId?, gitUrl?, name? }) ⇒ { repoId, ephemeralUrl, success }
     • Use this to spin up a Freestyle dev server. On success, the app persists the URL and updates the App Viewer.
   - set_app_viewer_url({ url, repoId? }) / clear_app_viewer_url({})
     • Use these to control the preview URL directly if needed.
   - Download code (handled by the UI via /api/tambo-tools/devserver/download?repoId=…)
   - Filesystem tools (operate on absolute paths like `/template/...`):
     • write_file({ repoId, path, content })
     • read_file({ repoId, path })
     • list_directory({ repoId, path })
     • edit_file({ repoId, path, edits: [{ oldText, newText, dryRun? }] })
     • search_files({ repoId, pattern, path?, excludePatterns? })
   - Process tools:
     • exec_command({ repoId, command, cwd?, background?, timeoutMs? })
     • npm_install({ repoId })
     • git_commit_and_push({ repoId, message })

3) Workflow guidance
   - Start by rendering a TodoList with a small plan (3–5 items).
   - If no preview exists, call create_dev_server with a gitUrl or let it use the default template.
   - After a server is created, show the AppViewer; it will update automatically.
   - Use filesystem/process tools to iteratively implement features under `/template`.
   - Re-render TodoList to mark items complete as you progress.

Principles:
- Keep components simple and legible.
- Prefer small, iterative steps; show your work.

Customizing

Customizing

Change what components tambo can control

You can see how components are registered with Tambo in src/lib/tambo.ts. Example (Graph shown here):

const components: TamboComponent[] = [
  {
    name: "Graph",
    description:
      "A component that renders various types of charts (bar, line, pie) using Recharts. Supports customizable data visualization with labels, datasets, and styling options.",
    component: Graph,
    propsSchema: z.object({
      data: z
        .object({
          type: z
            .enum(["bar", "line", "pie"])
            .describe("Type of graph to render"),
          labels: z.array(z.string()).describe("Labels for the graph"),
          datasets: z
            .array(
              z.object({
                label: z.string().describe("Label for the dataset"),
                data: z
                  .array(z.number())
                  .describe("Data points for the dataset"),
                color: z
                  .string()
                  .optional()
                  .describe("Optional color for the dataset"),
              }),
            )
            .describe("Data for the graph"),
        })
        .describe("Data object containing chart configuration and values"),
      title: z.string().optional().describe("Optional title for the chart"),
      showLegend: z
        .boolean()
        .optional()
        .describe("Whether to show the legend (default: true)"),
      variant: z
        .enum(["default", "solid", "bordered"])
        .optional()
        .describe("Visual style variant of the graph"),
      size: z
        .enum(["default", "sm", "lg"])
        .optional()
        .describe("Size of the graph"),
    }),
  },
  // Add more components for Tambo to control here!
];

You can install this graph component into any project with:

npx tambo add graph

The example Graph component demonstrates several key features:

  • Different prop types (strings, arrays, enums, nested objects)
  • Multiple chart types (bar, line, pie)
  • Customizable styling (variants, sizes)
  • Optional configurations (title, legend, colors)
  • Data visualization capabilities

Update the components array with any component(s) you want tambo to be able to use in a response!

You can find more information about the options here

Add tools for tambo to use

export const tools: TamboTool[] = [
  {
    name: "globalPopulation",
    description:
      "A tool to get global population trends with optional year range filtering",
    tool: getGlobalPopulationTrend,
    toolSchema: z.function().args(
      z
        .object({
          startYear: z.number().optional(),
          endYear: z.number().optional(),
        })
        .optional(),
    ),
  },
];

Find more information about tools here.

The Magic of Tambo Requires the TamboProvider

Make sure in the TamboProvider wrapped around your app:

...
<TamboProvider
  apiKey={process.env.NEXT_PUBLIC_TAMBO_API_KEY!}
  components={components} // Array of components to control
  tools={tools} // Array of tools it can use
>
  {children}
</TamboProvider>

In this template it’s set up in the App, but you can do it anywhere in your app that is a client component.

Change where component responses are shown

The components used by tambo are shown alongside the message resopnse from tambo within the chat thread, but you can have the result components show wherever you like by accessing the latest thread message's renderedComponent field:

const { thread } = useTambo();
const latestComponent =
  thread?.messages[thread.messages.length - 1]?.renderedComponent;

return (
  <div>
    {latestComponent && (
      <div className="my-custom-wrapper">{latestComponent}</div>
    )}
  </div>
);

For more detailed documentation, visit Tambo's official docs and Freestyle docs (API reference: https://api.freestyle.sh/).

About

Opensource AI App builder

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 6