diff --git a/CLAUDE.md b/CLAUDE.md index cecf7d7..442127e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -72,11 +72,14 @@ ssh unraid "cd /mnt/user/appdata/marketing && docker compose down && docker comp ``` ### Claude Auth in Docker -Claude Max uses OAuth, tokens stored in macOS Keychain. For headless 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` -3. The JSON has `claudeAiOauth.accessToken` — use just that value -4. Set `CLAUDE_CODE_OAUTH_TOKEN=` in the Unraid `.env` +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): diff --git a/README.md b/README.md index e215bc4..686ffd2 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,179 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# Marketing Command Center -## Getting Started +A Next.js dashboard that turns the Claude Code CLI into a marketing team. +Spawns `claude` as a subprocess to run an 8-agent pipeline that researches +trends, writes ad scripts, generates static and video creatives, drafts +platform-tuned copy, and prepares a publish manifest — all per campaign, +per app, per platform. -First, run the development server: +Used internally to drive social campaigns for the 88Oak app portfolio +(honeyDue, Reflect, etc.). Deployed on an Unraid server at +`marketing.88oakapps.com`. + +--- + +## What it does + +You create a Campaign in the UI. The web app spawns +`claude -p --output-format stream-json` once per agent, in order: + +| # | Agent | Output | +|---|---|---| +| 1 | **Trend Scout** | `trend_report.json` — viral hooks, competitor angles, formats (Tavily searches) | +| 2 | **Marketing Research** | `research_results.json` + `research_brief.md` (deep Tavily) | +| 3 | **Script Writer** | Hook/body/CTA scripts per platform, timed for video | +| 4 | **Gemini Ad Designer** | Photo-real static ads via NanoBanana MCP (Google Gemini) | +| 5 | **Poster Ad Designer** | "Museum-quality" HTML→PNG posters via Playwright | +| 6 | **Video Ad Producer** | Remotion compositions rendered to MP4 (Reels, TikTok) | +| 7 | **Copywriter** | Platform-tuned captions with hashtags | +| 8 | **Distribution** | `Publish_manifest.md` — never auto-publishes | + +Each agent reads its `pipeline/skills//SKILL.md` and the campaign's +brand/product knowledge files before producing output. Progress streams +back to the UI over SSE; completed assets are scanned into the DB and +shown in the campaign's asset gallery. + +From there: thumbs-up/down to teach style preferences (fed back into +future Gemini prompts as `reference_images`), spawn variations of a +winner, repurpose to other dimensions, or push individual assets to +[Postiz](https://postiz.com) for scheduling. + +For the agent details and folder conventions, see +[`pipeline/CLAUDE.md`](./pipeline/CLAUDE.md). + +--- + +## Tech stack + +- Next.js 16 (App Router) + React 19 + TypeScript 5 +- Tailwind 4 + shadcn/ui +- Prisma 7 over SQLite (`better-sqlite3` adapter) +- NextAuth 5 (credentials provider, `trustHost: true` for the reverse proxy) +- Playwright (HTML→PNG) +- Remotion (video rendering, project at `pipeline/remotion-ad/`) +- Claude Code CLI spawned via `child_process.spawn` (NOT the Anthropic SDK) +- NanoBanana MCP for Gemini image generation +- Tavily for web research + +--- + +## Local development ```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +npm install +npx prisma db push +npx prisma db seed # creates admin user + honeyDue app +npm run dev # http://localhost:3000 ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +Default login: `admin@localhost` / `admin123` (override with `ADMIN_EMAIL` +/ `ADMIN_PASSWORD` env vars). The Claude Max OAuth flow on your dev +machine handles auth automatically — no token needed locally as long as +you've signed into the `claude` CLI once. -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +--- -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +## Configuration -## Learn More +Every third-party credential is configured at **Settings** (`/settings`) +and stored in the `Setting` DB table. DB values override `.env` at +runtime. Each integration card has a **Test** button that hits the real +API to verify the credential. -To learn more about Next.js, take a look at the following resources: +| Integration | What it's for | +|---|---| +| **Claude** | OAuth access token for the `claude` CLI subprocess. Token expires (~1 year) — refresh here when launches start failing with auth errors. | +| **Tavily** | Web research for the Trend Scout and Research agents. | +| **Gemini** | Google Gemini powers NanoBanana MCP for static ad generation. | +| **Postiz** | Self-hosted social scheduling (Instagram, TikTok). | +| **Nextdoor** | Direct Nextdoor Ads API integration. | -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +To mint a Claude Code OAuth token (for headless / Docker use): -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +```bash +claude setup-token +# extract the access token from your keychain: +security find-generic-password -s "Claude Code-credentials" -a "$(whoami)" -w \ + | python3 -c 'import sys,json; print(json.load(sys.stdin)["claudeAiOauth"]["accessToken"])' +``` -## Deploy on Vercel +Paste the result into Settings → Claude → Save. There is no Anthropic UI +to list or revoke `setup-token` outputs — treat each one like a password. -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +--- -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +## Apps (multi-tenant) + +The same pipeline serves multiple products. Each `App` row in the DB +carries its own brand colors, brand identity markdown, product info, +platform guidelines, and learned style preferences. At launch time, +`launchPipeline` writes the active app's knowledge into +`pipeline/_knowledge/` and copies the app's `icon.png` / `phone.png` +into Remotion's `public/` directory. Switch active apps from the +sidebar. + +Per-app assets live at `pipeline/apps//`. + +--- + +## Deployment (Unraid) + +The app runs in a single Docker container behind Nginx Proxy Manager at +`marketing.88oakapps.com`. Source lives at +`/mnt/user/appdata/marketing/` (disposable, rsynced from dev). +Persistent data is bind-mounted from `/mnt/user/downloads/marketing/` +(`db/`, `outputs/`, `knowledge/`). + +```bash +# Sync source (preserves Unraid-specific .env and docker-compose.yml) +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 + restart +ssh unraid "cd /mnt/user/appdata/marketing && docker compose down \ + && docker compose build app && docker compose up -d" +``` + +Full deployment notes (`docker-compose.yml` shape, volume permissions, +Dockerfile quirks) are in [`CLAUDE.md`](./CLAUDE.md). + +--- + +## Repo layout + +``` +app/ Next.js App Router + (auth)/ Login + (dashboard)/ UI: campaigns, assets, apps, settings, trends + api/ REST endpoints (campaigns, assets, settings, files) +components/ React components incl. shadcn/ui +lib/ + claude.ts Subprocess orchestration, prompt building + scanner.ts Walks pipeline outputs into Asset rows + settings.ts DB-backed config + per-integration health checks + postiz.ts Postiz publishing + variations.ts "More like this" via Gemini + repurpose.ts Reformat to other aspect ratios + nextdoor.ts Direct Nextdoor Ads API +prisma/ + schema.prisma User, App, Campaign, AgentRun, Asset, ... + seed.ts Admin user + default app +pipeline/ + CLAUDE.md Agent pipeline architecture (read this) + skills//SKILL.md Per-agent instructions the spawned Claude reads + apps// Per-app assets (icon, phone frame, screenshots) + knowledge/ Default knowledge (overridden per-app at runtime) + outputs/_/ Generated campaign deliverables + remotion-ad/ Remotion video project +``` + +--- + +## License + +Private / internal.