Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions pocs/sgai-black-triangles/.claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"permissions": {
"allow": [
"Bash(pnpm add:*)",
"mcp__ide__getDiagnostics",
"Skill(skill-creator)",
"Bash(/Users/lunelson/.claude/plugins/marketplaces/anthropic-agent-skills/skill-creator/scripts/init_skill.py:*)",
"Bash(pnpm build:*)",
"Skill(tanstack-start)",
"Bash(pnpm build:ladle:*)",
"Bash(timeout 10 pnpm dev:*)"
],
"deny": [],
"ask": []
}
}
142 changes: 142 additions & 0 deletions pocs/sgai-black-triangles/.claude/skills/tanstack-start/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
---
name: tanstack-start
description: TanStack Start full-stack React framework patterns. Use when working with file-based routing, API routes, server handlers, TanStack Router layouts, or integrating AI/MCP features in this codebase.
---

# TanStack Start

## File-Based Routing

Routes live in `src/routes/`. The route tree auto-generates to `src/routeTree.gen.ts` - commit this file (required for type-checking).

### Route Types

- `__root.tsx` - Root layout, wraps all routes
- `index.tsx` - Index route for a path segment
- `demo.tsx` - Layout route wrapping all `demo/*.tsx` children (uses `<Outlet />`)
- `demo/*.tsx` - Child routes rendered inside parent layout
- `$param.tsx` - Dynamic route segment

### Layout Routes

Create `foo.tsx` alongside `foo/` directory to wrap child routes:

```tsx
import { createFileRoute, Outlet } from '@tanstack/react-router'

export const Route = createFileRoute('/foo')({
component: FooLayout,
})

function FooLayout() {
return (
<div>
<Outlet />
</div>
)
}
```

## API Routes

Server handlers use `api.*.ts` naming convention:

```tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/demo/api/example')({
server: {
handlers: {
POST: async ({ request }) => {
const data = await request.json()
return Response.json({ result: data })
},
},
},
})
```

## AI Chat Integration

### Client Side

Use `@ai-sdk/react` with `DefaultChatTransport`:

```tsx
import { useChat } from '@ai-sdk/react'
import { DefaultChatTransport } from 'ai'

const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
api: '/demo/api/chat',
}),
})
```

### Server Side

Use `streamText` from `ai` package with OpenRouter provider:

```tsx
import { convertToModelMessages, streamText } from 'ai'
import { haiku } from '@/lib/openrouter'

const result = await streamText({
model: haiku,
messages: convertToModelMessages(messages),
system: SYSTEM_PROMPT,
tools,
})

return result.toUIMessageStreamResponse()
```

### OpenRouter Setup

Create provider singleton in `src/lib/openrouter.ts`:

```tsx
import { createOpenRouter } from '@openrouter/ai-sdk-provider'

export const openrouter = createOpenRouter({
apiKey: process.env.OPENROUTER_API_KEY,
})

export const haiku = openrouter('anthropic/claude-3.5-haiku')
```

## MCP Server

Register tools with Zod schemas in route files:

```tsx
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import z from 'zod'
import { handleMcpRequest } from '@/utils/mcp-handler'

const server = new McpServer({ name: 'my-server', version: '1.0.0' })

server.registerTool(
'toolName',
{
title: 'Tool Title',
description: 'Tool description',
inputSchema: { param: z.string().describe('Param description') },
},
({ param }) => ({
content: [{ type: 'text', text: result }],
}),
)

export const Route = createFileRoute('/mcp')({
server: {
handlers: {
POST: async ({ request }) => handleMcpRequest(request, server),
},
},
})
```

## Path Aliases

`@/*` maps to `./src/*` (configured in tsconfig.json).
22 changes: 22 additions & 0 deletions pocs/sgai-black-triangles/.cta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"initialized": true,
"mode": "file-router",
"projectName": "sgai-black-triangles",
"typescript": true,
"tailwind": true,
"git": true,
"addOnOptions": {},
"packageManager": "pnpm",
"version": 1,
"framework": "react-cra",
"chosenAddOns": [
"compiler",
"mcp",
"start",
"store",
"tanstack-query",
"tanchat",
"eslint",
"nitro"
]
}
17 changes: 17 additions & 0 deletions pocs/sgai-black-triangles/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
count.txt
.env
.nitro
.tanstack
.wrangler
mcp-todos.json.output
.vinxi
todos.json

# build outputs
.output
build
7 changes: 7 additions & 0 deletions pocs/sgai-black-triangles/.ladle/config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/** @type {import('@ladle/react').UserConfig} */
export default {
stories: 'sketches/**/*.stories.{js,jsx,ts,tsx,mdx}',
viteConfig: '.ladle/vite.config.ts',
base: '/sketches/',
outDir: 'build/sketches',
}
8 changes: 8 additions & 0 deletions pocs/sgai-black-triangles/.ladle/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'

const config = defineConfig({
plugins: [tailwindcss()],
})

export default config
3 changes: 3 additions & 0 deletions pocs/sgai-black-triangles/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package-lock.json
pnpm-lock.yaml
yarn.lock
72 changes: 72 additions & 0 deletions pocs/sgai-black-triangles/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# CLAUDE.md

## Style

Be extremely concise. Sacrifice grammar for the sake of concision.

## Commands

```bash
pnpm install # Install dependencies
pnpm dev # Start dev server on port 3000
pnpm build # Build for production
pnpm serve # Preview production build
pnpm test # Run tests with Vitest
pnpm lint # Run ESLint
pnpm format # Run Prettier
pnpm check # Format and lint with auto-fix
```

## Architecture

This is a TanStack Start application (full-stack React meta-framework) with Claude AI integration.

### Tech Stack

- **Framework**: TanStack Start (built on Vite + Nitro)
- **Routing**: TanStack Router (file-based routing in `src/routes/`)
- **State**: TanStack Store + TanStack Query
- **Styling**: Tailwind CSS v4
- **AI**: Vercel AI SDK (`ai` package) + Anthropic Claude
- **MCP**: Model Context Protocol server support via `@modelcontextprotocol/sdk`

### Key Patterns

**File-based routing**: Routes are defined in `src/routes/`. The route tree is auto-generated in `src/routeTree.gen.ts` - do not edit this file manually.

**API routes**: Server handlers use the pattern `src/routes/demo/api.*.ts` with `server.handlers` export:

```typescript
export const Route = createFileRoute('/demo/api/example')({
server: {
handlers: {
POST: async ({ request }) => {
/* ... */
},
},
},
})
```

**AI chat integration**: Uses `@ai-sdk/react` `useChat` hook with `DefaultChatTransport` pointing to API routes. Server uses `streamText` from `ai` package with Anthropic models.

**MCP server**: The `/mcp` route exposes an MCP server using in-memory transports. Tools are registered via `server.registerTool()` with Zod schemas for input validation.

**Path aliases**: `@/*` maps to `./src/*` (configured in tsconfig.json).

### Project Structure

- `src/routes/__root.tsx` - Root layout with Header and DevTools
- `src/router.tsx` - Router setup with TanStack Query integration
- `src/integrations/tanstack-query/` - Query client provider
- `src/utils/` - Shared utilities (tools, MCP handler)
- `src/components/` - React components
- `src/data/` - Static data (guitars, songs examples)

## Environment Variables

Required in `.env.local`:

```
ANTHROPIC_API_KEY=your_api_key
```
Loading
Loading