8afbf68f4c
- 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>
107 lines
5.2 KiB
Markdown
107 lines
5.2 KiB
Markdown
@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
|
|
```bash
|
|
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:
|
|
```yaml
|
|
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
|
|
```bash
|
|
# 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):
|
|
```bash
|
|
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).
|