Files
Trey T 8afbf68f4c docs: real README and update Claude auth section in CLAUDE.md
- Replaces the create-next-app boilerplate README with a real one:
  what the project does, the 8-agent pipeline table, tech stack, local
  dev, configuration via the Settings UI, multi-tenant App model,
  Unraid deployment, and repo layout.
- CLAUDE.md "Claude Auth in Docker" no longer claims .env is the only
  way to set the OAuth token — describes the Settings page as primary,
  .env as bootstrap fallback, mentions the Test button, and notes that
  Anthropic exposes no UI to list/revoke setup-tokens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 21:13:45 -05:00

5.2 KiB

@AGENTS.md

Marketing Command Center

Next.js 16 app that uses Claude Code CLI as a subprocess to run an 8-agent marketing pipeline. Generates ads, videos, copy, and research for mobile apps.

Tech Stack

  • Next.js 16.2.1, React 19, TypeScript 5, TailwindCSS 4, shadcn/ui
  • Prisma 7.5 with SQLite (better-sqlite3 adapter)
  • NextAuth 5 (credentials provider, trustHost: true required)
  • Claude Code CLI spawned via child_process.spawn() (NOT the Anthropic SDK)
  • Playwright for HTML-to-PNG rendering
  • Remotion for video generation (in pipeline/remotion-ad/)

How Claude Is Used

The app spawns claude -p <prompt> --output-format stream-json as a subprocess. Auth comes from the user's Claude Max subscription. In Docker, auth is via CLAUDE_CODE_OAUTH_TOKEN env var (an access token extracted from macOS Keychain Claude Code-credentials entry). See lib/claude.ts for all spawn logic.

Local Development

npm install
npx prisma db push
npx prisma db seed   # creates admin user + honeyDue app
npm run dev           # http://localhost:3000

Default login: admin@localhost / admin123 (set via ADMIN_EMAIL/ADMIN_PASSWORD env vars).

Unraid Docker Deployment

The app runs on an Unraid server at marketing.88oakapps.com behind Nginx Proxy Manager.

Architecture

  • App source: /mnt/user/appdata/marketing/ (disposable, rsync from dev machine)
  • Persistent data: /mnt/user/downloads/marketing/ (survives app updates)
    • db/ - SQLite database
    • outputs/ - generated campaign assets (ads, videos, copy)
    • knowledge/ - brand identity, platform guidelines

Unraid docker-compose.yml (lives on Unraid, NOT the repo version)

The repo docker-compose.yml includes Postiz services for local dev. The Unraid version is app-only:

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NEXTAUTH_URL=http://localhost:3000
      - NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
      - ADMIN_EMAIL=${ADMIN_EMAIL:-admin@localhost}
      - ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin123}
      - DATABASE_URL=file:./prisma/data/marketing.db
      - PIPELINE_ROOT=/app/pipeline
      - CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN}
    volumes:
      - /mnt/user/downloads/marketing/db:/app/prisma/data
      - /mnt/user/downloads/marketing/outputs:/app/pipeline/outputs
      - /mnt/user/downloads/marketing/knowledge:/app/pipeline/knowledge

Deploying Updates to Unraid

# Sync code (excludes env/compose so Unraid config isn't overwritten)
rsync -avz \
  --exclude='node_modules' --exclude='.next' --exclude='prisma/data' \
  --exclude='pipeline/outputs' --exclude='pipeline/remotion-ad/.next' \
  --exclude='pipeline/remotion-ad/node_modules' \
  --exclude='.env' --exclude='.env.local' --exclude='docker-compose.yml' \
  --delete \
  ./ unraid:/mnt/user/appdata/marketing/

# Rebuild and restart
ssh unraid "cd /mnt/user/appdata/marketing && docker compose down && docker compose build app && docker compose up -d"

Claude Auth in Docker

Claude Max uses OAuth — tokens stored in macOS Keychain locally, but the headless Docker container has no keychain, so it needs the OAuth access token explicitly. The token is rotated from the Settings page (/settings → Claude card); the DB value is injected into the spawned claude subprocess env at launch time and overrides CLAUDE_CODE_OAUTH_TOKEN from the container env. The Test button on the same card validates the token by hitting the Anthropic messages API and surfaces 401s as "Token expired or invalid".

To mint a token:

  1. Run claude setup-token locally, open the magic link in browser
  2. Extract the access token: security find-generic-password -s "Claude Code-credentials" -a "$(whoami)" -w | python3 -c 'import sys,json; print(json.load(sys.stdin)["claudeAiOauth"]["accessToken"])'
  3. Paste it into Settings → Claude → Save (no rebuild required)

CLAUDE_CODE_OAUTH_TOKEN in .env still works as a bootstrap fallback (used on first boot before any DB value is saved, or if the DB is wiped). There is no Anthropic UI to list or revoke setup-token outputs — they live ~1 year. Treat each one like a password.

Volume Permissions

Host directories must be owned by UID 1000 (node user in container):

ssh unraid "chown -R 1000:1000 /mnt/user/downloads/marketing/{db,outputs,knowledge}"

Dockerfile Notes

  • Base image: node:20-slim (NOT alpine — Playwright needs apt/Debian)
  • Startup script (start.sh) runs prisma db push and prisma db seed before node server.js
  • Seed is idempotent (uses upsert), safe to run on every restart

Key Files

  • lib/claude.ts - Claude CLI spawn logic, pipeline orchestration, chat sessions
  • lib/auth.ts - NextAuth config (trustHost: true for reverse proxy)
  • lib/scanner.ts - scans pipeline output directories for assets
  • prisma/schema.prisma - 8 models: User, App, Campaign, AgentRun, Asset, ClaudeSession, TrendReport, Setting
  • prisma/seed.ts - creates admin user + honeyDue app
  • pipeline/CLAUDE.md - agent system docs (read this for pipeline details)
  • pipeline/skills/ - 8 agent skill definitions
  • pipeline/knowledge/ - brand assets & guidelines

SSH

Unraid is accessible via ssh unraid (configured in ~/.ssh/config on dev machine).