├── LICENSE ├── biome.json ├── templates └── chat │ ├── biome.json │ ├── postcss.config.mjs │ ├── src │ ├── app │ │ ├── api │ │ │ ├── events │ │ │ │ └── route.ts │ │ │ └── auth │ │ │ │ └── [...auth] │ │ │ │ └── route.ts │ │ ├── twitter-image.tsx │ │ ├── (app) │ │ │ ├── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── ai.tsx │ │ │ └── chat.tsx │ │ ├── styles.css │ │ ├── (landing) │ │ │ └── signin │ │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── icon.tsx │ │ └── opengraph-image.tsx │ └── lib │ │ ├── db.ts │ │ ├── auth │ │ ├── actions.ts │ │ ├── client.ts │ │ └── server.ts │ │ ├── events │ │ ├── types.ts │ │ ├── client.ts │ │ └── server.ts │ │ ├── components │ │ ├── SignIn.tsx │ │ ├── SignOut.tsx │ │ ├── Nav.tsx │ │ ├── ChatBox.tsx │ │ └── Message.tsx │ │ ├── tools │ │ ├── createTodo.ts │ │ └── getTodoList.ts │ │ ├── env.ts │ │ └── agents │ │ └── todo.ts │ ├── public │ └── fonts │ │ └── PlusJakartaSans-Bold.ttf │ ├── .env.example │ ├── prisma.config.ts │ ├── prisma │ ├── seed.ts │ ├── schema.prisma │ └── auth.prisma │ ├── next-env.d.ts │ ├── next.config.ts │ ├── tsconfig.json │ ├── .gitignore │ ├── .vscode │ └── settings.json │ ├── README.md │ └── package.json ├── .github └── workflows │ ├── publish.yml │ └── notify-monorepo.yml ├── README.md ├── .gitignore ├── .vscode └── settings.json ├── package.json ├── index.ts └── CHANGELOG.md /LICENSE: -------------------------------------------------------------------------------- 1 | go nuts -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@rubriclab/config/biome"] 3 | } 4 | -------------------------------------------------------------------------------- /templates/chat/biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@rubriclab/config/biome"] 3 | } 4 | -------------------------------------------------------------------------------- /templates/chat/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@rubriclab/config/postcss' 2 | -------------------------------------------------------------------------------- /templates/chat/src/app/api/events/route.ts: -------------------------------------------------------------------------------- 1 | export { GET, maxDuration } from '~/events/server' 2 | -------------------------------------------------------------------------------- /templates/chat/src/app/twitter-image.tsx: -------------------------------------------------------------------------------- 1 | export { alt, contentType, default, size } from './opengraph-image' 2 | -------------------------------------------------------------------------------- /templates/chat/src/lib/db.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client' 2 | 3 | export default new PrismaClient() 4 | -------------------------------------------------------------------------------- /templates/chat/src/app/api/auth/[...auth]/route.ts: -------------------------------------------------------------------------------- 1 | import { routes } from '~/auth/server' 2 | 3 | export const { GET } = routes 4 | -------------------------------------------------------------------------------- /templates/chat/src/app/(app)/page.tsx: -------------------------------------------------------------------------------- 1 | import Chat from './chat' 2 | 3 | export default function Page() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /templates/chat/src/app/styles.css: -------------------------------------------------------------------------------- 1 | /** biome-ignore-all lint/suspicious/noUnknownAtRules: Tailwind Grammar */ 2 | 3 | @import "tailwindcss"; 4 | -------------------------------------------------------------------------------- /templates/chat/public/fonts/PlusJakartaSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RubricLab/create-rubric-app/HEAD/templates/chat/public/fonts/PlusJakartaSans-Bold.ttf -------------------------------------------------------------------------------- /templates/chat/.env.example: -------------------------------------------------------------------------------- 1 | DATABASE_URL= 2 | GITHUB_CLIENT_ID= 3 | GITHUB_CLIENT_SECRET= 4 | NEXT_PUBLIC_AUTH_URL='http://localhost:3000/api' 5 | OPENAI_API_KEY= 6 | UPSTASH_REDIS_URL= -------------------------------------------------------------------------------- /templates/chat/src/lib/auth/actions.ts: -------------------------------------------------------------------------------- 1 | 'use server' 2 | 3 | import { actions } from './server' 4 | 5 | export const { signIn, signOut, sendMagicLink, getAuthConstants, getSession } = actions 6 | -------------------------------------------------------------------------------- /templates/chat/src/app/(landing)/signin/page.tsx: -------------------------------------------------------------------------------- 1 | import { SignInWithGithubButton } from '~/components/SignIn' 2 | 3 | export default function SignInPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /templates/chat/src/lib/events/types.ts: -------------------------------------------------------------------------------- 1 | import { createEventTypes } from '@rubriclab/events' 2 | import { todoAgentEventTypes } from '~/agents/todo' 3 | 4 | export const eventTypes = createEventTypes({ 5 | ...todoAgentEventTypes 6 | }) 7 | -------------------------------------------------------------------------------- /templates/chat/prisma.config.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config' 2 | import { defineConfig } from 'prisma/config' 3 | 4 | export default defineConfig({ 5 | migrations: { 6 | seed: 'bun run prisma/seed.ts' 7 | }, 8 | schema: 'prisma' 9 | }) 10 | -------------------------------------------------------------------------------- /templates/chat/src/lib/events/client.ts: -------------------------------------------------------------------------------- 1 | import { createEventsClient } from '@rubriclab/events/client' 2 | import { eventTypes } from '~/events/types' 3 | 4 | export const { useEvents } = createEventsClient({ 5 | eventTypes, 6 | url: '/api/events' 7 | }) 8 | -------------------------------------------------------------------------------- /templates/chat/prisma/seed.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bun 2 | 3 | import db from '~/db' 4 | 5 | async function seed() { 6 | await db.task.create({ 7 | data: { 8 | status: false, 9 | title: 'Create your first task' 10 | } 11 | }) 12 | } 13 | 14 | seed() 15 | -------------------------------------------------------------------------------- /templates/chat/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | import "./.next/dev/types/routes.d.ts"; 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 7 | -------------------------------------------------------------------------------- /templates/chat/src/lib/events/server.ts: -------------------------------------------------------------------------------- 1 | import { createEventsServer } from '@rubriclab/events/server' 2 | import env from '~/env' 3 | import { eventTypes } from '~/events/types' 4 | 5 | export const { publish, GET, maxDuration } = createEventsServer({ 6 | eventTypes, 7 | redisURL: env.REDIS_URL 8 | }) 9 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Release Package 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | permissions: 9 | contents: read 10 | packages: write 11 | id-token: write 12 | 13 | jobs: 14 | release: 15 | uses: rubriclab/package/.github/workflows/release-package.yml@main 16 | secrets: inherit 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Create Rubric App 2 | 3 | The fastest, safest way to bootstrap an AI chat app. 4 | 5 | ```sh 6 | bun x create-rubric-app@latest init 7 | ``` 8 | 9 | With: 10 | 11 | - 🔒 Auth 12 | - 🛠️ Type-safe tool-calling and response shaping 13 | - 💬 Type-safe event streaming 14 | - 💾 Persistence 15 | - 🎨 Default styling 16 | -------------------------------------------------------------------------------- /templates/chat/src/lib/components/SignIn.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { signIn } from '~/auth/actions' 4 | 5 | export function SignInWithGithubButton() { 6 | return ( 7 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /templates/chat/src/lib/components/SignOut.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { signOut } from '~/auth/actions' 4 | 5 | export function SignOutButton() { 6 | return ( 7 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /templates/chat/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import './styles.css' 2 | 3 | export const metadata = { 4 | description: 'Generated by Create Rubric App', 5 | title: 'Create Rubric App' 6 | } 7 | 8 | export default function RootLayout({ children }: { children: React.ReactNode }) { 9 | return ( 10 | 11 | {children} 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /templates/chat/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from 'next' 2 | 3 | export default { 4 | reactStrictMode: true, 5 | transpilePackages: [ 6 | '@rubriclab/actions', 7 | '@rubriclab/agents', 8 | '@rubriclab/auth', 9 | '@rubriclab/blocks', 10 | '@rubriclab/chains', 11 | '@rubriclab/events', 12 | '@rubriclab/shapes', 13 | '@rubriclab/webhooks' 14 | ] 15 | } satisfies NextConfig 16 | -------------------------------------------------------------------------------- /templates/chat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "~/*": [ 6 | "./src/lib/*" 7 | ] 8 | }, 9 | "plugins": [ 10 | { 11 | "name": "next" 12 | } 13 | ] 14 | }, 15 | "exclude": [ 16 | "node_modules" 17 | ], 18 | "extends": "@rubriclab/config/tsconfig", 19 | "include": [ 20 | "**/*.ts", 21 | "**/*.tsx", 22 | ".next/types/**/*.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /templates/chat/prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | generator client { 2 | provider = "prisma-client-js" 3 | } 4 | 5 | datasource db { 6 | provider = "postgresql" 7 | url = env("DATABASE_URL") 8 | } 9 | 10 | model Task { 11 | id Int @id @default(autoincrement()) 12 | title String 13 | createdAt DateTime @default(now()) 14 | status Boolean @default(false) 15 | 16 | user User? @relation(fields: [userId], references: [id]) 17 | userId String? 18 | } 19 | -------------------------------------------------------------------------------- /templates/chat/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | .pnp 4 | .pnp.js 5 | 6 | # testing 7 | coverage 8 | 9 | # next.js 10 | .next 11 | out 12 | build 13 | dist 14 | 15 | # misc 16 | .DS_Store 17 | *.pem 18 | 19 | # debug 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | 24 | # local env files 25 | .env* 26 | !.env.example 27 | 28 | # turbo 29 | .turbo 30 | 31 | # vercel 32 | .vercel 33 | 34 | # bun 35 | bun.lockb 36 | bun.lock 37 | 38 | # prisma 39 | generated -------------------------------------------------------------------------------- /templates/chat/src/lib/tools/createTodo.ts: -------------------------------------------------------------------------------- 1 | import { createTool } from '@rubriclab/agents' 2 | import { z } from 'zod' 3 | import db from '~/db' 4 | 5 | export default createTool({ 6 | async execute({ status, title }) { 7 | await db.task.create({ 8 | data: { 9 | status, 10 | title 11 | } 12 | }) 13 | return undefined 14 | }, 15 | schema: { 16 | input: z.object({ 17 | status: z.boolean(), 18 | title: z.string() 19 | }), 20 | output: z.undefined() 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /templates/chat/src/lib/components/Nav.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useSession } from '~/auth/client' 4 | import { SignOutButton } from '~/components/SignOut' 5 | 6 | export function Nav() { 7 | const { user } = useSession() 8 | return ( 9 |
10 | Home 11 |
12 |

