# AI Agent Source: https://docs.tinykit.studio/agent Prompting guide for the tinykit AI Agent. The AI Agent acts as an interface to the tinykit codebase. It interprets natural language prompts to modify app code, manage configurations, and handle database interactions. AI Agent in action ## Mechanics When a prompt is submitted: 1. **Context Loading**: The agent ingests the current `Code`, design system states, content fields, and database collections. 2. **Tool selection**: It determines which tools to execute (e.g., `write_code`, `create_collection`). 3. **Execution**: Changes are applied to the project. 4. **Refresh**: The preview pane updates. ## Prompting Strategy Describe **what** the feature should do (behavior), rather than **how** to write the code (implementation). * **Vague**: "Make the list better." * **Implementation-focused**: "Create a loop using `.map()` to render `li` elements with class `item`." * **Behavior-focused**: "Display a list of episodes where each item links to its details page." ## Tools The agent has access to the following server-side tools: | Tool | Function | | :--------------------- | :--------------------------------- | | `write_code` | Overwrites app code with new code. | | `create_content_field` | Defines editable content fields. | | `create_design_field` | Defines CSS variables. | | `create_data_file` | Creates data collections. | | `insert_records` | Seeds data into collections. | | `update_spec` | Updates project metadata. | ## Limitations * **Single File**: Modifications are restricted to a single `Svelte` component file. * **CSS**: Uses semantic classes and CSS variables, not utility frameworks like Tailwind (though you can use them by prompting the agent or manually including them). * **Runtime**: Cannot execute arbitrary shell commands or write backend logic besides `proxy` (yet). * **Database**: At the moment, collections under the 'Data' are all stored within a single Pocketbase JSON field. In a future update, these will be replaced with actual Pocketbase collections. # Architecture Source: https://docs.tinykit.studio/architecture How tinykit works under the hood ## One Server Philosophy **One server. One deployment. Zero complexity.** Unlike other platforms where you build locally and deploy separately, tinykit runs the builder AND your app on the same server. Edit at `/tinykit`, ship at `/`. *** ## URL Structure Your tinykit instance serves everything from a single domain: ``` tinykit Server (Railway) ├── /tinykit/dashboard → Project list (you) ├── /tinykit/studio → Edit current domain's app (you) ├── / → Your generated app (users) ├── /api/agent → AI endpoints ├── /api/projects → Project operations └── /_pb/_ → PocketBase admin ``` | Path | Purpose | Who Uses It | | ---------------------- | --------------------------- | --------------- | | `/` | Your generated app | End users | | `/tinykit/dashboard` | List all projects | You (developer) | | `/tinykit/studio` | Edit app for current domain | You (developer) | | `/tinykit/studio?id=X` | Edit specific project | You (developer) | | `/api/agent` | AI code generation | Builder | | `/_pb/_` | PocketBase admin | You (optional) | *** ## Domain-Based Routing Run hundreds of apps from one tinykit instance. Each domain serves a different app: ``` calculator.myserver.com/ → Serves calculator app calculator.myserver.com/tinykit → Edit calculator app blog.myserver.com/ → Serves blog app blog.myserver.com/tinykit → Edit blog app recipes.myserver.com/ → Serves recipes app recipes.myserver.com/tinykit → Edit recipes app ``` **How it works:** 1. Point multiple domains to your tinykit server 2. Each domain is associated with a project in PocketBase 3. Root URL (`/`) serves the pre-built HTML for that domain's project 4. `/tinykit` lets you edit the project for the current domain **Unknown domains** redirect to `/tinykit/new?domain=X` to create a new project. *** ## Tech Stack Fast, modern framework for the builder and generated apps Modern code editor with syntax highlighting and autocomplete Your choice of AI provider for code generation Embedded database for data persistence Utility-first styling for rapid UI development Type safety throughout the codebase *** ## Data Storage All project data is stored in a single PocketBase collection (`_tk_projects`): ``` _tk_projects ├── frontend_code → Code (your app's code) ├── content → Content fields (JSON) ├── design → CSS variables (JSON) ├── agent_chat → Conversation history (JSON) ├── snapshots → Time travel history (JSON) ├── data → App data collections (JSON) ├── domain → Associated domain └── published_html → Compiled HTML (production build) ``` Everything in one collection means simple backups and easy migrations. *** ## Data Flow Send a prompt like "Create a todo list app" to the AI Agent panel. The AI responds with code, which streams to your browser in real-time. Generated code is saved to the project's `frontend_code` field. The live preview compiles and shows your changes immediately. Server compiles Svelte to standalone HTML, saves as static file. Anyone visiting your root URL sees the updated app instantly. *** ## Build System **Preview** uses in-browser Svelte compilation for instant feedback. **Production** uses server-side compilation: * Svelte 5's native `compile()` function * Generates standalone HTML with CDN-based imports * Triggered by clicking "Deploy" button * Result stored as file attachment in PocketBase *** ## AI Agent System The AI agent uses a **tool-use pattern** to build your app: ``` You: "Add a contact form" │ ▼ ┌─────────────────────────────┐ │ LLM receives: │ │ - Your prompt │ │ - Current code │ │ - Design/content fields │ │ - Conversation history │ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ LLM calls tools: │ │ - write_code │ │ - create_design_field │ │ - create_content_field │ │ - create_data_file │ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ Tools execute │ │ Results fed back │ │ Loop until done │ └─────────────────────────────┘ │ ▼ Response streamed to you ``` ### Available Tools | Tool | What It Does | | ---------------------- | ---------------------------------- | | `write_code` | Updates app code | | `create_design_field` | Adds CSS variables (colors, fonts) | | `create_content_field` | Adds CMS fields (text, images) | | `create_data_file` | Creates data collections | | `insert_records` | Seeds data into collections | | `update_spec` | Updates project metadata | *** ## Performance | Operation | Typical Time | | ---------------- | ------------------------- | | Editor load | \~100-200ms | | File save | \~50-100ms | | Preview refresh | \~100-200ms | | AI response | \~2-5s (depends on model) | | Production build | \~500ms-2s | *** ## Deployment Options While Railway is recommended for simplicity, you can deploy anywhere that runs Node.js: One-click deploy with the template. Automatic HTTPS, custom domains, and scaling. [![Deploy on Railway](https://railway.app/button.svg)](https://railway.com/deploy/tinykit?referralCode=RCPU7k\&utm_medium=integration\&utm_source=template\&utm_campaign=generic) ```bash theme={null} fly launch fly secrets set LLM_API_KEY=sk-... fly deploy ``` Connect your GitHub repo and configure environment variables in the dashboard. ```bash theme={null} git clone https://github.com/tinykit-studio/tinykit.git cd tinykit npm install npm run build npm run preview ``` Use PM2 or systemd to keep it running. ```dockerfile theme={null} FROM node:20-alpine WORKDIR /app COPY . . RUN npm install && npm run build CMD ["npm", "run", "preview"] ``` # Configuration Source: https://docs.tinykit.studio/configuration Environment variables and settings reference Configure tinykit using environment variables. All settings can be configured via a `.env` file or passed directly to your deployment platform. ## Quick Reference | Variable | Required | Default | Description | | --------------------------- | -------- | --------- | ------------------------- | | `LLM_PROVIDER` | No\* | - | AI provider to use | | `LLM_API_KEY` | No\* | - | API key for your provider | | `LLM_MODEL` | No | varies | Model name | | `LLM_BASE_URL` | No | - | Custom API endpoint | | `POCKETBASE_ADMIN_EMAIL` | Yes | - | Database admin email | | `POCKETBASE_ADMIN_PASSWORD` | Yes | - | Database admin password | | `PORT` | No | `3000` | Server port | | `HOST` | No | `0.0.0.0` | Server host | \*Can also be configured via `/tinykit/settings` UI after deployment. *** ## AI Provider Settings ### LLM\_PROVIDER The AI provider to use for the agent. Options: | Provider | Value | Description | | --------- | ----------- | --------------------------- | | Anthropic | `anthropic` | Claude models (recommended) | | OpenAI | `openai` | GPT-4 and GPT-3.5 models | | Google | `gemini` | Gemini models | ```bash theme={null} LLM_PROVIDER=anthropic ``` ### LLM\_API\_KEY Your API key from the provider. Get yours from: * **Anthropic**: [console.anthropic.com](https://console.anthropic.com/) * **OpenAI**: [platform.openai.com/api-keys](https://platform.openai.com/api-keys) * **Google**: [aistudio.google.com/apikey](https://aistudio.google.com/apikey) ```bash theme={null} LLM_API_KEY=sk-ant-api03-... ``` Never commit API keys to version control. Use environment variables or secrets management. ### LLM\_MODEL The specific model to use. Defaults vary by provider: | Provider | Default | Recommended | | --------- | -------------------------- | -------------------------- | | Anthropic | `claude-sonnet-4-20250514` | `claude-sonnet-4-20250514` | | OpenAI | `gpt-4o` | `gpt-4o` | | Gemini | `gemini-2.0-flash` | `gemini-2.0-flash` | ```bash theme={null} LLM_MODEL=claude-sonnet-4-20250514 ``` ### LLM\_BASE\_URL Custom API endpoint for OpenAI-compatible providers. Only needed for: * Self-hosted models * Other OpenAI-compatible APIs ```bash theme={null} # For local OpenAI-compatible server LLM_BASE_URL=http://localhost:8080/v1 ``` *** ## Database Settings ### POCKETBASE\_ADMIN\_EMAIL Email address for the Pocketbase admin account. Used to initialize the database on first run. ```bash theme={null} POCKETBASE_ADMIN_EMAIL=admin@yourdomain.com ``` ### POCKETBASE\_ADMIN\_PASSWORD Password for the Pocketbase admin account. Must be at least 8 characters. ```bash theme={null} POCKETBASE_ADMIN_PASSWORD=your-secure-password ``` Use a strong, unique password. This account has full access to your database. ### POCKETBASE\_URL Optional. URL of an external Pocketbase instance. By default, tinykit runs its own embedded Pocketbase. ```bash theme={null} # Only set if using external Pocketbase POCKETBASE_URL=http://127.0.0.1:8091 ``` *** ## Server Settings ### PORT The port the server listens on. ```bash theme={null} PORT=3000 ``` * **Default**: `3000` (production), `5173` (development) * Railway and similar platforms set this automatically ### HOST The host address to bind to. ```bash theme={null} HOST=0.0.0.0 ``` * **Default**: `0.0.0.0` (all interfaces) * Use `127.0.0.1` to restrict to localhost only *** ## Provider Examples ### Anthropic (Claude) ```bash theme={null} LLM_PROVIDER=anthropic LLM_API_KEY=sk-ant-api03-... LLM_MODEL=claude-sonnet-4-20250514 ``` ### OpenAI (GPT-4) ```bash theme={null} LLM_PROVIDER=openai LLM_API_KEY=sk-... LLM_MODEL=gpt-4 ``` ### Google (Gemini) ```bash theme={null} LLM_PROVIDER=gemini LLM_API_KEY=your-gemini-api-key LLM_MODEL=gemini-pro ``` *** ## Complete Example Here's a complete `.env` file for production: ```bash theme={null} # AI Configuration LLM_PROVIDER=anthropic LLM_API_KEY=sk-ant-api03-... LLM_MODEL=claude-sonnet-4-20250514 # Database POCKETBASE_ADMIN_EMAIL=admin@yourdomain.com POCKETBASE_ADMIN_PASSWORD=your-secure-password-here # Server (usually set by platform) PORT=3000 HOST=0.0.0.0 ``` *** ## Platform-Specific Notes ### Railway Railway automatically sets `PORT`. Configure other variables in the Railway dashboard under **Variables**. ### Docker Pass variables with `-e` flags or use `--env-file`: ```bash theme={null} docker run -d \ -e LLM_PROVIDER=anthropic \ -e LLM_API_KEY=sk-ant-... \ -e POCKETBASE_ADMIN_EMAIL=admin@example.com \ -e POCKETBASE_ADMIN_PASSWORD=password \ tinykit ``` ### VPS / PM2 Use a `.env` file in the project root, or configure in `ecosystem.config.js`: ```javascript theme={null} module.exports = { apps: [{ name: 'tinykit', script: './start.sh', env: { LLM_PROVIDER: 'anthropic', LLM_API_KEY: 'sk-ant-...', // ... } }] } ``` *** ## Troubleshooting Check these in order: 1. `LLM_API_KEY` is set and valid 2. `LLM_PROVIDER` matches your key (e.g., Anthropic key with `anthropic` provider) 3. Your account has credits/quota remaining 4. Check logs for specific error messages Verify: 1. `POCKETBASE_ADMIN_EMAIL` is a valid email format 2. `POCKETBASE_ADMIN_PASSWORD` is at least 8 characters 3. The `pocketbase/pb_data` directory is writable Check: 1. `PORT` isn't already in use (`lsof -i :3000`) 2. `HOST` is set correctly for your environment 3. Firewall allows traffic on the port Environment variables are read at startup. After changing `.env`: * **Development**: Restart `npm run dev` * **PM2**: Run `pm2 restart tinykit` * **Docker**: Recreate the container * **systemd**: Run `sudo systemctl restart tinykit` # Content Fields Source: https://docs.tinykit.studio/content-fields CMS-like editable text for non-developers Content fields let non-technical users edit text without touching code. Headlines, descriptions, button labels—anything that might need updating can be a content field. Content fields panel ## How It Works 1. The AI (or you) creates a content field with a name and value 2. The field appears in the **Content tab** with an editable input 3. Your code references the field via `import content from '$content'` 4. When someone edits the field, the preview updates instantly ``` Content Tab Your Code ┌─────────────────────┐ ┌─────────────────────┐ │ Hero Title │ │

