diff --git a/.gitignore b/.gitignore index 1e7d287..74467cc 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,8 @@ canvas/.env .env.local # Overnight review snapshots (dated) tools/overnight-review-[0-9]*.html +# OCR working artifacts (per-archetype, not source-controlled) +archetypes/*/.ocr-notes.txt +# Phase 24: AI-generated images (never commit generated content) +assets/generated/ +canvas/assets/generated/ diff --git a/AGENTS.md b/AGENTS.md index 6af3b61..d745170 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,9 +12,34 @@ cd canvas && npm install && npm run dev - **Template library:** http://localhost:5174/ - **Brand assets served at:** `/fluid-assets/*` -Environment variables (`.env` at repo root): -- `ANTHROPIC_API_KEY` — required for generation +## Running Locally — Claude Authentication + +The agent runtime uses `@anthropic-ai/claude-agent-sdk`. Two auth paths are supported: + +**Option A — API key (CI + production):** +```bash +# Add to .env at repo root (or export in your shell) +ANTHROPIC_API_KEY=sk-ant-... +``` + +**Option B — Claude CLI login (local dev, one-time setup):** +```bash +# Install the Claude CLI if not already present, then: +claude login +# Follow the OAuth flow. Credentials are saved to ~/.claude/.credentials.json +# No API key provisioning needed; the SDK picks up the session automatically. +``` + +API key takes precedence if both are set. `GET /api/health` returns `anthropic: 'ok'` +for either path. + +## Other Environment Variables + - `VITE_FLUID_DAM_TOKEN` — optional, for DAM picker integration +- `GEMINI_API_KEY` — required for AI image generation (generate_image tool) +- `FLUID_AGENT_MODEL` — override the Claude model (default: `claude-sonnet-4-6`) +- `FLUID_DISPATCH_TRUSTED` — set to `true` to bypass ask-first tool permission prompts +- `FLUID_DAILY_COST_CAP_USD` — daily spend cap for image generation (default: `10.00`) ## Tech Stack @@ -23,7 +48,7 @@ Environment variables (`.env` at repo root): | Frontend | React 19, TypeScript 5.6, Zustand 5, Vite 6 | | Backend | Vite middleware plugin (no Express) | | Database | SQLite (better-sqlite3, WAL mode) | -| AI | Anthropic SDK with tool use, SSE streaming | +| AI | Claude Agent SDK (`@anthropic-ai/claude-agent-sdk`) with MCP tool servers, SSE streaming | | Testing | Vitest + Playwright | | Styling | Plain CSS (no Tailwind in the app itself) | diff --git a/archetypes/SPEC.md b/archetypes/SPEC.md index 025fb3e..70a1dfe 100644 --- a/archetypes/SPEC.md +++ b/archetypes/SPEC.md @@ -490,15 +490,94 @@ components/testimonial-block/ # quote + name + title (3 fields) ## Section 9: Platform Dimensions Reference -| Platform | Width (px) | Height (px) | Phase | -|----------|------------|-------------|-------| -| Instagram Square | 1080 | 1080 | 19 | -| LinkedIn Landscape | 1200 | 627 | 21 | -| One-Pager (US Letter) | 612 | 792 | 21 | +| Platform | Width (px) | Height (px) | Phase | `schema.platform` value | +|----------|------------|-------------|-------|------------------------| +| Instagram Portrait (4:5) | 1080 | 1350 | 23 | `instagram-portrait` | +| Instagram Square | 1080 | 1080 | 19 | `instagram-square` | +| LinkedIn Landscape | 1200 | 627 | 21 | `linkedin-landscape` | +| One-Pager (US Letter) | 612 | 792 | 21 | `one-pager` | -**Phase 19 scope:** Instagram Square only (`1080 × 1080`). LinkedIn and One-Pager archetypes are Phase 21. +**Phase 23 default:** Instagram Portrait (`1080 × 1350`) is the standard for all new archetypes. Instagram Square is legacy — only use on explicit request. -All Phase 19 archetypes MUST use `"width": 1080, "height": 1080` in `schema.json` and `width: 1080px; height: 1080px` on `body` in `index.html`. +New Phase 23 archetypes MUST use `"width": 1080, "height": 1350` in `schema.json`, `width: 1080px; height: 1350px` on `body` in `index.html`, and `"platform": "instagram-portrait"` as a top-level field. + +--- + +## Section 10: Self-Documenting Metadata (Phase 23 — instagram-portrait archetypes) + +Phase 23 introduces a `meta` object in `schema.json` for all `instagram-portrait` archetypes. The `meta` object makes archetypes self-documenting so `list_archetypes()` can surface them with rich discovery signals without loading HTML. + +**This section is forward-only.** Existing archetypes (Phases 19–22) are unaffected and do not need to add `meta`. + +### Required `meta` fields (instagram-portrait only) + +| Field | Type | Description | +|-------|------|-------------| +| `category` | `string` | Functional category for filtering (e.g., `"hero-photo"`, `"quote-testimonial"`) | +| `imageRole` | `"none" \| "accent" \| "background" \| "hero" \| "grid"` | How image assets are used in the layout | +| `useCases` | `string[]` (≥1) | Concrete scenarios where this archetype excels | +| `slotCount` | `number` (integer, ≥1) | Total number of editable slots (text + image fields) | + +### Recommended `meta` fields + +| Field | Type | Description | +|-------|------|-------------| +| `mood` | `string[]` | Descriptive mood tags (e.g., `["editorial", "bold"]`) | +| `contentDensity` | `"sparse" \| "moderate" \| "dense"` | How much content the layout can hold | +| `imageHints` | `object` | Guidance for selecting images from the DAM | +| `avoidCases` | `string[]` | When NOT to use this archetype | + +### Full `meta` example + +```json +{ + "archetypeId": "photo-darken-headline", + "platform": "instagram-portrait", + "width": 1080, + "height": 1350, + "meta": { + "category": "hero-photo", + "mood": ["editorial", "cinematic", "bold"], + "contentDensity": "sparse", + "imageRole": "background", + "imageHints": { + "suggestedAspect": "4:5 or wider", + "suggestedSubject": "single person, landscape, or abstract texture", + "treatment": "darken overlay 40–60% for text legibility", + "damPreference": ["lifestyle", "people", "abstract"] + }, + "useCases": [ + "Brand manifesto / mission statement", + "Launch hero with dramatic mood", + "Hiring post with team photo" + ], + "avoidCases": [ + "Data-heavy content", + "Posts needing multiple bullet points", + "Brands without strong photography in DAM" + ], + "slotCount": 3 + }, + "fields": [], + "brush": null, + "brushAdditional": [] +} +``` + +### Valid category values (Phase 23) + +| Category string | Slug group | +|----------------|------------| +| `"stat-data"` | hero-stat-45, big-number-card, stat-comparison | +| `"quote-testimonial"` | photocentric-quote, typographic-quote, book-quote-highlight | +| `"announcement"` | coming-soon-minimal, website-launch-mockup, event-promo | +| `"photo-collage"` | vintage-scrapbook, fashion-moodboard, memory-grid-4up, asymmetric-photo-collage | +| `"hero-photo"` | photo-darken-headline, split-photo-feature | +| `"tips-howto"` | numbered-tips-cover, how-to-step-card | +| `"personal-about"` | about-me-portrait, hiring-portrait-cta | +| `"product"` | product-hero-backlit, product-feature-grid, product-callout-macro | +| `"motivational"` | affirmation-note, handwritten-quote-photo | +| `"carousel-cover"` | carousel-cover-typographic | --- diff --git a/archetypes/_review.html b/archetypes/_review.html index 75e3211..33cd8a7 100644 --- a/archetypes/_review.html +++ b/archetypes/_review.html @@ -12,19 +12,40 @@ .card-left { display: flex; flex-direction: column; gap: 8px; } .card-label { font-size: 14px; font-weight: 600; color: #555; text-transform: uppercase; letter-spacing: 0.05em; } .frame-wrap { - width: 540px; height: 540px; + width: 270px; height: 338px; border: 1px solid #d0d0d0; background: #f5f5f5; overflow: hidden; position: relative; } + .frame-wrap.square { + width: 270px; height: 270px; + } + .frame-wrap.linkedin { + width: 360px; height: 188px; + } + .frame-wrap.onepager { + width: 245px; height: 317px; + } .frame-wrap iframe { - width: 1080px; height: 1080px; - transform: scale(0.5); + width: 1080px; height: 1350px; + transform: scale(0.25); transform-origin: top left; border: none; display: block; } + .frame-wrap.square iframe { + width: 1080px; height: 1080px; + transform: scale(0.25); + } + .frame-wrap.linkedin iframe { + width: 1200px; height: 627px; + transform: scale(0.3); + } + .frame-wrap.onepager iframe { + width: 612px; height: 792px; + transform: scale(0.4); + } .card-right { display: flex; flex-direction: column; gap: 6px; flex: 1; min-width: 320px; } .card-right label { font-size: 13px; font-weight: 500; color: #777; } .card-right textarea { @@ -54,7 +75,7 @@ -

Phase 19 — Archetype Review

+

Phase 19–23 — Archetype Review

@@ -66,28 +87,94 @@

Phase 19 — Archetype Review