Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

standupinator

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.

License: MIT Node

Quick start · Personas · How it works · Deploy


Why this exists

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.


Personas

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 extraction

Persona files are plain Markdown — fork and edit to match your team voice.


How it works

The big picture

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"]
Loading

Capture loop (during the day)

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.
Loading

Three ways to capture:

Method Entry point
Channel posts POST /api/slack/events
Slash command /standupapi/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

Brief loop (weekday mornings)

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"]
Loading

Quick start

git clone https://github.com/wicolian/standupinator.git
cd standupinator
cp .env.example .env
npm install

1. Database

Create a free Postgres project on Neon or Supabase:

DATABASE_URL=postgresql://user:pass@host/db?sslmode=require
npm run db:migrate

2. GitHub

Edit 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.

3. Slack (minimum: outbound)

Set SLACK_WEBHOOK_URL from an Incoming Webhook pointed at your DM.

For capture (inbound), see docs/SLACK_DAILYDIGEST_SETUP.md.

4. Test

npm run brief:dry    # preview in terminal, no Slack
npm run brief        # post to Slack + save DB

Environment variables

Copy .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.


LLM synthesis

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 data

Deploy (Vercel)

vercel 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 --prod

Cron: 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

API endpoints

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

Commands

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

Project layout

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

Security & going public

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.


Example morning brief

# 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_

Contributing

Issues and PRs welcome. Please don't commit secrets or personal config.

  1. Fork the repo
  2. Create a feature branch
  3. Run npm run brief:dry to verify
  4. Open a PR

License

MIT © wicolian

About

Headless standup bot — GitHub, Figma, meetings → Slack brief with persona-aware LLM synthesis

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages