CLI ยท commit 849340c

standup

Daily standup & weekly report from your actual work

Download standup.zip
Quickstart
unzip standup.zip && cd standup
uv venv .venv && uv pip install -p .venv/bin/python -e '.[dev]'
.venv/bin/python -m pytest -q

standup

Writes your daily standup (Yesterday / Today / Blockers) or a weekly manager report from what you actually did. It scans your git repos and an optional checklist file, reduces everything to a compact deterministic digest, and asks Claude Haiku 5.5 only to phrase it. Diffs are never sent.

Features

Install

cd standup
uv venv .venv && uv pip install -p .venv/bin/python -e '.[dev,clipboard]'
export ANTHROPIC_API_KEY=sk-ant-...   # only needed for live runs

Quickstart

.venv/bin/standup --dry-run                      # what would be sent, no key needed
.venv/bin/standup                                # daily standup (needs API key)
.venv/bin/standup week --format slack --copy

Reproducible demo workspace (two repos, a TODO file, config):

examples/make_demo.sh /tmp/standup-demo
GIT_CONFIG_GLOBAL=/dev/null .venv/bin/standup --config /tmp/standup-demo/config.toml --today 2026-10-16 --dry-run

Usage

standup [day|week] [--config FILE] [--root DIR]... [--todo FILE] [--since YYYY-MM-DD]
        [--today YYYY-MM-DD] [--allow GLOB]... [--deny GLOB]... [--format markdown|slack|plain]
        [--tone concise|detailed] [--model ID] [--no-paths] [--no-subjects] [--copy]
        [--dry-run] [--no-cache] [--verbose]

Dry run (real output, no API key needed)

$ standup --config /tmp/standup-demo/config.toml --today 2026-10-16 --dry-run
...
--- user ---
Git activity digest:

MODE: day
PERIOD: 2026-10-15 to 2026-10-16 (today)
TOTALS: 7 commits in 2 repos, 5 files, +7/-0, 1 merges, 1 active days
REPO billing-api
  [PAY-142 branch feature/PAY-142-refunds] 4 commits, 2 wip/fixup collapsed, 2 files, +4/-0
    - PAY-142 add refund model
    - PAY-142 refund endpoint and validation
    paths: src/billing/ (2)
  [branch main] 1 commits, 1 files, +1/-0
    - Bump retry limit for webhook delivery (#88)
    paths: src/billing/
REPO dashboard-web
  [UI-7 branch main] 2 commits, 2 files, +2/-0
    - UI-7 fix chart tooltip overflow
    - UI-7 add tooltip tests
    paths: web/ (2)
MERGED:
  - billing-api: PR #88: Bump retry limit for webhook delivery
UNCOMMITTED:
  - billing-api on main: 0 staged, 0 modified, 1 untracked (src/billing/scratch.py)
POSSIBLE BLOCKERS (from TODO):
  - waiting on finance for refund policy sign-off
PLAN CANDIDATES:
  - continue PAY-142 in billing-api (branch feature/PAY-142-refunds, 4 commits so far)
  - finish uncommitted changes in billing-api (branch main)
  - TODO: write refund runbook
  - TODO: waiting on finance for refund policy sign-off

--- model: claude-haiku-5-5 | estimated input tokens: ~425 | no API call made ---

Final output (requires a real key; wording below is illustrative of the model's phrasing, the layout is produced by the formatter)

## Yesterday
- Built the refund model and endpoint for PAY-142, including validation
- Raised the webhook retry limit to 5 (PR #88)
- Fixed chart tooltip overflow in dashboard-web and added tests (UI-7)

## Today
- Continue PAY-142 refunds and clean up uncommitted scratch work
- Write the refund runbook

## Blockers
- Waiting on finance for refund policy sign-off

--format slack renders *Yesterday* headings and bullets; --format plain uses YESTERDAY headings and - bullets. standup week adds a title, summary, themes, impact and a "Numbers" section computed in Python (not by the model).

With --verbose a line like [live] input=1000 output=200 cost=$0.000200 goes to stderr; re-runs with identical input print [cached] and cost nothing.

Configuration

~/.config/standup/config.toml (see examples/config.toml):

key meaning
roots directories walked for repos (default ~/development)
max_depth levels below a root to search (default 3)
emails alias author emails, in addition to git config user.email and each repo's local email
holidays ISO dates skipped by last-working-day logic
todo_file markdown checklist to diff
allow / deny fnmatch globs on repo name or path
send_paths / send_subjects set false to keep paths or commit subjects out of the prompt
ticket_regex ticket id pattern (default \b[A-Z][A-Z0-9]+-\d+\b)
model default claude-haiku-5-5 (also STANDUP_MODEL / --model)

Environment: ANTHROPIC_API_KEY, STANDUP_CACHE_DIR (default ~/.cache/standup), STANDUP_STATE_DIR (default ~/.local/state/standup, holds TODO snapshots).

Privacy

Only the digest shown by --dry-run is sent: commit subjects, file paths (grouped by directory), counts, TODO item text. Never diffs or file contents. Use --no-paths, --no-subjects, --deny to reduce it further.

Architecture

src/standup/
  config.py      config file, identity, cache/state dirs
  discovery.py   find_repos, allow/deny
  workdays.py    last_working_day, week_start
  gitlog.py      git log/status parsing, WIP and merge detection
  cluster.py     clustering by ticket/branch, path summaries
  todo.py        checklist parsing, snapshot diff
  digest.py      builds and renders the digest (the only thing sent)
  ai.py          prompts, JSON schemas, Haiku call, cache, cost, error mapping
  formatters.py  markdown/slack/plain rendering
  cli.py         click entry point

See docs/design.md for algorithms and worked examples.

Tests

.venv/bin/pytest -q     # 47 tests, fake Claude client, temp git repos

Limitations