- 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>
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: truerequired) - 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 databaseoutputs/- 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:
- Run
claude setup-tokenlocally, open the magic link in browser - 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"])' - 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) runsprisma db pushandprisma db seedbeforenode 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 sessionslib/auth.ts- NextAuth config (trustHost: true for reverse proxy)lib/scanner.ts- scans pipeline output directories for assetsprisma/schema.prisma- 8 models: User, App, Campaign, AgentRun, Asset, ClaudeSession, TrendReport, Settingprisma/seed.ts- creates admin user + honeyDue apppipeline/CLAUDE.md- agent system docs (read this for pipeline details)pipeline/skills/- 8 agent skill definitionspipeline/knowledge/- brand assets & guidelines
SSH
Unraid is accessible via ssh unraid (configured in ~/.ssh/config on dev machine).