Wake up to a standup script in Slack — built from your git, Figma, meetings, and notes.
No dashboard. No Jira sync. A headless worker collects what you actually did, synthesizes a conversational brief, and posts it to your DM every weekday morning.
Standups shouldn't require reconstructing your day from scattered tabs. standupinator connects the tools you already use:
| Source | What it pulls |
|---|---|
| GitHub | Commits, WIP, branch activity across any repo |
| Figma | Named version history on tracked files |
| Otter / Fireflies | Commitments you made, asks directed at you |
| Slack | Manual updates you drop throughout the day |
Every weekday morning you get a Slack message with:
| Section | Purpose |
|---|---|
| You can prolly say | 3–5 sentences — read aloud at standup |
| Today's tasks | Max 5, prioritized |
| Your Slack updates | Context you posted since last brief |
| Figma yesterday | Design activity |
| Commitments / asks | From meeting transcripts |
| Blockers | Real blockers only |
Capture work all day in Slack → wake up to a synthesized brief built from git, Figma, meetings, and your own notes.
Set STANDUP_PERSONA to tune voice and priorities. Each persona loads a dedicated prompt from prompts/personas/.
| Persona | STANDUP_PERSONA |
Best for |
|---|---|---|
| Designer | designer |
Figma + UI implementation, design–dev handoff |
| Software engineer | sde |
PRs, reviews, architecture, shipping |
| Executive | csuite |
Strategy, outcomes, decisions — concise exec altitude |
| Sales | sales |
Pipeline, deals, calls, follow-ups |
# .env
STANDUP_PERSONA=sde
STANDUP_DISPLAY_NAME=Alex # used in LLM prompts and meeting extractionPersona files are plain Markdown — fork and edit to match your team voice.
flowchart TB
subgraph YOU["You (during the day)"]
POST["Post in #standup-capture\nor /standup"]
end
subgraph SOURCES["External sources"]
GH["GitHub commits + WIP"]
FIG["Figma version history"]
MTG["Otter / Fireflies transcripts"]
MAN["inputs/manual-tasks.md"]
end
subgraph APP["standupinator (Vercel + Node)"]
IN["Inbound APIs"]
CRON["Cron · weekday mornings"]
COL["Collectors"]
SYN["Synthesizer\nLLM or rules"]
OUT["Slack delivery"]
end
subgraph DB["PostgreSQL"]
T1["tasks · manual_updates"]
T2["daily_briefs · activity_events"]
T3["commitments · asks"]
end
POST --> IN --> T1
CRON --> COL
GH & FIG & MTG & MAN --> COL
T1 --> COL --> SYN --> T2 & T3 --> OUT --> DM["Your Slack DM"]
sequenceDiagram
actor You
participant Slack as #standup-capture
participant API as api/slack/events
participant Parse as parse-message.mjs
participant DB as PostgreSQL
You->>Slack: done: shipped settings. today: onboarding.
Slack->>API: signed message event
API->>Parse: parse into buckets
Parse->>DB: manual_updates + tasks
API->>Slack: thread reply — Got it, logged.
Three ways to capture:
| Method | Entry point |
|---|---|
| Channel posts | POST /api/slack/events |
| Slash command | /standup → api/slack/commands |
| HTTP | POST /api/capture |
Parsing
| Input | Bucket |
|---|---|
done: or "Finished…" |
Yesterday + marks matching tasks done |
today: |
Today's tasks |
tomorrow: |
Next brief |
blocker: |
Blockers section |
| Freeform | Notes woven into synthesis |
flowchart LR
A["/api/cron/daily"] --> B["run-daily.mjs"]
B --> C1["github.mjs"]
B --> C2["figma.mjs"]
B --> C3["meetings"]
B --> C4["slack-capture"]
C1 & C2 & C3 & C4 --> D["synthesize.mjs"]
D --> E{"LLM_SYNTHESIS"}
E -->|auto/always| F["Claude / GPT"]
E -->|off| G["Rules fallback"]
F & G --> H["Slack DM + DB"]
git clone https://github.com/wicolian/standupinator.git
cd standupinator
cp .env.example .env
npm installCreate a free Postgres project on Neon or Supabase:
DATABASE_URL=postgresql://user:pass@host/db?sslmode=require
npm run db:migrateEdit config/repos.json:
{
"repos": [
{
"owner": "your-org",
"repo": "your-app",
"branches": ["main"],
"authors": ["your-github-username"],
"primary": true
}
]
}Add GITHUB_TOKEN — fine-grained PAT with Contents: Read on tracked repos.
Set SLACK_WEBHOOK_URL from an Incoming Webhook pointed at your DM.
For capture (inbound), see docs/SLACK_DAILYDIGEST_SETUP.md.
npm run brief:dry # preview in terminal, no Slack
npm run brief # post to Slack + save DBCopy .env.example → .env. Never commit .env.
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
For persistence | Postgres connection string |
CRON_SECRET |
Vercel cron | Random string for /api/cron/daily |
GITHUB_TOKEN |
Recommended | PAT with repo read access |
STANDUP_AUTHOR |
Default your-github-username |
GitHub username to filter commits |
STANDUP_PERSONA |
Default designer |
designer · sde · csuite · sales |
STANDUP_DISPLAY_NAME |
Optional | Name used in prompts and meeting extraction |
FIGMA_TOKEN |
Optional | Scope file_versions:read |
MEETING_SOURCE |
Default otter-mcp |
otter-mcp · fireflies · otter |
APP_BASE_URL |
For OAuth | Public URL for Otter connect callback |
SLACK_WEBHOOK_URL |
Outbound | Incoming webhook → morning brief |
SLACK_BOT_TOKEN |
Inbound | Bot token for capture + thread replies |
SLACK_SIGNING_SECRET |
Inbound | Verify Slack requests |
LLM_SYNTHESIS |
Default auto |
auto · always · off |
ANTHROPIC_API_KEY |
Optional | Claude synthesis (preferred) |
OPENAI_API_KEY |
Optional | GPT synthesis alternative |
Full list in .env.example.
Voice rules live in prompts/synthesis.md + prompts/personas/{persona}.md.
LLM_SYNTHESIS |
Behavior |
|---|---|
auto (default) |
LLM if API key set; rules fallback on failure |
always |
Require LLM — errors if no key or API fails |
off |
Rules only |
npm run brief:dry # rules preview
ANTHROPIC_API_KEY=sk-ant-... npm run synthesize:test # LLM smoke test
LLM_SYNTHESIS=always npm run brief:dry # force LLM on real datavercel link
vercel env add DATABASE_URL
vercel env add SLACK_WEBHOOK_URL
vercel env add CRON_SECRET
# ... add remaining vars from .env.example
vercel deploy --prodCron: vercel.json runs /api/cron/daily at 30 2 * * 1-5 UTC (weekday mornings).
Manual trigger:
curl -H "Authorization: Bearer $CRON_SECRET" \
https://your-app.vercel.app/api/cron/daily| Method | Path | Auth | Purpose |
|---|---|---|---|
GET |
/api/health |
— | Health check |
GET |
/api/cron/status |
— | Last brief run status |
GET/POST |
/api/cron/daily |
Bearer CRON_SECRET |
Run daily pipeline |
GET |
/api/otter/connect |
— | Otter OAuth start |
GET |
/api/otter/callback |
— | Otter OAuth callback |
POST |
/api/slack/events |
Slack signature | Capture channel messages |
POST |
/api/slack/commands |
Slack signature | /standup slash command |
POST |
/api/slack/interactions |
Slack signature | Block Kit actions |
POST |
/api/capture |
Bearer CAPTURE_SECRET |
HTTP capture |
| Command | Description |
|---|---|
npm run brief |
Full pipeline → Slack + DB |
npm run brief:dry |
Preview in terminal |
npm run brief:llm |
Force LLM synthesis (dry run) |
npm run synthesize:test |
LLM test with fixture data |
npm run db:migrate |
Apply Postgres schema |
npm run collect:git |
Debug GitHub collector |
npm run collect:figma |
Debug Figma collector |
npm run otter:connect |
Otter MCP OAuth helper |
standupinator/
├── api/ # Vercel serverless routes
├── config/ # repos.json, figma-files.json
├── db/schema.sql # Postgres schema
├── prompts/
│ ├── synthesis.md # Shared synthesis rules
│ └── personas/ # designer · sde · csuite · sales
├── src/
│ ├── collectors/ # github, figma, otter, fireflies
│ ├── deliver/ # Slack outbound + Block Kit
│ ├── ingest/ # Slack inbound parse + capture
│ ├── synthesizer/ # LLM + rules + personas
│ └── worker/ # run-daily.mjs orchestrator
└── vercel.json # Cron schedule
Before deploying or open-sourcing your fork:
| Risk | Mitigation |
|---|---|
.env / tokens in git |
Never commit .env. Use Vercel env vars. Rotate anything ever pushed. |
config/repos.json local paths |
Remove local_path or use env-only paths — exposes machine layout. |
| Slack signing secret / bot token | Treat as passwords. Regenerate if leaked. |
CRON_SECRET / CAPTURE_SECRET |
Use long random strings. Without them, endpoints are open if deployed without auth check. |
| Database URL | Full read/write access to your briefs and tasks. |
| Meeting transcripts | Brief content may include customer names — review before sharing logs. |
| GitHub PAT | Use fine-grained, read-only, repo-scoped tokens. |
This repo ships example configs only — no real credentials, org names, or personal paths.
# Daily brief — Tue, Jul 7
## You can prolly say
Yesterday I shipped the settings refresh and synced my branch with main.
Most of the onboarding work is still WIP — about 12 files. Today I'm
finishing that flow and following up on the API review.
## Today's tasks
- [ ] Finish onboarding flow in code
- [ ] Batch-commit WIP when coherent
- [ ] Get sign-off on launch checklist
## Your Slack updates
- 2026-07-06: done: settings sidebar. today: onboarding flow.
_Synthesized via anthropic/claude-sonnet-4-20250514_Issues and PRs welcome. Please don't commit secrets or personal config.
- Fork the repo
- Create a feature branch
- Run
npm run brief:dryto verify - Open a PR