│ │ ┌─────────────────┐ │ → │ {content.hero_ │ │ │ Welcome to App │ │ │ title} │ │ └─────────────────┘ │ │

│ └─────────────────────┘ └─────────────────────┘ ``` *** ## Using Content Fields ### In Your Code ```svelte theme={null}

{content.hero_title}

{content.hero_description}

``` ### Name Conversion Field names are converted to snake\_case: | Field Name | Access As | | ---------------- | -------------------------- | | Hero Title | `content.hero_title` | | CTA Button Text | `content.cta_button_text` | | Footer Copyright | `content.footer_copyright` | | App Name | `content.app_name` | When you hover over a field in the Content tab, you'll see the exact key to use in your code. ### Default Values Always provide fallbacks for robustness: ```svelte theme={null}

{content.hero_title || 'Welcome'}

{content.tagline || 'Your default tagline here'}

``` *** ## Field Types | Type | Input | Best For | | ------------ | ---------------- | --------------------------- | | **text** | Single line | Titles, labels, short text | | **textarea** | Multi-line | Descriptions, paragraphs | | **number** | Numeric input | Prices, quantities, limits | | **boolean** | Toggle switch | Feature flags, visibility | | **image** | Image upload | Logos, photos, icons | | **markdown** | Rich text editor | Formatted content, articles | ### Text Fields Single line text for titles, labels, and short content: ```svelte theme={null}