Signed in as {user.email}

13 | 14 |
15 |
16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /templates/chat/src/lib/auth/client.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import type { Prisma } from '@prisma/client' 4 | import { CreateAuthContext } from '@rubriclab/auth/client' 5 | 6 | export const { ClientAuthProvider, useSession } = 7 | CreateAuthContext< 8 | Prisma.SessionGetPayload<{ 9 | include: { 10 | user: { 11 | include: { 12 | apiKeyAuthorizationAccounts: true 13 | oAuth2AuthenticationAccounts: true 14 | oAuth2AuthorizationAccounts: true 15 | } 16 | } 17 | } 18 | }> 19 | >() 20 | -------------------------------------------------------------------------------- /templates/chat/src/lib/auth/server.ts: -------------------------------------------------------------------------------- 1 | import { createAuth, createGithubAuthenticationProvider, prismaAdapter } from '@rubriclab/auth' 2 | import db from '~/db' 3 | import env from '~/env' 4 | 5 | export const { routes, actions, __types } = createAuth({ 6 | authUrl: env.NEXT_PUBLIC_AUTH_URL, 7 | databaseProvider: prismaAdapter(db), 8 | oAuth2AuthenticationProviders: { 9 | github: createGithubAuthenticationProvider({ 10 | githubClientId: env.GITHUB_CLIENT_ID, 11 | githubClientSecret: env.GITHUB_CLIENT_SECRET 12 | }) 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | # next.js 12 | .next/ 13 | out/ 14 | build 15 | dist/ 16 | 17 | # misc 18 | .DS_Store 19 | *.pem 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # local env files 27 | .env* 28 | !.env.example 29 | 30 | # turbo 31 | .turbo 32 | 33 | # vercel 34 | .vercel 35 | 36 | jarvis/ 37 | 38 | **/dev.db 39 | 40 | **/bun.lockb -------------------------------------------------------------------------------- /templates/chat/src/lib/env.ts: -------------------------------------------------------------------------------- 1 | import { createEnv } from '@t3-oss/env-nextjs' 2 | import { z } from 'zod' 3 | 4 | export default createEnv({ 5 | client: { 6 | NEXT_PUBLIC_AUTH_URL: z.string().min(1) 7 | }, 8 | experimental__runtimeEnv: { 9 | NEXT_PUBLIC_AUTH_URL: process.env.NEXT_PUBLIC_AUTH_URL 10 | }, 11 | server: { 12 | DATABASE_URL: z.string().min(1), 13 | GITHUB_CLIENT_ID: z.string().min(1), 14 | GITHUB_CLIENT_SECRET: z.string().min(1), 15 | NODE_ENV: z.string(), 16 | OPENAI_API_KEY: z.string().min(1), 17 | REDIS_URL: z.string().min(1) 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /.github/workflows/notify-monorepo.yml: -------------------------------------------------------------------------------- 1 | name: Notify Main Repo 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | notify: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Trigger main repo workflow 13 | run: | 14 | curl -X POST \ 15 | -H "Authorization: token ${{ secrets.RUBROT_PAT }}" \ 16 | -H "Accept: application/vnd.github.v3+json" \ 17 | https://api.github.com/repos/RubricLab/rubric/actions/workflows/update-submodules.yml/dispatches \ 18 | -d '{"ref":"main","inputs":{"repository":"${{ github.repository }}","ref":"${{ github.ref }}"}}' 19 | -------------------------------------------------------------------------------- /templates/chat/src/app/icon.tsx: -------------------------------------------------------------------------------- 1 | import { ImageResponse } from 'next/og' 2 | 3 | export const contentType = 'image/png' 4 | export const size = { 5 | height: 32, 6 | width: 32 7 | } 8 | 9 | export default async function Icon() { 10 | return new ImageResponse( 11 |
24 | R 25 |
, 26 | { 27 | ...size 28 | } 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[json]": { 3 | "editor.defaultFormatter": "biomejs.biome" 4 | }, 5 | "[prisma]": { 6 | "editor.defaultFormatter": "Prisma.prisma" 7 | }, 8 | "[typescript]": { 9 | "editor.defaultFormatter": "biomejs.biome" 10 | }, 11 | "[typescriptreact]": { 12 | "editor.defaultFormatter": "biomejs.biome" 13 | }, 14 | "editor.codeActionsOnSave": { 15 | "source.fixAll.biome": "explicit", 16 | "source.organizeImports.biome": "explicit" 17 | }, 18 | "editor.defaultFormatter": "biomejs.biome", 19 | "editor.formatOnSave": true, 20 | "editor.formatOnType": true, 21 | "npm.packageManager": "bun" 22 | } 23 | -------------------------------------------------------------------------------- /templates/chat/src/lib/tools/getTodoList.ts: -------------------------------------------------------------------------------- 1 | import { createTool } from '@rubriclab/agents' 2 | import { z } from 'zod' 3 | import db from '~/db' 4 | 5 | export default createTool({ 6 | async execute() { 7 | return await db.task.findMany({ 8 | include: { 9 | user: { 10 | select: { 11 | email: true 12 | } 13 | } 14 | } 15 | }) 16 | }, 17 | schema: { 18 | input: z.object({}), 19 | output: z.array( 20 | z.object({ 21 | status: z.boolean(), 22 | title: z.string(), 23 | user: z 24 | .object({ 25 | email: z.string() 26 | }) 27 | .nullable() 28 | }) 29 | ) 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /templates/chat/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[json]": { 3 | "editor.defaultFormatter": "biomejs.biome" 4 | }, 5 | "[prisma]": { 6 | "editor.defaultFormatter": "Prisma.prisma" 7 | }, 8 | "[typescript]": { 9 | "editor.defaultFormatter": "biomejs.biome" 10 | }, 11 | "[typescriptreact]": { 12 | "editor.defaultFormatter": "biomejs.biome" 13 | }, 14 | "editor.codeActionsOnSave": { 15 | "source.fixAll.biome": "explicit", 16 | "source.organizeImports.biome": "explicit" 17 | }, 18 | "editor.defaultFormatter": "biomejs.biome", 19 | "editor.formatOnSave": true, 20 | "editor.formatOnType": true, 21 | "npm.packageManager": "bun" 22 | } 23 | -------------------------------------------------------------------------------- /templates/chat/src/app/(app)/layout.tsx: -------------------------------------------------------------------------------- 1 | import { getSession } from '~/auth/actions' 2 | import { ClientAuthProvider } from '~/auth/client' 3 | import { Nav } from '~/components/Nav' 4 | 5 | export default async function AppLayout({ children }: { children: React.ReactNode }) { 6 | return ( 7 | // @ts-expect-error: Auth Package Bug 8 | 9 |
10 |
15 |
16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /templates/chat/src/app/opengraph-image.tsx: -------------------------------------------------------------------------------- 1 | import { readFile } from 'node:fs/promises' 2 | import { ImageResponse } from 'next/og' 3 | 4 | export const alt = 'Create Rubric App' 5 | export const size = { 6 | height: 630, 7 | width: 1200 8 | } 9 | 10 | export const contentType = 'image/png' 11 | 12 | export default async function Image() { 13 | const interSemiBold = await readFile('public/fonts/PlusJakartaSans-Bold.ttf') 14 | 15 | return new ImageResponse( 16 |
27 | R 28 |
, 29 | { 30 | ...size, 31 | fonts: [ 32 | { 33 | data: interSemiBold, 34 | name: 'Inter', 35 | style: 'normal', 36 | weight: 400 37 | } 38 | ] 39 | } 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /templates/chat/src/app/(app)/ai.tsx: -------------------------------------------------------------------------------- 1 | 'use server' 2 | 3 | import { executeTodoAgent } from '~/agents/todo' 4 | import env from '~/env' 5 | import { publish } from '~/events/server' 6 | 7 | export async function sendMessage({ userId, message }: { userId: string; message: string }) { 8 | const { response } = await executeTodoAgent({ 9 | messages: [{ content: message, role: 'user' }], 10 | onEvent: async events => { 11 | switch (events.type) { 12 | case 'assistant_message': { 13 | await publish({ 14 | channel: userId, 15 | eventType: events.type, 16 | payload: events 17 | }) 18 | break 19 | } 20 | case 'function_call': { 21 | await publish({ 22 | channel: userId, 23 | eventType: events.name, 24 | payload: events 25 | }) 26 | break 27 | } 28 | } 29 | }, 30 | openAIKey: env.OPENAI_API_KEY 31 | }) 32 | 33 | console.log(response) 34 | } 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "bin": { 3 | "create-rubric-app": "./index.ts" 4 | }, 5 | "dependencies": { 6 | "@rubriclab/cli": "*" 7 | }, 8 | "devDependencies": { 9 | "@rubriclab/config": "*", 10 | "@rubriclab/package": "*" 11 | }, 12 | "homepage": "https://github.com/RubricLab/create-rubric-app#readme", 13 | "keywords": [ 14 | "agent", 15 | "ai", 16 | "create", 17 | "rubric" 18 | ], 19 | "license": "gonuts", 20 | "name": "create-rubric-app", 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/RubricLab/create-rubric-app.git" 24 | }, 25 | "scripts": { 26 | "bleed": "bun x npm-check-updates -u", 27 | "check": "bun x biome check .", 28 | "clean": "rm -rf .next && rm -rf node_modules", 29 | "format": "bun x biome check --write .", 30 | "prepare": "bun x @rubriclab/package prepare" 31 | }, 32 | "simple-git-hooks": { 33 | "post-commit": "bun x @rubriclab/package post-commit" 34 | }, 35 | "version": "1.6.31" 36 | } 37 | -------------------------------------------------------------------------------- /templates/chat/src/lib/agents/todo.ts: -------------------------------------------------------------------------------- 1 | import { createAgent, createResponseFormat, noTabs } from '@rubriclab/agents' 2 | import { z } from 'zod' 3 | import createTodo from '~/tools/createTodo' 4 | import getTodoList from '~/tools/getTodoList' 5 | 6 | const responseFormat = createResponseFormat({ 7 | name: 'todo_agent_response_format', 8 | schema: z.object({ 9 | response: z.string() 10 | }) 11 | }) 12 | 13 | const systemPrompt = noTabs` 14 | You are a todo agent. 15 | The user will ask you to do CRUD operations against a TODO database. 16 | You should use tools to help them. 17 | ` 18 | 19 | const { executeAgent, eventTypes, __ToolEvent, __ResponseEvent } = createAgent({ 20 | model: 'gpt-5.2', 21 | responseFormat, 22 | systemPrompt, 23 | tools: { 24 | createTodo, 25 | getTodoList 26 | } 27 | }) 28 | 29 | export { eventTypes as todoAgentEventTypes } 30 | export { executeAgent as executeTodoAgent } 31 | 32 | export type TodoAgentToolEvent = typeof __ToolEvent 33 | export type TodoAgentResponseEvent = typeof __ResponseEvent 34 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bun 2 | 3 | import { existsSync } from 'node:fs' 4 | import path from 'node:path' 5 | import { createCLI, createCommand } from '@rubriclab/cli' 6 | import { $ } from 'bun' 7 | import { z } from 'zod' 8 | 9 | const initCommand = createCommand({ 10 | args: z.object({ 11 | name: z.string().default('myApp').describe('Name of the new app directory') 12 | }), 13 | description: 'Inits an app based on Rubric patterns', 14 | handler: async ({ name }) => { 15 | const targetDir = path.join(process.cwd(), name) 16 | const templateDir = path.join(import.meta.dir, 'templates', 'chat') 17 | 18 | if (existsSync(targetDir)) { 19 | console.error(`Error: Directory "${name}" already exists`) 20 | process.exit(1) 21 | } 22 | 23 | await $`cp -r ${templateDir} ${targetDir}` 24 | console.log(`✅ Successfully created "${name}" from Rubric chat template`) 25 | console.log('\nNext steps:') 26 | console.log(` cd ${name}`) 27 | console.log(' bun install') 28 | console.log(' bun dev') 29 | }, 30 | name: 'init' 31 | }) 32 | 33 | const cli = createCLI({ 34 | commands: [initCommand], 35 | description: 'Create a new Rubric app', 36 | name: 'create-rubric-app', 37 | version: '1.0.0' 38 | }) 39 | 40 | cli.parse() 41 | -------------------------------------------------------------------------------- /templates/chat/README.md: -------------------------------------------------------------------------------- 1 | # Create Rubric App 2 | 3 | This project is bootstrapped with [`create-rubric-app`](https://github.com/RubricLab/create-rubric-app). 4 | 5 | ## Getting Started 6 | 7 | ### 1. Install dependencies 8 | 9 | ```sh 10 | npm i 11 | ``` 12 | 13 | ```sh 14 | bun i 15 | ``` 16 | 17 | ### 2. Set up the DB 18 | 19 | ```sh 20 | npm run db:push 21 | ``` 22 | 23 | ```sh 24 | bun db:push 25 | ``` 26 | 27 | ### 3. Run the development server 28 | 29 | ```sh 30 | npm run dev 31 | ``` 32 | 33 | ```sh 34 | bun dev 35 | ``` 36 | 37 | Open [localhost:3000](http://localhost:3000) in your browser to see the result. 38 | 39 | You can start modifying the UI by editing [src/app/page.tsx](./src/app/(app)/page.tsx). The page auto-updates as you edit the file. 40 | 41 | ### Deployment 42 | 43 | To serve your app to users, simply deploy the Next.js app eg. on [Railway](https://railway.app/new) or [Vercel](https://deploy.new/). 44 | 45 | To persist data, you'll need a database. Both [Railway](https://docs.railway.app/databases/postgresql) and [Vercel](https://vercel.com/docs/storage/vercel-postgres) provide Postgres DBs. 46 | 47 | ## Learn More 48 | 49 | To learn more about this project, take a look at this [blog post](https://rubriclabs.com/blog/create-rubric-app). 50 | -------------------------------------------------------------------------------- /templates/chat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@prisma/client": "6.19.1", 4 | "@rubriclab/agents": "0.0.84", 5 | "@rubriclab/auth": "0.0.55", 6 | "@rubriclab/events": "0.0.40", 7 | "@t3-oss/env-nextjs": "0.13.10", 8 | "dotenv": "17.2.3", 9 | "next": "16.1.0", 10 | "prisma": "6.19.1", 11 | "react": "19.2.3", 12 | "react-dom": "19.2.3", 13 | "zod": "4.1.13" 14 | }, 15 | "description": "This project was bootstrapped with create-rubric-app", 16 | "devDependencies": { 17 | "@rubriclab/config": "0.0.24", 18 | "@types/node": "25.0.3", 19 | "@types/react": "19.2.7", 20 | "@types/react-dom": "19.2.3", 21 | "typescript": "5.9.3" 22 | }, 23 | "license": "go nuts", 24 | "name": "my-app", 25 | "private": true, 26 | "scripts": { 27 | "bleed": "bun x npm-check-updates -u --dep prod,dev,optional,peer", 28 | "build": "next build", 29 | "check": "bun x biome check .", 30 | "clean": "rm -rf .next && rm -rf node_modules", 31 | "db:generate": "prisma generate", 32 | "db:push": "bun --env-file=.env prisma generate && prisma db push", 33 | "db:seed": "prisma db seed", 34 | "db:studio": "prisma studio", 35 | "dev": "next dev", 36 | "format": "bun x biome check --write .", 37 | "start": "next start" 38 | }, 39 | "version": "0.0.0" 40 | } 41 | -------------------------------------------------------------------------------- /templates/chat/src/lib/components/ChatBox.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { type KeyboardEvent, useState } from 'react' 4 | 5 | export function ChatBox({ 6 | submit, 7 | placeholder = 'Type a message...' 8 | }: { 9 | submit: (message: string) => void 10 | placeholder?: string 11 | }) { 12 | const [message, setMessage] = useState(placeholder) 13 | 14 | return ( 15 |
16 |
17 |