# Project export: Claudeware

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2026
- Tagline: If u can prompt it we can print it , we killed the learning curve between learning CAD softwares and printing it physically
- Devpost: https://devpost.com/software/claudeware
- GitHub: https://github.com/vraj00222/Claudware
- Video: https://www.youtube.com/embed/Uyvus6JXSH0?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — vraj00222 (84 commits), Devin AI (41 commits), Claude Opus 4.8 (1M context) (7 commits)

## Devpost submission (written by the team)

### Inspiration

Claude can write, reason, and code — but it has no hardware branch. It lives entirely in software; it can describe a phone stand, but it can't hand you one. We wanted to change that: to give Claude a way to reach out of the screen and put a real, physical object in your hands. At the opening ceremony, the call was to go for big swings — the biggest swing at a meaningful problem. So we picked one. 3D printers are now cheap and everywhere, but adoption is still stuck, and the reason isn't the hardware. It's that the average person needs roughly 600 hours to become competent in CAD software like Blender or Fusion 360 — and most still can't produce something that prints without failing. Professional modeling costs $50–$500 per part, CAD licenses run ~$2,000/year, and even then you have to learn orientation, supports, wall thickness, tolerances, and splitting on your own. The barrier to 3D printing was never the printer. It was design expertise. So we built the thing that supplies it — and gave Claude its hardware branch in the process. We called it Claudeware. What It Does You describe any object in plain English — "a gear with 24 teeth," "a threaded M10 bolt," "a chubby sitting dragon" — and Claude designs it, makes it printable, shows it forming live in a 3D viewport you can orbit and drag, inspects its own render and fixes its own mistakes, and exports a print-ready file for the printer you already own. The flow is six steps, and Claude drives all of them: Describe — type, speak, or pick a prompt. Clarify — Claude asks prompt-specific questions first. A dragon gets asked about wings, scales, and pose; a bolt gets asked about thread pitch and length. No generic templates. Design — the model builds step-by-step, live, in a react-three-fiber viewport you can orbit, zoom, and drag while it's still forming. Open the native CAD app alongside (OpenSCAD, Blender, or Fusion) and the same build appears there too — dual view, web and desktop. Validate — four automatic printability checks run on the finished mesh (watertight geometry, single body, overhangs, wall thickness) and produce a 0–100 readiness score. Prepare — Claude auto-orients the model for the best print and exports real files: STL, OBJ, 3MF for a Bambu A1, and actual G-code sliced by PrusaSlicer, with real supports, real layer counts, and real filament estimates — not guesses. Print — send to the printer with one click. Too big for the bed? Claude splits it into parts joined by push-fit connectors — 5.4 mm pegs into 5.6 mm sockets, a 0.2 mm engineered clearance that snaps together without glue. The core idea is that no single engine is right for every object, so Claudeware isn't one tool — it's five engines behind one brain, with an Auto mode that classifies the prompt and routes it: OpenSCAD + BOSL2 for mechanical parts (real involute gears, real threads) Blender (bpy) for organic shapes you watch sculpt live Fusion 360 (adsk) for precision multi-part assemblies NVIDIA NIM (TRELLIS) for full-color textured characters Auto so the user never has to know the difference between parametric and organic modeling And unlike every other text-to-3D tool that hands you a mesh and wishes you luck, Claudeware checks its own work — it renders the result, scores it for likeness with computer vision, and regenerates if it falls short, with no human in the loop. How We Built It The frontend is Next.js + TypeScript + Tailwind with a react-three-fiber / drei viewport. UI and backend talk through exactly one contract — a typed AgentEvent stream over Server-Sent Events — so the UI is pure (events in, JSX out) and never imports backend logic. At the center is a single orchestrator, /api/generate, that runs the loop classify → clarify → resolve engine → generate → finish. Auto routing classifies the prompt and dispatches to one of four engines, each running as a subprocess with a timeout: OpenSCAD — Claude writes staged parametric SCAD with the vendored BOSL2 library Blender — Claude writes staged bpy Python, driven live over the BlenderMCP socket straight from Node (no MCP client needed), with a headless fallback Fusion 360 — Claude writes adsk scripts POSTed to Fusion's own HTTP MCP NVIDIA NIM — TRELLIS text-to-3D → textured GLB + STL Crucially, Claude writes a recipe — the actual SCAD/bpy/adsk script — not a frozen mesh. That's what makes "edit it by prompt" real: each prompt patches the script and re-runs into a new, diff-able version. The finish tail is the part nobody else builds: a Print Brain computes dimensions, supports, and split decisions from the mesh, and a Print-Readiness pipeline runs four real checks (manifold via open-edge count, floaters via connected-component analysis, overhang fraction via face normals, thin-feature heuristics), auto-orients by scoring the six axis-aligned poses, and exports 3MF/OBJ/G-code through a forked PrusaSlicer CLI. A self-inspect step renders the result and asks Claude vision to score likeness, triggering one bounded auto-fix retry. Every external service — Deepgram, Redis, Browserbase, The Token Company, Arize, Sentry, InsForge — sits behind a key-gated interface with a working fallback, so the whole app boots with zero keys. We TDD'd the pure logic throughout: 216 tests across 40 files, a clean production build, and cached fixtures as a wifi-death safety net. Challenges We Ran Into The LLM is a subprocess that can time out. Complex prompts made Claude reason past the claude -p time limit mid-write, so a threaded jar or an L-bracket would silently fall back to a generic block. We root-caused it with systematic debugging (the failed build's clock landed exactly on the timeout) and fixed it by pinning the script writers to Sonnet, which writes good CAD roughly twice as fast and finishes under the limit. A hidden concurrency starve. The bug's tell was "it always fails when starting a new session, then works after." The cause: classify, clarify, and generate each fire their own claude -p at once and starve the shared CLI — tipping an already-slow call over the edge. Non-obvious, and only findable by tracing the timing. BOSL2 dropping geometry silently. A bolt rendered as just its hex head because BOSL2's part libraries (threads, gears, bearings) are separate files from the core — so threaded_rod() was an unknown module that OpenSCAD drops with a warning, not an error. We had to re-prepend the full library set on every stage and start treating render warnings as signal. NVIDIA's endpoints fighting back. The hosted TRELLIS text-to-3D endpoint intermittently 500s and sometimes returns an empty/queued artifact under load; we built retry-with-polling to lift the success rate from ~1-in-3 to ~90%. The image-to-3D endpoint 500s server-side even with the correct upload flow, so we pivoted: Claude vision describes the reference image in text and we feed that into the working text-to-3D path. Driving real CAD apps live. Blender's MCP is a Claude Desktop extension, not a Claude Code server, so we drove the addon's localhost socket directly from the backend; Fusion we drove over its own HTTP MCP. Both had sharp edges (scope-poisoning imports, units in cm vs mm) to file down. Keeping a frozen design intact. A hard rule of the project was not to touch the frozen UI, so the entire print-readiness and assembly backend had to be built additively behind the AgentEvent contract until a deliberate "change the design" go-ahead. Accomplishments That We're Proud Of We gave Claude a hardware branch. It can now turn a sentence into a physical object you can hold. Five engines, one brain — a genuine multi-engine system with Auto routing, not a one-trick demo. Mechanical parts, organic figures, precision assemblies, and textured characters all from one prompt box. AI that checks its own work — the render → vision-score → auto-fix loop is the differentiator, and it runs with no human in the loop. A real manufacturing pipeline, not a toy. Actual G-code from PrusaSlicer, real BOSL2 involute threads, and engineered 0.2 mm push-fit tolerances that snap together without glue. Zero-key boot. The entire app runs and demos with no API keys at all — every integration degrades gracefully instead of breaking. Production-grade under hackathon time — 216 tests across 40 files, a clean build, and 8 sponsor integrations each doing real work behind a fallback. What We Learned The biggest model isn't always the right one. Under a latency budget, faster mid-size models (Sonnet, Haiku for classification) beat the largest model that reasons past the timeout. Right-sizing the model to the task is an engineering decision, not a default. Treat the LLM like any other subprocess — bound it with timeouts and retries, and assume it can fail or hang. One bad generation must never hang the loop. Generation isn't the hard part; printability is. Every other text-to-3D tool stops at the mesh. The real value — and the real engineering — lives in the orientation, checks, splitting, and slicing that turn a shape into something that actually prints. Honest fallbacks beat silent ones. Surfacing the real failure reason ("Claude ran past the limit → generic shape," "Blender isn't connected") makes the product trustworthy; a silently faked result destroys trust the moment it's caught. The "recipe, not a mesh" abstraction is what makes edit-by-prompt real — and it's worth designing for up front rather than retrofitting. What's Next for Claudeware True likeness via image-to-3D — a self-hosted TRELLIS or an image-accepting provider (Rodin/Tripo/Meshy) so a reference photo reproduces an accurate model, not just a loose guide. Staged complex builds — stage the Fusion and OpenSCAD builds the way Blender already is, to beat the single-shot ceiling on multi-feature parts and assemblies. Serialize the generate route so classify/clarify/generate stop share-starving the shared model at session start. Real auto-repair transforms — auto-thicken thin legs, and decompose complex models into separate printable parts nested on a single plate with library connectors. Close the loop to the printer — OrcaSlicer G-code and a real one-click send to a Bambu over LAN, plus a printer cam to watch it print. A fuller voice agent (Deepgram end-to-end) and broader model-repo search with authenticated downloads.

## README (from the GitHub repository)

# Claude Hardware

**Describe it. We make it printable.**

Claude Hardware is an AI-powered 3D printing design studio. Describe any object in plain language — a phone stand, a dragon figurine, a threaded bolt — and Claude designs it, makes it printable, shows it forming live in a 3D viewport you can orbit and drag, inspects its own render, fixes its own mistakes, and exports a print-ready file for your printer.

The barrier to 3D printing isn't the printer — it's design expertise. We supply it.

> Built with Claude Code during CAL AI HACKATHON 2026. Track: Lab.

---

## What it does

1. **Describe** — type, speak, or pick a prompt ("a gear with 24 teeth", "a chubby sitting dragon")
2. **Clarify** — Claude asks smart, prompt-specific questions (style? size? pose?) before building
3. **Design** — watch the model build step-by-step in a live 3D viewport (and optionally in a real CAD app)
4. **Validate** — automatic printability checks (watertight, wall thickness, overhangs, single body)
5. **Prepare** — auto-orient, export STL/OBJ/3MF/G-code, real slicing with PrusaSlicer
6. **Print** — send to your Bambu A1 (or any printer) with one click

## 5 engines, 1 brain

| Engine | Best for | How it works |
|--------|----------|--------------|
| **OpenSCAD** | Mechanical parts, gears, bolts, brackets | Claude writes parametric SCAD scripts with BOSL2 — real threads, real gears |
| **Blender** | Organic shapes, figurines, artistic models | Claude writes staged `bpy` Python — live build in Blender or headless |
| **Fusion 360** | Precise CAD, assemblies, multi-part prints | Claude writes `adsk` scripts via Fusion's HTTP MCP — watchable in Fusion |
| **NVIDIA NIM** | Textured figurines, characters, creatures | TRELLIS text-to-3D — textured GLB preview + printable STL |
| **Auto** | Everything (default) | Claude classifies your prompt and picks the right engine |

Plus **Clean in Blender** (post-step) and **Model Search** (find existing models before generating).

## Sponsor integrations

| Sponsor | What it powers |
|---------|---------------|
| **Anthropic** (Claude) | The brain — designs, classifies, clarifies, self-inspects, fixes |
| **Arize AX** | LLM observability — traces every Claude call, generation pipeline spans, LLM-as-judge evaluator |
| **Deepgram** | Voice input — speak your idea instead of typing |
| **NVIDIA NIM** | TRELLIS text-to-3D for textured organic models |
| **Redis** | Semantic generation cache + vector search + agent memory |
| **Browserbase** | Live web search of free model repos (Printables) — reuse before regenerate |
| **The Token Company** | Prompt compression — cuts Claude token costs via bear-2 |
| **InsForge** | Auth (Google OAuth) + database (per-user projects) + file storage |

Every integration is **key-gated with a working fallback** — the app boots and demos with zero keys.

---

## Quick start

```bash
# Clone
git clone https://github.com/vraj00222/Claudware.git
cd Claudware

# Install dependencies
npm install

# Set up environment
cp .env.example .env.local
# Fill in your API keys (see "Environment variables" below)

# Install system tools (Ubuntu/Debian)
sudo apt-get install -y openscad blender prusa-slicer

# Clone BOSL2 for OpenSCAD mechanical parts
git clone https://github.com/BelfrySCAD/BOSL2.git tools/openscad-libs/BOSL2

# Build + run
npm run build
npm run dev
# → http://localhost:3000
```

### Routes

| Route | What |
|-------|------|
| `/` | Animated landing page |
| `/app` | Studio (behind Google sign-in; "continue without signing in" for dev) |
| `/projects` | Your saved projects gallery |
| `/profile` | Account + sign out |

### Environment variables

Copy `.env.example` to `.env.local` and fill in:

| Variable | Required? | What |
|----------|-----------|------|
| `ANTHROPIC_API_KEY` | **Yes** for real generation | Claude API key — the brain |
| `NVIDIA_NIM` | For textured figurines | NVIDIA NIM key (`nvapi-...`) from build.nvidia.com |
| `DEEPGRAM_API_KEY` | For voice input | Deepgram STT key |
| `REDIS_URL` | For semantic cache | `redis://localhost:6379` or Redis Cloud URL |
| `BROWSERBASE_API_KEY` | For live model search | Browserbase API key |
| `BROWSERBASE_PROJECT_ID` | For model search | Browserbase project ID |
| `TTC_API_KEY` | For token savings | The Token Company API key |
| `ARIZE_SPACE_ID` | For LLM tracing | Arize AX Space ID (from app.arize.com settings) |
| `ARIZE_API_KEY` | For LLM tracing | Arize AX API Key |
| `NEXT_PUBLIC_INSFORGE_URL` | For auth + persistence | InsForge project URL (browser) |
| `NEXT_PUBLIC_INSFORGE_ANON_KEY` | For auth + persistence | InsForge anon key (browser) |
| `INSFORGE_URL` | For server storage | InsForge URL (server-only) |
| `INSFORGE_API_KEY` | For server storage | InsForge admin key (server-only) |

> **Zero-key boot**: the app works with NO keys — deterministic generation, localStorage persistence, Web Speech voice, in-memory cache. Real keys unlock real generation.

### Optional: live CAD sync

- **OpenSCAD**: open `tools/_watch/model.scad` with Design → Automatic Reload — watch the same build in the native app
- **Blender**: N-panel → BlenderMCP → Connect (socket 9876) — watch Claude build in your Blender window
- **Fusion 360**: HTTP MCP on `127.0.0.1:27182` — watch parts build in Fusion

---

## Architecture

```
┌─────────────────────────────────────────────────────────┐
│  Frontend (Next.js + Tailwind + react-three-fiber)      │
│  "Hardware Paper" design system — warm light, terracotta│
│  Pure components: AgentEvent in → JSX out                │
└───────────────────────┬─────────────────────────────────┘
                        │ SSE (AgentEvents)
┌───────────────────────▼─────────────────────────────────┐
│  /api/generate — the orchestrator                        │
│  classify → clarify → resolve engine → generate → finish│
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
│  │ OpenSCAD │ │ Blender  │ │ Fusion   │ │ NVIDIA NIM │ │
│  │ (BOSL2)  │ │ (bpy)    │ │ (adsk)   │ │ (TRELLIS)  │ │
│  └──────────┘ └──────────┘ └──────────┘ └────────────┘ │
│  + validate + estimate + printplan + storage upload      │
└───────────────────────┬─────────────────────────────────┘
                        │
┌───────────────────────▼─────────────────────────────────┐
│  Integrations (all key-gated, all have fallbacks)       │
│  Voice→Deepgram(Web Speech) · Cache→Redis(in-mem)       │
│  Auth→InsForge(zero-key) · Search→Browserbase(curated)  │
│  Compress→TTC(passthrough) · Telemetry→Sentry(no-op)    │
└─────────────────────────────────────────────────────────┘
```

### Key files

| Path | What |
|------|------|
| `src/app/api/generate/route.ts` | The main generation endpoint — SSE orchestrator |
| `src/server/openscad.ts` | OpenSCAD engine (Claude → SCAD + BOSL2 → render → STL) |
| `src/server/blender.ts` | Blender engine (Claude → bpy → live/headless → STL) |
| `src/server/fusion.ts` | Fusion engine (Claude → adsk → HTTP MCP → STL) |
| `src/server/meshgen/nim.ts` | NVIDIA NIM TRELLIS (text → textured GLB + STL) |
| `src/server/engineRoute.ts` | Auto-routing classifier (prompt → best engine) |
| `src/server/printPlan.ts` | Print Brain — dims, supports, split decision |
| `src/server/printReady/` | Print Readiness v2 — diagnose, orient, export 3MF/OBJ/G-code |
| `src/server/claude.ts` | Shared Claude API client (claudeText + claudeVision) |
| `src/server/genCache.ts` | Redis semantic cache + vector search |
| `src/server/agentMemory.ts` | Redis agent memory (cross-session learning) |
| `src/lib/agentStream.ts` | Client-side SSE consumer → AgentEvents |
| `src/components/` | Pure UI components (frozen design) |
| `src/viewport/` | react-three-fiber 3D viewport |
| `frontend/` | Claude Design export — the design contract (read-only) |

---

## The generation pipeline

```
User prompt
    │
    ▼
┌─ Classify (Claude Haiku) ──────────────────────────┐
│  Is this mechanical? organic? a character?          │
│  → route to the right engine                        │

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 179 recognized source files, 1394 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- Redis (technology) — detected in the code
- SQL (language) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository; commit authorship or trailers
- AI coding agent: Codex — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (120 of 190)

```
.claude/settings.json
.env.example
.gitignore
AGENTS.md
ARCHITECTURE.md
claude-hardware-build-playbook.md
CLAUDE.md
DECISIONS.md
demo-fixtures/README.md
DEMO.md
DESIGN.md
docs/landing-trunk-design.md
docs/oss-toolbox.md
docs/ROADMAP.md
docs/SETUP.md
docs/superpowers/plans/2026-06-13-frontend-shell-mockstream.md
docs/superpowers/plans/2026-06-15-meshgen-and-clarify.md
docs/superpowers/plans/2026-06-15-model-search.md
docs/superpowers/plans/2026-06-15-print-brain-v1.md
docs/superpowers/specs/2026-06-13-claude-hardware-design.md
docs/superpowers/specs/2026-06-15-meshgen-and-clarify-design.md
docs/superpowers/specs/2026-06-15-model-search-design.md
docs/superpowers/specs/2026-06-15-print-brain-v1-design.md
docs/superpowers/specs/2026-06-16-multi-engine-routing-and-fusion-design.md
docs/superpowers/specs/2026-06-17-print-readiness-pipeline-v2-design.md
docs/superpowers/specs/2026-06-18-fusion-design-types-design.md
eslint.config.mjs
frontend/Claude Hardware.dc.html
frontend/Hardware States Board.dc.html
frontend/homepage.html
frontend/support.js
insforge.toml
migrations/20260615020915_create-projects.sql
next.config.ts
package.json
PITCH.md
postcss.config.mjs
PROGRESS.md
public/assets/showcase/dragon.glb
public/assets/showcase/printer.glb
public/fixtures/phone_stand.stl
public/landing.html
public/support.js
README.md
sentry.client.config.ts
sentry.edge.config.ts
sentry.server.config.ts
src/app/api/arize/route.ts
src/app/api/clarify/route.ts
src/app/api/classify/route.ts
src/app/api/generate/route.ts
src/app/api/import/route.ts
src/app/api/prepare/route.ts
src/app/api/print/route.ts
src/app/api/redis/route.ts
src/app/api/search/route.ts
src/app/api/sentry-test/route.ts
src/app/api/split/route.ts
src/app/api/transform/route.ts
src/app/api/upload/route.ts
src/app/app/page.tsx
src/app/global-error.tsx
src/app/globals.css
src/app/layout.tsx
src/app/page.tsx
src/app/profile/page.tsx
src/app/projects/page.tsx
src/app/showcase/page.tsx
src/components/__tests__/AgentFeed.test.tsx
src/components/__tests__/StageTracker.test.tsx
src/components/AgentFeed.tsx
src/components/AuthGate.tsx
src/components/ClarifyCard.tsx
src/components/ConversationPanel.tsx
src/components/DemoHarness.tsx
src/components/DesignNotes.tsx
src/components/ModelSearchPanel.tsx
src/components/PrintCenter.tsx
src/components/PrinterLoader.tsx
src/components/PrintPartsPanel.tsx
src/components/PrintPlan.tsx
src/components/PrintReadyPanel.tsx
src/components/RenderLoader.tsx
src/components/StageTracker.tsx
src/components/Studio.tsx
src/components/TopBar.tsx
src/components/VersionRail.tsx
src/design/__tests__/tokens.test.ts
src/design/Scaler.tsx
src/design/tokens.ts
src/instrumentation.ts
src/lib/__tests__/agentEvent.test.ts
src/lib/__tests__/demoPrompts.test.ts
src/lib/__tests__/fileName.test.ts
src/lib/__tests__/mockStream.test.ts
src/lib/__tests__/promptSuggestions.test.ts
src/lib/__tests__/viewModel.glb.test.ts
src/lib/__tests__/viewModel.printplan.test.ts
src/lib/__tests__/viewModel.printready.test.ts
src/lib/__tests__/viewModel.test.ts
src/lib/agentEvent.ts
src/lib/agentStream.ts
src/lib/clarify.ts
src/lib/demoPrompts.ts
src/lib/fileName.ts
src/lib/insforge.ts
src/lib/mockStream.ts
src/lib/projects.ts
src/lib/promptSuggestions.ts
src/lib/searchStream.ts
src/lib/useSpeechToText.ts
src/lib/viewModel.ts
src/server/__tests__/agentMemory.test.ts
src/server/__tests__/bambuPrint.test.ts
src/server/__tests__/bin.test.ts
src/server/__tests__/blender.wrapStage.test.ts
src/server/__tests__/clarify.test.ts
src/server/__tests__/engineRoute.test.ts
src/server/__tests__/fixtures/printables-sample.ts
src/server/__tests__/fusion.test.ts
[70 more files omitted for size]
```

### Dependencies

- package.json: @anthropic-ai/sdk@^0.105.0, @arizeai/openinference-instrumentation-anthropic@^0.1.13, @arizeai/openinference-semantic-conventions@^2.5.0, @browserbasehq/sdk@^2.14.0, @fontsource/bricolage-grotesque@^5.2.10, @fontsource/jetbrains-mono@^5.2.8, @fontsource/space-grotesk@^5.2.10, @insforge/sdk@^1.4.0, @opentelemetry/api@^1.9.1, @opentelemetry/exporter-trace-otlp-proto@^0.219.0, @opentelemetry/resources@^2.8.0, @opentelemetry/sdk-trace-base@^2.8.0, @opentelemetry/sdk-trace-node@^2.8.0, @opentelemetry/semantic-conventions@^1.41.1, @react-three/drei@^10.7.7, @react-three/fiber@^9.6.1, @sentry/nextjs@^9.47.1, @sentry/profiling-node@^9.47.1, @tailwindcss/postcss@^4, @testing-library/jest-dom@^6.9.1, @testing-library/react@^16.3.2, @testing-library/user-event@^14.6.1, @types/node@^20.19.43, @types/react@^19, @types/react-dom@^19, @types/three@^0.184.1, @vitejs/plugin-react@^6.0.2, eslint@^9, eslint-config-next@16.2.9, jsdom@^29.1.1, next@16.2.9, react@19.2.4, react-dom@19.2.4, redis@^6.0.0, tailwindcss@^4, the-token-company@^0.3.1, three@^0.184.0, three-stdlib@^2.36.1, typescript@^5, vitest@^4.1.8

### Recent commits (newest first)

- Merge pull request #37 from vraj00222/devin/1782050741-pitch-sponsor-tracks
- docs: add sponsor track pitches, judging criteria sections, and pitch lines to PITCH.md
- Merge pull request #36 from vraj00222/devin/1782048708-sponsor-pitch
- docs: add 'Why We Should Win' sponsor track pitches
- Merge pull request #35 from vraj00222/devin/1782047875-pitch-script
- docs: add winning pitch script for Cal AI Hackathon demo
- Merge pull request #34 from vraj00222/devin/1782047142-premium-engine-names
- feat: rebrand engine names to premium product-focused labels
- Merge pull request #33 from vraj00222/devin/1782045347-sentry-integration
- feat: integrate Sentry SDK — error monitoring, tracing, profiling, structured logs
- Merge pull request #32 from vraj00222/devin/1782044667-arize-readme
- docs: add Arize AX to sponsor table + env vars + PROGRESS
- Merge pull request #31 from vraj00222/devin/1782044516-arize-tracing
- feat: add Arize AX tracing + LLM evaluator (sponsor: Arize)
- Merge pull request #30 from vraj00222/devin/1782043810-showcase-page
- Add /showcase page with 24 AI-generated 3D models across all 4 engines
- Merge pull request #29 from vraj00222/devin/1782042623-nvidia-speedup
- Merge pull request #28 from vraj00222/feat/blender5x-printable-fuse
- perf: NVIDIA path ~2-4× faster — remove redundant inspect, reduce retries, skip double-enrichment
- fix: Blender engine reliability — sanitize regex, boolean-union base, TTC retry, repairBpy context

## Key source files (fetched from GitHub, selected and truncated for size)

### AGENTS.md

```markdown
# AGENTS.md

<!-- INSFORGE:START -->
## InsForge backend

This project uses [InsForge](https://insforge.dev): an all-in-one, open-source Postgres-based backend (BaaS) that gives this app a database, authentication, file storage, edge functions, realtime, an AI model gateway, and payments through one platform.

- **Project:** **claude-hardware** (API base `https://f7c2td39.us-west.insforge.app`)
- **Skills:** these InsForge skills are installed for supported coding agents. Reach for them before implementing any InsForge feature instead of guessing the API:
  - `insforge`: app code with the `@insforge/sdk` client (database CRUD, auth, storage, edge functions, realtime, AI, email, and Stripe payments).
  - `insforge-cli`: backend and infrastructure via the `insforge` CLI (projects, SQL, migrations, RLS policies, storage buckets, functions, secrets, payment setup, schedules, deploys).
  - `insforge-debug`: diagnosing failures (SDK/HTTP errors, RLS denials, auth and OAuth issues) and running security or performance audits.
  - `insforge-integrations`: wiring external auth providers (Clerk, Auth0, WorkOS, Better Auth, etc.) for JWT-based RLS, or the OKX x402 payment facilitator.
  - `find-skills`: discovering additional skills on demand.
- **Credentials:** app code reads keys from `.env.local`; the CLI reads `.insforge/project.json`. Never hardcode or commit keys.

Key patterns:

- Database inserts take an array: `insert([{ ... }])`.
- Reference users with `auth.users(id)`; use `auth.uid()` in RLS policies.
- For storage uploads, persist both the returned `url` and `key`.
<!-- INSFORGE:END -->

```

### DEMO.md

```markdown
# DEMO — live, prompt-driven

The app boots to an empty "describe anything" state. The demo IS typing a real prompt and
watching the model actually build (real generation, not a script).

## Setup (before the demo)

1. `npm run dev` → http://localhost:3000
2. Ensure `ANTHROPIC_API_KEY` is in `.env.local` (restart dev server after adding)
3. Optional: open OpenSCAD on `tools/_watch/model.scad` (Automatic Reload) for the dual-view wow
4. Optional: connect Blender (N-panel → BlenderMCP → Connect) for live organic builds

## Beat 1 — Describe → watch it BUILD (~30s, OpenSCAD)

Open `/` → "Start designing" → skip sign-in → type or click "a gear with 24 teeth"

The agent feed shows: plan → write_openscad → render_preview (per stage) → mesh appears step-by-step
in the viewport. If OpenSCAD is open alongside, the SAME build appears in the native app.

**Line**: "You describe it; Claude designs it — with real threads, real gears, real tolerances."

### Best OpenSCAD prompts (verified working)
- "a simple phone stand" → 6 stages, cable slot, angled back, gussets
- "M10 hex bolt with threads" → real BOSL2 threaded_rod, 17mm across-flats
- "a gear with 24 teeth" → real spur_gear from BOSL2
- "a soap dish with drainage holes" → multi-stage parametric

## Beat 2 — Switch engines (~60s, NVIDIA or Blender)

Click the engine picker → **NVIDIA** → type "a chubby sitting dragon"

The clarify card asks style/wings/size. Pick options → Generate.
NVIDIA NIM TRELLIS produces a textured 3D model. The viewport shows it with color/texture.

**Line**: "Same app, different engine. NVIDIA NIM for characters, OpenSCAD for engineering."

### Best NVIDIA prompts
- "a chubby sitting dragon" → textured, detailed
- "a cute owl figurine" → smooth, organic
- "a tiny astronaut" → character with detail

### Best Blender prompts
- "a tiny rocket ship" → 3 stages, fins + nozzles + nose cone
- "a mushroom" → organic, smooth
- "a chess pawn" → clean turned shape

## Beat 3 — Print readiness (~10s)

After any model builds, the Print Center shows dynamic stats (grams, time, layers).
Click **"Prepare for print"** → readiness score, 4 checks, auto-orientation, and downloadable
STL/OBJ/3MF/G-code files.

**Line**: "Not just shapes — print-ready files. Oriented, checked, sliced for your Bambu A1."

## Beat 4 — Model search (Browserbase)

Click "Find an existing model" → search "benchy" → curated + live Printables results.
Click "Use this" on 3DBenchy → imports into the studio with Print Brain analysis.

**Line**: "Why generate when a great model already exists? Browserbase searches the web for you."

## Beat 5 — Refine in place

After a model builds, type "make it twice as tall" → v2 appears. Click v1/v2 in the version rail.

**Line**: "Edit by talking. Every version is saved."

## DO-NOT-BREAK

- Prompt (typed or example chip) → real `/api/generate` → step-by-step mesh in the viewport
- The same stages writing to `tools/_watch/model.scad` for the native-app watch
- Send-to-printer button → pri
[truncated — 540 more characters]
```

### package.json

```
{
  "name": "claude-hardware",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint",
    "test": "vitest run",
    "test:watch": "vitest"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.105.0",
    "@arizeai/openinference-instrumentation-anthropic": "^0.1.13",
    "@arizeai/openinference-semantic-conventions": "^2.5.0",
    "@browserbasehq/sdk": "^2.14.0",
    "@fontsource/bricolage-grotesque": "^5.2.10",
    "@fontsource/jetbrains-mono": "^5.2.8",
    "@fontsource/space-grotesk": "^5.2.10",
    "@insforge/sdk": "^1.4.0",
    "@opentelemetry/api": "^1.9.1",
    "@opentelemetry/exporter-trace-otlp-proto": "^0.219.0",
    "@opentelemetry/resources": "^2.8.0",
    "@opentelemetry/sdk-trace-base": "^2.8.0",
    "@opentelemetry/sdk-trace-node": "^2.8.0",
    "@opentelemetry/semantic-conventions": "^1.41.1",
    "@react-three/drei": "^10.7.7",
    "@react-three/fiber": "^9.6.1",
    "@sentry/nextjs": "^9.47.1",
    "@sentry/profiling-node": "^9.47.1",
    "next": "16.2.9",
    "react": "19.2.4",
    "react-dom": "19.2.4",
    "redis": "^6.0.0",
    "the-token-company": "^0.3.1",
    "three": "^0.184.0",
    "three-stdlib": "^2.36.1"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@testing-library/jest-dom": "^6.9.1",
    "@testing-library/react": "^16.3.2",
    "@testing-library/user-event": "^14.6.1",
    "@types/node": "^20.19.43",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "@types/three": "^0.184.1",
    "@vitejs/plugin-react": "^6.0.2",
    "eslint": "^9",
    "eslint-config-next": "16.2.9",
    "jsdom": "^29.1.1",
    "tailwindcss": "^4",
    "typescript": "^5",
    "vitest": "^4.1.8"
  }
}

```

### src/app/page.tsx

```typescript
import { redirect } from "next/navigation";

// `/` is the landing page, served via a rewrite to /landing.html (see next.config.ts). This redirect
// is the fallback if the rewrite is ever bypassed. The studio itself lives at /app behind the auth gate.
export default function Page() {
  redirect("/landing.html");
}

```

### src/app/layout.tsx

```typescript
import type { Metadata } from "next";
import "./globals.css";

export const metadata: Metadata = {
  title: "Claude Hardware",
  description: "Describe anything — get a print-ready file for your printer.",
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

```

### src/app/app/page.tsx

```typescript
import { Scaler } from "@/design/Scaler";
import { Studio } from "@/components/Studio";
import { AuthGate } from "@/components/AuthGate";

// The studio behind the Google auth gate. The landing (/landing.html) deep-links its CTAs here.
export default function AppPage() {
  return (
    <Scaler>
      <AuthGate>
        <Studio />
      </AuthGate>
    </Scaler>
  );
}

```

### src/server/modelSearch/index.ts

```typescript
import type { ModelSearchProvider } from "./types";
import { browserbaseConfigured } from "./bb";
import { fallbackSearch } from "./fallback";
import { browserbaseSearch } from "./browserbase";

const fallbackProvider: ModelSearchProvider = { search: (q, limit) => fallbackSearch(q, limit) };
const browserbaseProvider: ModelSearchProvider = { search: (q, limit) => browserbaseSearch(q, limit) };

/** Browserbase when configured, else the zero-key curated fallback. */
export function pickModelSearch(): ModelSearchProvider {
  return browserbaseConfigured ? browserbaseProvider : fallbackProvider;
}

export type { ModelResult, ModelSearchProvider } from "./types";

```

### src/server/meshgen/index.ts

```typescript
import { rodinProvider } from "./rodin";
import { nimProvider } from "./nim";
import { proceduralProvider } from "./procedural";
import type { MeshGenProvider, MeshGenRequest, MeshGenResult } from "./types";
export type { MeshGenRequest, MeshGenResult } from "./types";

/** Try providers in order; first available one that doesn't throw wins. Pure (DI'd) for testing. */
export async function runProviders(providers: MeshGenProvider[], req: MeshGenRequest): Promise<MeshGenResult> {
  let lastErr: unknown;
  for (const p of providers) {
    try { if (await p.available(req)) return await p.generate(req); }
    catch (e) { lastErr = e; /* fan down to the next provider */ }
  }
  throw lastErr ?? new Error("no meshgen provider available");
}

/** Production order: NVIDIA NIM (cloud, textured) → procedural (zero-key). Rodin is OFF by default
 *  (its free-trial key returns API_INSUFFICIENT_FUNDS); set ENABLE_RODIN=1 once the Hyper3D trial is
 *  topped up (or the addon is in FAL_AI mode) to restore the live-build-in-Blender path as primary. */
export function generateMesh(req: MeshGenRequest): Promise<MeshGenResult> {
  const providers = process.env.ENABLE_RODIN === "1"
    ? [rodinProvider, nimProvider, proceduralProvider]
    : [nimProvider, proceduralProvider];
  return runProviders(providers, req);
}

```

### src/app/projects/page.tsx

```typescript
"use client";
import { useCallback, useEffect, useRef, useState } from "react";
import { getActiveStore, latest, type Project, type AsyncProjectStore } from "@/lib/projects";
import { C, FONT } from "@/design/tokens";

const fmtDate = (t: number) =>
  new Date(t).toLocaleDateString(undefined, { month: "short", day: "numeric" }) +
  " · " +
  new Date(t).toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" });

// Demo: only surface projects generated on Jun 20–21, 2026 (today + tomorrow); hide older clutter.
const DEMO_FROM = new Date(2026, 5, 20).getTime(); // Jun 20, 2026 00:00 local
const DEMO_TO = new Date(2026, 5, 22).getTime();   // Jun 22, 2026 00:00 local (exclusive)
const inDemoWindow = (t: number) => t >= DEMO_FROM && t < DEMO_TO;

export default function ProjectsPage() {
  const [projects, setProjects] = useState<Project[]>([]);
  const [loaded, setLoaded] = useState(false);
  const storeRef = useRef<AsyncProjectStore | null>(null);

  const refresh = useCallback(async () => {
    if (!storeRef.current) storeRef.current = await getActiveStore();
    const all = await storeRef.current.list();
    setProjects(all.filter((p) => inDemoWindow(p.createdAt)));
    setLoaded(true);
  }, []);
  useEffect(() => { void refresh(); }, [refresh]);

  const remove = useCallback(async (id: string) => {
    const s = storeRef.current ?? (storeRef.current = await getActiveStore());
    await s.remove(id);
    void refresh();
  }, [refresh]);

  return (
    <div style={{ height: "100vh", overflowY: "auto", background: C.canvas, color: C.text, fontFamily: FONT.sans, fontWeight: 500 }}>
      <div style={{ maxWidth: 1100, margin: "0 auto", padding: "40px 28px 80px" }}>
        {/* header */}
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 28 }}>
          <a href="/app" style={{ display: "flex", alignItems: "center", textDecoration: "none" }}>
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img src="/logo.png" alt="Claude Hardware" style={{ height: 58, width: "auto", display: "block" }} />
          </a>
          <a href="/app" style={{ display: "flex", alignItems: "center", gap: 6, height: 38, padding: "0 16px", borderRadius: 9999, background: C.accent, color: C.printBtnInk, textDecoration: "none", fontWeight: 700, fontSize: 13.5 }}>
            <span style={{ fontSize: 16, marginTop: -1 }}>+</span> New project
          </a>
        </div>

        <h1 style={{ fontSize: 34, fontWeight: 800, letterSpacing: "-0.03em", margin: "0 0 4px" }}>Your projects</h1>
        <p style={{ fontFamily: FONT.mono, fontSize: 12.5, color: C.faint, margin: "0 0 28px" }}>
          {projects.length} saved · everything you&apos;ve described and built
        </p>

        {loaded && projects.length === 0 ? (
          <div style={{ border: `1px dashed ${C.border}`, borderRadius: 16, padding: "64px 24px", textAlign: "center", background: C.surface }}>
            <div style={{ fontSize: 18, fontWeight: 700, marginBottom: 6 }}>No projects yet</div>
            <div style={{ fontFamily: FONT.mono, fontSize: 12.5, color: C.faint, marginBottom: 18 }}>describe something and watch it build — it&apos;ll save here automatically</div>
            <a href="/app" style={{ color: C.accentWeak, textDecoration: "none", fontWeight: 700 }}>Describe your first model →</a>
          </div>
        ) : (
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(248px, 1fr))", gap: 16 }}>
            {projects.map((p) => {
              const v = latest(p);
              return (
                <a key={p.id} href={`/app?project=${p.id}`} style={{ display: "block", textDecoration: "none", color: "inherit", border: `1px solid ${C.borderSub}`, borderRadius: 14, background: C.surface, overflow: "hidden" }}>
                  {/* thumbnail: layer-line motif over the viewport tone */}
                  <div style={{ height: 132, background: C.viewportBg, position: "relative", borderBottom: `1px solid ${C.borderSub}`, display: "flex", alignItems: "center", justifyContent: "center" }}>
                    <div style={{ position: "absolute", inset: 0, backgroundImage: `repeating-linear-gradient(0deg, ${C.layer}55 0 1px, transparent 1px 7px)`, opacity: 0.6 }} />
                    <span style={{ position: "relative", fontFamily: FONT.mono, fontSize: 11, color: C.text2, background: C.surface, border: `1px solid ${C.borderSub}`, borderRadius: 7, padding: "4px 9px" }}>
                      {v ? `${v.stages} build steps` : "model"}
                    </span>
                  </div>
                  <div style={{ padding: "12px 13px 13px" }}>
                    <div style={{ fontSize: 14.5, fontWeight: 600, lineHeight: "19px", marginBottom: 7, display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden" }}>{p.title}</div>
                    <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", fontFamily: FONT.mono, fontSize: 10.5, color: C.faint }}>
                      <span>{fmtDate(p.updatedAt)}</span>
                      {v?.estimate && <span>{v.estimate.grams}g · {v.estimate.layers}L</span>}
                    </div>
                    <button
                      onClick={(e) => { e.preventDefault(); remove(p.id); }}
                      style={{ marginTop: 10, width: "100%", height: 28, borderRadius: 7, border: `1px solid ${C.borderSub}`, background: "transparent", color: C.faint, cursor: "pointer", fontFamily: FONT.mono, fontSize: 11 }}
                    >
                      delete
                    </button>
                  </div>
                </a>
              );
            })}
          </div>
        )}
      </div>
    </div>
  );
}

```

### src/app/profile/page.tsx

```typescript
"use client";
import { useCallback, useEffect, useRef, useState } from "react";
import { getActiveStore, latest, type Project, type AsyncProjectStore } from "@/lib/projects";
import { getCurrentUser, signOut, insforgeConfigured, type AuthUser } from "@/lib/insforge";
import { C, FONT } from "@/design/tokens";

const fmtDate = (t: number) =>
  new Date(t).toLocaleDateString(undefined, { month: "short", day: "numeric" }) +
  " · " +
  new Date(t).toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" });

export default function ProfilePage() {
  const [user, setUser] = useState<AuthUser | null>(null);
  const [projects, setProjects] = useState<Project[]>([]);
  const [loaded, setLoaded] = useState(false);
  const [signingOut, setSigningOut] = useState(false);
  const storeRef = useRef<AsyncProjectStore | null>(null);

  useEffect(() => {
    let cancelled = false;
    (async () => {
      const [u, store] = await Promise.all([getCurrentUser(), getActiveStore()]);
      if (cancelled) return;
      storeRef.current = store;
      setUser(u);
      setProjects(await store.list());
      setLoaded(true);
    })();
    return () => { cancelled = true; };
  }, []);

  // Sign out → back to the landing (`/`). signOut is a no-op when InsForge is unconfigured.
  const onSignOut = useCallback(async () => {
    setSigningOut(true);
    try { await signOut(); } finally { window.location.href = "/"; }
  }, []);

  const name = user?.profile?.name?.trim() || user?.email || "Guest";
  const initial = (user?.profile?.name || user?.email || "?").trim().charAt(0).toUpperCase();

  return (
    <div style={{ height: "100vh", overflowY: "auto", background: C.canvas, color: C.text, fontFamily: FONT.sans, fontWeight: 500 }}>
      <div style={{ maxWidth: 1100, margin: "0 auto", padding: "40px 28px 80px" }}>
        {/* header */}
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 28 }}>
          <a href="/app" style={{ display: "flex", alignItems: "center", textDecoration: "none" }}>
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img src="/logo.png" alt="Claude Hardware" style={{ height: 58, width: "auto", display: "block" }} />
          </a>
          <a href="/app" style={{ display: "flex", alignItems: "center", gap: 6, height: 38, padding: "0 16px", borderRadius: 9999, border: `1px solid ${C.border}`, background: C.surface, color: C.text, textDecoration: "none", fontWeight: 700, fontSize: 13.5 }}>
            ← Studio
          </a>
        </div>

        <h1 style={{ fontSize: 34, fontWeight: 800, letterSpacing: "-0.03em", margin: "0 0 24px" }}>Account</h1>

        {/* account card */}
        <div style={{ display: "flex", alignItems: "center", gap: 18, border: `1px solid ${C.borderSub}`, borderRadius: 16, background: C.surface, padding: "20px 22px", marginBottom: 36 }}>
          {user?.profile?.avatar_url ? (
            // eslint-disable-next-line @next/next/no-img-element
            <img src={user.profile.avatar_url} alt="" style={{ width: 56, height: 56, borderRadius: "50%", objectFit: "cover", display: "block", flex: "none" }} />
          ) : (
            <span style={{ width: 56, height: 56, borderRadius: "50%", background: C.accent, color: "#fff", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 24, fontWeight: 700, flex: "none" }}>{initial}</span>
          )}
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ fontSize: 18, fontWeight: 700, lineHeight: 1.2, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{name}</div>
            <div style={{ fontFamily: FONT.mono, fontSize: 12, color: C.faint, marginTop: 4 }}>
              {user ? (user.email ?? "signed in") : insforgeConfigured ? "not signed in" : "local workspace (no account)"}
            </div>
          </div>
          {user ? (
            <button
              onClick={onSignOut}
              disabled={signingOut}
              style={{ flex: "none", height: 38, padding: "0 18px", borderRadius: 9999, border: `1px solid ${C.border}`, background: "transparent", color: C.text, cursor: signingOut ? "default" : "pointer", fontFamily: FONT.sans, fontWeight: 700, fontSize: 13.5, opacity: signingOut ? 0.6 : 1 }}
            >
              {signingOut ? "Signing out…" : "Sign out"}
            </button>
          ) : (
            <a href="/app" style={{ flex: "none", display: "flex", alignItems: "center", height: 38, padding: "0 18px", borderRadius: 9999, background: C.accent, color: C.printBtnInk, textDecoration: "none", fontWeight: 700, fontSize: 13.5 }}>
              Sign in
            </a>
          )}
        </div>

        {/* their projects */}
        <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", marginBottom: 16 }}>
          <h2 style={{ fontSize: 20, fontWeight: 800, letterSpacing: "-0.02em", margin: 0 }}>Your projects</h2>
          <a href="/projects" style={{ fontFamily: FONT.mono, fontSize: 12, color: C.accentWeak, textDecoration: "none", fontWeight: 700 }}>
            view all →
          </a>
        </div>

        {loaded && projects.length === 0 ? (
          <div style={{ border: `1px dashed ${C.border}`, borderRadius: 16, padding: "48px 24px", textAlign: "center", background: C.surface }}>
            <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 6 }}>No projects yet</div>
            <div style={{ fontFamily: FONT.mono, fontSize: 12.5, color: C.faint, marginBottom: 16 }}>describe something and watch it build — it&apos;ll save here automatically</div>
            <a href="/app" style={{ color: C.accentWeak, textDecoration: "none", fontWeight: 700 }}>Describe your first model →</a>
          </div>
        ) : (
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(248px, 1fr))", gap: 16 }}>
            {projects.map((p) => {
              const v 
[truncated — 1731 more characters]
```

[161 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]