{content.page_title}

``` ### Textarea Fields Multi-line text for longer content: ```svelte theme={null}

{content.about_text}

{content.author_bio}
``` ### Number Fields Numeric values that can be used in calculations: ```svelte theme={null}

Starting at ${content.base_price}

Max items: {content.max_items}

``` ### Boolean Fields Toggle switches for on/off settings: ```svelte theme={null} {#if content.show_banner} {/if} {#if content.enable_dark_mode} ... {/if} ``` ### Image Fields Image uploads with automatic URL handling: ```svelte theme={null} Hero Company logo ``` Image fields automatically convert uploaded filenames to full asset URLs. ### Markdown Fields Rich text content with markdown formatting: ```svelte theme={null}
{@html content.article_body}
``` Markdown fields are rendered as HTML. Only use `{@html}` with trusted content to avoid XSS risks. *** ## Creating Content Fields ### Via the AI Ask the AI to create content fields: ``` Add a hero section with a title and description that I can edit without code ``` The AI will: 1. Create content fields for the text 2. Reference them in the code 3. Fields appear in the Content tab ### Via the Content Tab 1. Click **Add Content Field** at the bottom of the Content tab 2. Enter a name (e.g., "Hero Title") 3. Choose a type 4. Set an initial value 5. Optionally add a description 6. Click **Add Field** ### In Code (Advanced) If you're editing code manually, first use the text in your code: ```svelte theme={null}

{content.new_headline}

``` Then ask the AI to create the matching field, or add it in the Content tab. *** ## Best Practices ### Use Descriptive Names * Hero Title * Navigation Home Link * Footer Copyright Year * Empty Cart Message * Title * Text 1 * String * Foo ### Group Related Content Use consistent prefixes for related fields: ``` hero_title hero_description hero_cta_text footer_copyright footer_company_name footer_email ``` ### Provide Context with Descriptions When creating fields, add descriptions to help editors: ``` Name: Hero Title Description: Main headline on the home page (keep under 50 characters) Name: CTA Button Description: Call-to-action button text (e.g., "Get Started", "Try Free") ``` ### Handle Missing Values Always provide fallbacks in case a field hasn't been created yet: ```svelte theme={null}

{content.title || 'Default Title'}

{content.description || ''}

``` *** ## Common Patterns ### Conditional Content ```svelte theme={null} {#if content.announcement_text}
{content.announcement_text}
{/if} ``` ### Lists from JSON ```svelte theme={null} ``` ### Dynamic Placeholders ```svelte theme={null} ``` ### Content with Design Combine content fields with design fields: ```svelte theme={null}

{content.hero_title}

``` *** ## Sharing the Content Tab Content fields are designed for non-developers to edit. You can share access to just the Content tab without exposing the full builder: A dedicated content-only editing view is planned for a future release. For now, editors access the full builder but only need to use the Content tab. Consider creating a guide for your content editors: 1. Log in at `yourapp.com/tinykit` 2. Click the **Content** tab 3. Edit values and see changes in the preview 4. Changes save automatically *** ## FAQ Not directly in the UI. Delete the old field and create a new one with the correct name. Remember to update your code to use the new key. The code will show `undefined` or your fallback value. Always use fallbacks like `{content.title || 'Default'}`. Content fields are plain text by default. For HTML, use `{@html content.rich_text}`, but be careful with user-provided content (XSS risk). tinykit doesn't enforce required fields. Use fallback values in your code to handle missing content gracefully. # Data & Content Source: https://docs.tinykit.studio/data-fetching Working with data, content fields, and external APIs in tinykit tinykit provides three special imports for handling data in your apps: | Import | Purpose | | ---------- | ------------------------------------------ | | `$data` | Database collections with realtime updates | | `$content` | Editable CMS text fields | | `$tinykit` | Proxy for fetching external APIs | *** ## Database (\$data) Store and retrieve data with realtime subscriptions. Data is stored in Pocketbase and syncs automatically across all connected clients. ### Basic Usage ```svelte theme={null} {#if loading}

Loading...

{:else} {#each todos as todo (todo.id)}

{todo.title}

{/each} {/if} ``` ### CRUD Operations ```javascript theme={null} // Create a new record await data.todos.create({ title: 'Buy groceries', completed: false }) // Update a record by ID await data.todos.update('abc123', { completed: true }) // Delete a record by ID await data.todos.delete('abc123') ``` All operations trigger realtime updates—any subscribed component will update automatically. ### Complete Example ```svelte theme={null} {#each todos as todo (todo.id)}
toggle_todo(todo)} /> {todo.title}
{/each} ``` ### Creating Collections The AI creates collections when you ask for data storage. You can also create them in the Data tab or ask the AI directly: ``` Add a collection for storing recipes with title, ingredients, and instructions ``` *** ## Content Fields (\$content) Editable text values that non-developers can change without touching code. Perfect for headlines, descriptions, and labels. ### Basic Usage ```svelte theme={null}

{content.hero_title}

{content.hero_description}

``` Content field names are automatically converted to snake\_case: * "Hero Title" → `content.hero_title` * "CTA Button Text" → `content.cta_button_text` ### Default Values Always provide fallbacks for robustness: ```svelte theme={null}

{content.hero_title || 'Welcome'}

``` ### Field Types | Type | Description | Example Value | | ---------- | ---------------------------- | --------------------------- | | `text` | Single line text | `"Welcome to My App"` | | `textarea` | Multi-line text | `"A longer description..."` | | `number` | Numeric value | `42` | | `boolean` | True/false toggle | `true` | | `image` | Image upload | `"/path/to/image.png"` | | `markdown` | Rich text (rendered as HTML) | `"# Heading\n\nParagraph"` | ### Creating Content Fields The AI creates content fields when it builds your app. You can also: 1. **Ask the AI**: "Add a content field for the footer copyright text" 2. **Use the Content tab**: Add fields directly in the builder 3. **Edit values**: Change text in the Content tab without touching code *** ## External APIs (\$tinykit) Fetch data from external APIs without CORS issues. The proxy routes requests through your server. ### Fetching JSON ```svelte theme={null} ``` ### Fetching Text (RSS, HTML, XML) ```svelte theme={null} ``` ### Media URLs For audio, images, and other media that need a direct URL: ```svelte theme={null}