An AI agent that turns your GitHub commits into social media posts. Built on the Eve framework with Next.js.
Daily Cron (6 PM UTC):
- Fetches your latest public GitHub commits via the GitHub Events API + Repos API fallback
- Filters out trivial commits (merges, typos, bumps, WIP)
- Generates an X/Twitter post (casual, ≤280 chars) and a LinkedIn post (professional, problem/solution framing)
- Saves them as "pending" to Neon DB
- Notifies you via Slack with a link to the dashboard
On-Demand Chat:
- Open the chat widget on the dashboard
- Ask to generate posts anytime — no need to wait for cron
- Posts appear instantly on the Pending tab
Dashboard:
- Review pending posts with Post/Reject buttons
- Approved posts are published directly to X/Twitter and LinkedIn
- Light/Dark theme with smooth animations
┌─────────────────────────────────────────────────────────────┐
│ Vercel │
│ │
│ Cron (6 PM UTC) Chat Widget │
│ → daily-posts.ts → /api/chat │
│ → lib/github.ts → lib/github.ts │
│ → OpenRouter LLM → OpenRouter LLM │
│ → Neon DB (pending) → Neon DB (pending) │
│ │
│ Next.js Dashboard (/dashboard) │
│ → Tabs: Pending / Posted / Rejected │
│ → Post: Twitter API + LinkedIn API │
│ → Reject: mark as rejected │
│ → Chat: generate posts on demand │
│ │
│ Neon DB (PostgreSQL) │
│ → posts table: id, content, platform, status, timestamps │
│ → status: pending → posted / rejected │
│ │
│ Eve Agent (mounted via withEve()) │
│ → HTTP channel for API access │
│ → Tools: post-to-twitter, post-to-linkedin │
│ → Schedule: daily-posts.ts │
└─────────────────────────────────────────────────────────────┘
commit-voice/
├── agent/ # Eve agent
│ ├── agent.ts # Model config (defineAgent)
│ ├── instructions.md # System prompt (always loaded)
│ ├── channels/
│ │ ├── eve.ts # HTTP channel (for API/TUI access)
│ │ └── slack.ts # Slack notifications (Vercel Connect)
│ ├── tools/
│ │ ├── fetch-github-commits.ts # GitHub Events API tool
│ │ ├── get-env-vars.ts # Read env vars from runtime
│ │ ├── post-to-twitter.ts # Twitter API v2 posting
│ │ └── post-to-linkedin.ts # LinkedIn UGC Posts API
│ ├── skills/
│ │ ├── dev-skill.md # Eve development troubleshooting
│ │ └── post-generation.md # Post writing guidelines
│ └── schedules/
│ └── daily-posts.ts # Daily cron handler (6 PM UTC)
├── app/ # Next.js App Router
│ ├── layout.tsx # Root layout (ThemeProvider + ToastProvider)
│ ├── page.tsx # Landing page
│ ├── not-found.tsx # 404 page
│ ├── globals.css # CSS variables (light/dark themes)
│ ├── dashboard/
│ │ ├── page.tsx # Dashboard with Post/Reject UI
│ │ ├── PostCard.tsx # Animated post card component
│ │ ├── ChatWidget.tsx # Floating conversational chat
│ │ ├── ChatMessage.tsx # Chat bubble component
│ │ ├── ThemeToggle.tsx # Light/dark theme switch
│ │ ├── EmptyState.tsx # Animated empty state
│ │ ├── loading.tsx # Skeleton loading
│ │ └── error.tsx # Animated error boundary
│ ├── login/
│ │ ├── page.tsx # Login page (Suspense wrapper)
│ │ └── LoginForm.tsx # Login form with password input
│ └── api/
│ ├── chat/
│ │ └── route.ts # POST — generate posts on demand
│ ├── auth/
│ │ ├── login/route.ts # POST — verify password, set cookie
│ │ └── logout/route.ts # POST — clear cookie
│ └── posts/
│ ├── route.ts # GET posts (filter by status)
│ └── [id]/
│ ├── approve/route.ts # POST approve → publish
│ └── reject/route.ts # POST reject
├── components/
│ └── ui/ # shadcn/ui components
│ ├── button.tsx
│ ├── card.tsx
│ ├── tabs.tsx
│ ├── dialog.tsx
│ ├── toast.tsx
│ ├── use-toast.ts
│ ├── skeleton.tsx
│ └── switch.tsx
├── lib/ # Shared code
│ ├── db.ts # Neon DB connection + queries
│ ├── github.ts # GitHub API (Events + Repos fallback)
│ ├── utils.ts # cn() helper (clsx + tailwind-merge)
│ ├── themes.ts # Theme configuration constants
│ ├── auth.ts # Auth helpers (verify, cookies)
│ ├── schema.sql # Posts table schema
│ └── social.ts # Twitter/LinkedIn API helpers
├── middleware.ts # Auth protection (Next.js middleware)
├── tailwind.config.ts # Tailwind v3 config
├── next.config.mjs # withEve() wrapper
├── postcss.config.mjs # PostCSS config
├── package.json
├── tsconfig.json
├── .npmrc # legacy-peer-deps=true
├── .env.example
└── .gitignore
| Component | Technology |
|---|---|
| Framework | Eve 0.12.0 (Vercel) + Next.js 14 |
| Language | TypeScript + Markdown |
| Model | owl-alpha via OpenRouter |
| Database | Neon DB (serverless PostgreSQL) |
| GitHub | GitHub REST API (Events + Repos) |
| Slack | Vercel Connect |
| Twitter API v2 (twitter-api-v2) | |
| LinkedIn UGC Posts API | |
| UI | shadcn/ui + Tailwind CSS + framer-motion |
| Icons | lucide-react |
| Theming | next-themes (light/dark) |
| Scheduling | Vercel Cron Jobs (UTC) |
| Node.js | 24.x |
- Node.js 18+ (Vercel builds with Node 24 automatically)
- Vercel CLI (
npm i -g vercel) - GitHub Personal Access Token (public_repo scope)
- OpenRouter API key
- Slack workspace
- Neon DB account (free tier)
- Twitter Developer App with write access
- LinkedIn Developer App with
w_member_socialscope
# 1. Clone and install
# Note: --legacy-peer-deps is required because Eve framework has strict peer dependency requirements
# that conflict with some packages. The .npmrc file already includes this setting.
git clone https://github.com/anusbutt/commit-voice.git
cd commit-voice
npm install --legacy-peer-deps
# 2. Set up Neon DB
# Go to https://neon.tech, create a project, run the SQL from lib/schema.sql
# 3. Configure environment
cp .env.example .env
# Edit .env with your values
# 4. Set up Slack via Vercel Connect
vercel connect create slack --triggers
vercel connect attach <uid> --triggers --trigger-path /eve/v1/slack --yes
# 5. Deploy
vercel deploy --prod
# 6. Set env vars in Vercel
vercel env add GITHUB_TOKEN
vercel env add GITHUB_USERNAME
vercel env add SLACK_CHANNEL_ID
vercel env add OPENROUTER_API_KEY
vercel env add DATABASE_URL
vercel env add DASHBOARD_PASSWORD
vercel env add NEXT_PUBLIC_APP_URL
vercel env add TWITTER_API_KEY
vercel env add TWITTER_API_SECRET
vercel env add TWITTER_ACCESS_TOKEN
vercel env add TWITTER_ACCESS_SECRET
vercel env add LINKEDIN_PERSON_ID
vercel env add LINKEDIN_ACCESS_TOKEN
# 7. Redeploy with env vars
vercel deploy --prod| Variable | Required | Description |
|---|---|---|
GITHUB_TOKEN |
Yes | GitHub Personal Access Token (public_repo scope) |
GITHUB_USERNAME |
Yes | Your GitHub username |
SLACK_CHANNEL_ID |
Yes | Slack channel ID for notifications |
OPENROUTER_API_KEY |
Yes | OpenRouter API key |
DATABASE_URL |
Yes | Neon DB connection string |
DASHBOARD_PASSWORD |
Yes | Password for dashboard login |
NEXT_PUBLIC_APP_URL |
Yes | Your app URL (e.g., https://commit-voice.vercel.app) |
TWITTER_API_KEY |
Yes | Twitter App Consumer Key |
TWITTER_API_SECRET |
Yes | Twitter App Consumer Secret |
TWITTER_ACCESS_TOKEN |
Yes | Twitter App Access Token |
TWITTER_ACCESS_SECRET |
Yes | Twitter App Access Token Secret |
LINKEDIN_PERSON_ID |
Yes | Your LinkedIn Person ID |
LINKEDIN_ACCESS_TOKEN |
Yes | LinkedIn OAuth 2.0 Access Token (expires every 60 days — regenerate at https://developers.linkedin.com/) |
Visit https://your-app.vercel.app/dashboard to see pending posts. Click Post to publish to X/Twitter or LinkedIn. Click Reject to skip.
Click the floating chat bubble in the bottom-right corner. The chat understands:
- Greetings: "hi", "hello", "hey"
- Help: "help", "what can you do"
- Generation: "generate posts", "create a tweet", "make LinkedIn posts"
- Confirmations: "yes", "sure", "go ahead"
npx eve dev https://commit-voice.vercel.app# Get posts
curl https://commit-voice.vercel.app/api/posts?status=pending
# Approve a post (publishes to platform)
curl -X POST https://commit-voice.vercel.app/api/posts/1/approve
# Reject a post
curl -X POST https://commit-voice.vercel.app/api/posts/1/reject
# Generate posts via chat
curl -X POST https://commit-voice.vercel.app/api/chat \
-H "Content-Type: application/json" \
-d '{"message":"Generate posts for my latest commits"}'The schedule fires automatically at 6 PM UTC. Posts are saved as pending for dashboard review.
- Human approval required — Posts must be explicitly approved via the dashboard before publishing (constitution principle)
- Direct OpenRouter provider — Bypasses Vercel AI Gateway for model flexibility
- Neon DB for persistence — Survives serverless cold starts, unlike file-based storage
- Next.js + Eve via withEve() — Single deployment for both frontend and agent
- Separate tools per platform — Twitter and LinkedIn have different auth mechanisms
- Dual GitHub API strategy — Events API first (fast), Repos API fallback (reliable)
- Conversational chat — Chat widget handles greetings and questions without calling the API
Apache-2.0