Skip to content

Commit a5bc00f

Browse files
[H-5717] SgAI Black Triangles (#94)
* init with start-app@latest demo template * adapt to work with openrouter * move the guitar example routes under demo * start cleaning up the devtools integrations * move query devtools * clean up devtools patterns * move the demo app UI to demo subroutes only * add a SKILL for tanstack-start * configure eslint fully; run lint and format fixes * re-arrange the demo layout vs index content properly * get ladle setup * create a config page, to allow users to specify open router api token * use ClientOnly to prevent SSR issues with assistants while setting up transport correctly * first shot at confguring ladle to run within start on /sketches * Potential fix for code scanning alert no. 3758: Clear text storage of sensitive information Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * solve the security approach in a smarter way --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
1 parent 09a7e77 commit a5bc00f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+14597
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(pnpm add:*)",
5+
"mcp__ide__getDiagnostics",
6+
"Skill(skill-creator)",
7+
"Bash(/Users/lunelson/.claude/plugins/marketplaces/anthropic-agent-skills/skill-creator/scripts/init_skill.py:*)",
8+
"Bash(pnpm build:*)",
9+
"Skill(tanstack-start)",
10+
"Bash(pnpm build:ladle:*)",
11+
"Bash(timeout 10 pnpm dev:*)"
12+
],
13+
"deny": [],
14+
"ask": []
15+
}
16+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
---
2+
name: tanstack-start
3+
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.
4+
---
5+
6+
# TanStack Start
7+
8+
## File-Based Routing
9+
10+
Routes live in `src/routes/`. The route tree auto-generates to `src/routeTree.gen.ts` - commit this file (required for type-checking).
11+
12+
### Route Types
13+
14+
- `__root.tsx` - Root layout, wraps all routes
15+
- `index.tsx` - Index route for a path segment
16+
- `demo.tsx` - Layout route wrapping all `demo/*.tsx` children (uses `<Outlet />`)
17+
- `demo/*.tsx` - Child routes rendered inside parent layout
18+
- `$param.tsx` - Dynamic route segment
19+
20+
### Layout Routes
21+
22+
Create `foo.tsx` alongside `foo/` directory to wrap child routes:
23+
24+
```tsx
25+
import { createFileRoute, Outlet } from '@tanstack/react-router'
26+
27+
export const Route = createFileRoute('/foo')({
28+
component: FooLayout,
29+
})
30+
31+
function FooLayout() {
32+
return (
33+
<div>
34+
<Outlet />
35+
</div>
36+
)
37+
}
38+
```
39+
40+
## API Routes
41+
42+
Server handlers use `api.*.ts` naming convention:
43+
44+
```tsx
45+
import { createFileRoute } from '@tanstack/react-router'
46+
47+
export const Route = createFileRoute('/demo/api/example')({
48+
server: {
49+
handlers: {
50+
POST: async ({ request }) => {
51+
const data = await request.json()
52+
return Response.json({ result: data })
53+
},
54+
},
55+
},
56+
})
57+
```
58+
59+
## AI Chat Integration
60+
61+
### Client Side
62+
63+
Use `@ai-sdk/react` with `DefaultChatTransport`:
64+
65+
```tsx
66+
import { useChat } from '@ai-sdk/react'
67+
import { DefaultChatTransport } from 'ai'
68+
69+
const { messages, sendMessage } = useChat({
70+
transport: new DefaultChatTransport({
71+
api: '/demo/api/chat',
72+
}),
73+
})
74+
```
75+
76+
### Server Side
77+
78+
Use `streamText` from `ai` package with OpenRouter provider:
79+
80+
```tsx
81+
import { convertToModelMessages, streamText } from 'ai'
82+
import { haiku } from '@/lib/openrouter'
83+
84+
const result = await streamText({
85+
model: haiku,
86+
messages: convertToModelMessages(messages),
87+
system: SYSTEM_PROMPT,
88+
tools,
89+
})
90+
91+
return result.toUIMessageStreamResponse()
92+
```
93+
94+
### OpenRouter Setup
95+
96+
Create provider singleton in `src/lib/openrouter.ts`:
97+
98+
```tsx
99+
import { createOpenRouter } from '@openrouter/ai-sdk-provider'
100+
101+
export const openrouter = createOpenRouter({
102+
apiKey: process.env.OPENROUTER_API_KEY,
103+
})
104+
105+
export const haiku = openrouter('anthropic/claude-3.5-haiku')
106+
```
107+
108+
## MCP Server
109+
110+
Register tools with Zod schemas in route files:
111+
112+
```tsx
113+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
114+
import z from 'zod'
115+
import { handleMcpRequest } from '@/utils/mcp-handler'
116+
117+
const server = new McpServer({ name: 'my-server', version: '1.0.0' })
118+
119+
server.registerTool(
120+
'toolName',
121+
{
122+
title: 'Tool Title',
123+
description: 'Tool description',
124+
inputSchema: { param: z.string().describe('Param description') },
125+
},
126+
({ param }) => ({
127+
content: [{ type: 'text', text: result }],
128+
}),
129+
)
130+
131+
export const Route = createFileRoute('/mcp')({
132+
server: {
133+
handlers: {
134+
POST: async ({ request }) => handleMcpRequest(request, server),
135+
},
136+
},
137+
})
138+
```
139+
140+
## Path Aliases
141+
142+
`@/*` maps to `./src/*` (configured in tsconfig.json).
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"initialized": true,
3+
"mode": "file-router",
4+
"projectName": "sgai-black-triangles",
5+
"typescript": true,
6+
"tailwind": true,
7+
"git": true,
8+
"addOnOptions": {},
9+
"packageManager": "pnpm",
10+
"version": 1,
11+
"framework": "react-cra",
12+
"chosenAddOns": [
13+
"compiler",
14+
"mcp",
15+
"start",
16+
"store",
17+
"tanstack-query",
18+
"tanchat",
19+
"eslint",
20+
"nitro"
21+
]
22+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
node_modules
2+
.DS_Store
3+
dist
4+
dist-ssr
5+
*.local
6+
count.txt
7+
.env
8+
.nitro
9+
.tanstack
10+
.wrangler
11+
mcp-todos.json.output
12+
.vinxi
13+
todos.json
14+
15+
# build outputs
16+
.output
17+
build
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** @type {import('@ladle/react').UserConfig} */
2+
export default {
3+
stories: 'sketches/**/*.stories.{js,jsx,ts,tsx,mdx}',
4+
viteConfig: '.ladle/vite.config.ts',
5+
base: '/sketches/',
6+
outDir: 'build/sketches',
7+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import tailwindcss from '@tailwindcss/vite'
2+
import { defineConfig } from 'vite'
3+
4+
const config = defineConfig({
5+
plugins: [tailwindcss()],
6+
})
7+
8+
export default config
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package-lock.json
2+
pnpm-lock.yaml
3+
yarn.lock
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# CLAUDE.md
2+
3+
## Style
4+
5+
Be extremely concise. Sacrifice grammar for the sake of concision.
6+
7+
## Commands
8+
9+
```bash
10+
pnpm install # Install dependencies
11+
pnpm dev # Start dev server on port 3000
12+
pnpm build # Build for production
13+
pnpm serve # Preview production build
14+
pnpm test # Run tests with Vitest
15+
pnpm lint # Run ESLint
16+
pnpm format # Run Prettier
17+
pnpm check # Format and lint with auto-fix
18+
```
19+
20+
## Architecture
21+
22+
This is a TanStack Start application (full-stack React meta-framework) with Claude AI integration.
23+
24+
### Tech Stack
25+
26+
- **Framework**: TanStack Start (built on Vite + Nitro)
27+
- **Routing**: TanStack Router (file-based routing in `src/routes/`)
28+
- **State**: TanStack Store + TanStack Query
29+
- **Styling**: Tailwind CSS v4
30+
- **AI**: Vercel AI SDK (`ai` package) + Anthropic Claude
31+
- **MCP**: Model Context Protocol server support via `@modelcontextprotocol/sdk`
32+
33+
### Key Patterns
34+
35+
**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.
36+
37+
**API routes**: Server handlers use the pattern `src/routes/demo/api.*.ts` with `server.handlers` export:
38+
39+
```typescript
40+
export const Route = createFileRoute('/demo/api/example')({
41+
server: {
42+
handlers: {
43+
POST: async ({ request }) => {
44+
/* ... */
45+
},
46+
},
47+
},
48+
})
49+
```
50+
51+
**AI chat integration**: Uses `@ai-sdk/react` `useChat` hook with `DefaultChatTransport` pointing to API routes. Server uses `streamText` from `ai` package with Anthropic models.
52+
53+
**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.
54+
55+
**Path aliases**: `@/*` maps to `./src/*` (configured in tsconfig.json).
56+
57+
### Project Structure
58+
59+
- `src/routes/__root.tsx` - Root layout with Header and DevTools
60+
- `src/router.tsx` - Router setup with TanStack Query integration
61+
- `src/integrations/tanstack-query/` - Query client provider
62+
- `src/utils/` - Shared utilities (tools, MCP handler)
63+
- `src/components/` - React components
64+
- `src/data/` - Static data (guitars, songs examples)
65+
66+
## Environment Variables
67+
68+
Required in `.env.local`:
69+
70+
```
71+
ANTHROPIC_API_KEY=your_api_key
72+
```

0 commit comments

Comments
 (0)