| name | meeting-standup |
| description | Turns your AI agent into a STANDUP MANAGER: it joins your Google Meet, Zoom, or Teams call as an audio-only bot and runs the whole daily standup. Calls on each person in the call by name, keeps time, tracks blockers across days, then posts a summary with action items to the chat and stays on to manage it. Use when someone wants their agent to run, facilitate, or host a daily standup or scrum.
|
| argument-hint | <meeting-url> [--auto] [--name <bot>] [--voice <id>] (or --local to dry-run) |
| user-invocable | true |
| license | MIT |
Standup Manager — a skill that runs your team's standup, then manages the summary
This file is the rulebook for how you behave in the meeting. Read it FULLY before every run —
especially in a fresh session — and act exactly as it says. The engine handles the mechanics; every
judgment call (blockers, reflections, the summary, requests, when to leave) is yours, and this file is
where those rules live. Don't improvise a different protocol mid-call.
For managers and team leads. This turns your AI agent into the person who runs standup. It joins
the call as an audio-only bot, greets the team, goes around one at a time by name, keeps time
(so a 5-person standup is ~7 minutes, not 20), and keeps a running summary with action items. When
the round's done it posts the summary in the chat and stays — it takes a latecomer's update (with a
raised hand), reads the summary back if someone asks, and leaves only when asked, or when everyone else
has (always stopping billing). It also remembers blockers across days and follows up on them.
You are the brain — but stay OUT of the round's way (this is the important part)
The bot runs the round itself, fast and deterministically — it auto-starts the moment anyone speaks,
times each turn, drops a quick filler ("Got it, thanks") between people, and never waits on you. That is
deliberate: you (an AI agent) have real latency, so if you sat in the turn-by-turn loop the meeting would
lag and desync. So don't drive the round. Do your thinking asynchronously and in standby.
Your two real jobs: (A) as updates stream in, quietly record blockers; (B) when the round ends,
compose and post one clean summary, then handle anything said in standby (a fix, "what's the summary?",
"you can leave"). Everything is a tiny file link: the bot appends to link/heard.jsonl; you append one
line to link/commands.jsonl. Start watching the moment you launch:
tail -n +1 -f link/heard.jsonl
(POSIX; on Windows see the alternatives just below.) The bot runs what you append within ~0.12s.
Windows alternatives:
Lines the bot sends you (link/heard.jsonl) — each has an id, a kind, and context:
kind | when | what you do |
|---|
update | a person's turn ended | the bot gives a quick generic ack itself and moves on — you do NOT need to reflect every turn. Just note their update for your running summary, and if it implies a blocker send {"cmd":"blocker","for":"<name>","text":"<blocker>"}. (A one-line personalised reflection {"cmd":"reflect","for":"<name>","text":"…"} is a nice bonus ONLY if you can land it instantly; otherwise skip it — the generic ack has it covered.) If no_update is true, the bot already says a short "no update from " line — just note it. |
round_done | everyone's done; the bot has just said a short "let me pull the summary together" to cover the gap | compose + post the summary YOURSELF, synthesised in your own words — NEVER the raw transcript read back. Build each person's line as their update arrives, so by now it is ready to send instantly. In order: (1) {"cmd":"chat","text":"…"} the FULL clean summary + action items; (2) {"cmd":"say","text":"…"} a spoken recap — a brief line for each person (a little of what they are on, not just a headline) plus the blockers; keep each line tight so it still speaks quickly, with the full detail in the chat; (3) if there are blockers, ask the room ONCE for ALL of them in a single question (never one-per-blocker): {"cmd":"say","text":"A couple of blockers came up: <b1>, and <b2>. Anyone got a workaround for either?","listen":true}, then attach an answer with solution only if it is a genuine, relevant fix for that blocker — ignore banter/off-topic chatter, and if no real fix comes, leave the blocker open. Ask for fixes ONLY here — never mid-standup |
standby | someone spoke while standing by | interpret: "what's the summary?" → {"cmd":"say","text":"<the FULL summary, spoken>"}; "you can head out" → {"cmd":"leave"}; asks about a PREVIOUS standup or past blockers ("what was blocking us last time?") → open the most recent standups/standup-<date>.md yourself and answer from it with a say (cross-day chasing is OFF by default, so this on-demand read IS the memory — never volunteer it, only when asked); a fix someone offers → ; — or to give them a proper spoken turn; else |
Commands you can send (link/commands.jsonl, one JSON per line, echo the id when replying to one):
| command | effect |
|---|
{"cmd":"blocker","for":"<name>","text":"<blocker>"} | record a blocker you spotted in an update (async — doesn't interrupt anything) |
{"cmd":"record","for":"<name>","text":"…"} | store a person's update text (e.g. one they posted in the chat) so it lands in the summary + file |
{"cmd":"reflect","for":"<name>","text":"…"} | say a one-line reflection of their update and advance to the next person |
{"cmd":"chat","text":"…"} | post your composed summary + action items into the meeting chat |
{"cmd":"say","text":"…","listen":true} | speak (read the summary, greet a latecomer, ask the room about a blocker). "listen":true keeps forwarding standby speech so you catch the reply |
{"cmd":"solution","for":"<name>","text":"<fix>"} | attach a fix to that person's blocker (turns it into the action item) |
{"cmd":"ask","person":"Sam"} | take a specific person's standup (e.g. a latecomer) |
{"cmd":"resolve","for":"<name>","cleared":true|false} | close (or keep) a cross-day blocker after a follow-up |
{"cmd":"leave"} | leave the meeting — billing stops. Use when asked to head out |
{"cmd":"skip"} / {"cmd":"next"} | override the timing: skip the current person, or wrap them early. Rarely needed |
{"cmd":"say","text":"…"} / {"cmd":"none"} | speak / do nothing |
Rhythm: the bot acks each turn generically itself, so don't reflect every turn — just record any blocker and note the update for the summary. As each update lands, fold that person into your running
summary (a synthesised line in your own words, never their raw transcript), built in parallel across the
round so it never blocks anything and is already assembled by the end. When
everyone's done (round_done): chat the full summary, say a spoken summary of each person + the
blockers, then ask the room for workarounds to those blockers and attach what comes back. In standby,
answer any question, catch late fixes, and leave when asked. Never block the round beyond the
reflection — everything else is async or in standby. Never ask for a fix mid-round — only after everyone.
Two rules for the summary:
- Reflect per person; full summary at the end. After each person, mirror their update in a line ("so
you did X, blocked on Y — got it"). At the end, speak a real summary (everyone + blockers) AND put the
full thing in chat. If someone later asks "what's the summary?", read the full one aloud again.
- Attribute honestly — keep YOUR ideas separate from what people said. A workaround a person stated is
their plan. A fix you're proposing is a suggestion — label it (a separate "Suggestion (from
):" line). Never fold your own idea into a person's update or action item as though they said it.
Do this in order
0 · Preflight (once). API key — check ~/.agentcall/config.json → AGENTCALL_API_KEY env; if neither,
ask the user and write it to ~/.agentcall/config.json. Then pip install -r requirements.txt.
1 · Set the roster. In config.jsonc, fill TEAM with the people to call on, in order, using the
name each shows as in the meeting. If the user didn't give you one, ask once. (Absent roster members are
skipped and noted; anyone present who isn't on the roster still gets a turn.)
2 · (optional) Dry-run: python scripts/standup.py --local — a scripted run (round → standby →
latecomer → leave) with no meeting or billing.
3 · Launch, and immediately start watching the link:
python scripts/standup.py "https://meet.google.com/abc-def-ghi" &
tail -n +1 -f link/heard.jsonl
It appears in ~30–90s, greets, and waits for a go-ahead — which comes to you as a waiting line. Pass
--auto to skip waiting. --name / --voice override the config.
4 · Drive it. Reply start on the go-ahead; let the round run (it times itself); when round_done
arrives, post your polished summary + action items to chat; answer name-addressed questions; leave when
asked. Keep watching until the bot has left.
5 · Finish. It leaves on your leave, when everyone else leaves, or on Ctrl+C — always stopping
billing (it sends the leave command and DELETEs the call, with retries, on every exit path).
What's deterministic vs. yours
- Deterministic (the bot, no keywords): join, greet, the round-robin timing (a nudge near
PER_PERSON_SECONDS, then it moves on; a turn ends on SILENCE_ENDS_TURN of quiet), capturing each
update, the fix-beat plumbing (ask → listen → attach → park), a heuristic summary + action items to
disk and chat, raising a hand for a latecomer, the cross-day follow-up, leaving on an empty room, and
billing-safe teardown.
- Yours (the brain): judging what's a blocker, what to ask, and what's a real fix; starting on a
go-ahead; sharp action items and the polished chat summary; answering questions; leaving on request.
All interpreted by you — never by brittle keyword lists.
Token cost is bounded to decisions, not talk. Mid-turn speech is captured locally (free); the bot only
forwards to you at decision points — one update per turn, the fix beat's few utterances, name-addressed
lines, and lifecycle events. Idle chatter is dropped. So a 5-person standup is ~a dozen round-trips, not
one per sentence.
Config (config.jsonc)
TEAM (names + order) · QUESTIONS (asked as one prompt) · PER_PERSON_SECONDS / NUDGE_AT_SECONDS /
SILENCE_ENDS_TURN / NO_RESPONSE_SECONDS (the timeboxing feel) · WAIT_FOR_GO_AHEAD ·
TRACK_ACROSS_DAYS (the cross-day blocker memory) · POST_SUMMARY_TO_CHAT · OUTPUT_DIR/OUTPUT_FORMAT.
Key/name/voice come from ~/.agentcall/config.json (the shared AgentCall config; bot name falls back to
Scrum — pick a short, STT-friendly name, long ones get misheard).
How it works
standup.py spawns AgentCall's audio bridge (engine/bridge.py, mode: audio) and drives it over
stdin/stdout: it speaks via tts.speak, hears each person as a VAD-coalesced user.message
(speaker + text), posts chat via send_chat, and raises a hand via raise_hand. A single-threaded
event loop runs the round; per-turn-token timers handle the nudge / timebox / silence / no-response so a
stale timer can never fire into the next turn. What it hears goes to link/heard.jsonl; your
link/commands.jsonl replies are applied within ~0.12s. Capture, blocker heuristics, cross-day
reconciliation, action items, and the summary are all local — no model, no network beyond the meeting.
Files
meeting-standup/
├── SKILL.md README.md LICENSE requirements.txt .gitignore
├── config.jsonc the one file you edit — roster, questions, timeboxes, tracking, chat
├── scripts/
│ └── standup.py the facilitator + summary-manager engine
├── engine/
│ └── bridge.py AgentCall's bundled AUDIO bridge (join, TTS, transcription, chat) — don't edit
├── link/ the agent⇄bot link (heard.jsonl / commands.jsonl) — runtime scratch
└── standups/ daily summaries + history.json — stays local
Run from the skill root (meeting-standup/) so config.jsonc, link/, and standups/ resolve; the
script finds engine/ on its own, and the key/name come from ~/.agentcall/config.json.
Checking for updates (optional)
Optional — the skill is backwards-compatible and runs fine without ever checking. To see whether there's
a newer version, look at the source repo github.com/pattern-ai-labs/built-with-agentcall (folder
meeting-standup) and compare it with your copy. How to update depends on how it was installed:
- An agent cloned the repo →
git pull in that repo (or just re-fetch the meeting-standup folder).
- Installed via
npx skills add → re-run the same command to pull the latest:
npx skills add pattern-ai-labs/built-with-agentcall --skill meeting-standup
Nothing to configure and no version check to run — updating only swaps in newer files.