Skip to main content

meeting-presenter

Join a Google Meet, Zoom, or Teams meeting as an AI presenter and DELIVER a presentation — slides on the bot's camera, narrated aloud, auto-advancing. Hand it a DOCUMENT (PDF, PowerPoint, or Word) or a TOPIC. A PowerPoint (already a deck) is shown as-is; a Word/PDF document is turned into a meaningful deck the agent authors from the source's text AND figures. Cross-platform and self-contained. Use when the user wants their agent to present something in a live meeting.

Informations de source

Dépôt
pattern-ai-labs/built-with-agentcall
Dernière activité de la source
15 juillet 2026 à 11:24
Langue détectée de SKILL.md
anglais
Étoiles
12
Forks
1

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Explorateur de fichiers
16 fichiers

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
meeting-presenter
description
Join a Google Meet, Zoom, or Teams meeting as an AI presenter and DELIVER a presentation — slides on the bot's camera, narrated aloud, auto-advancing. Hand it a DOCUMENT (PDF, PowerPoint, or Word) or a TOPIC. A PowerPoint (already a deck) is shown as-is; a Word/PDF document is turned into a meaningful deck the agent authors from the source's text AND figures. Cross-platform and self-contained. Use when the user wants their agent to present something in a live meeting.
argument-hint
<meeting-url> --deck <file.pdf|.pptx|.docx|deck.json> [--mode auto|show|generate] [--name <bot>] [--voice af_heart] [--pace 1.0] [--avatar-mode]
user-invocable
true
license
MIT
# Presenter — present a document (or a topic) in a meeting An AI bot **joins a meeting and presents**: by default the deck goes **big on the meeting's main stage as a screenshare**, narrated in the bot's voice and advancing by itself, with the bot's **face in a small camera tile**. (Pass `--avatar-mode` — or if the user says "join in avatar mode" — to instead show the deck *on* the camera tile, the original single-tile look.) No clicker either way. **Display modes:** *screenshare* is the default (readable full-stage slides + a presenter face); *avatar mode* (`--avatar-mode`) puts the deck on the camera tile only. Everything else — voice control, the companion remote, narration, Q&A — works identically in both. The headline use: **the user gives a meeting link and a document — PDF, PowerPoint, or Word — and the bot presents it.** You can also start from just a topic. ## Do this in order (the whole flow — same every time, no detours) **Before anything — you need something to present. Never join a meeting empty.** If the user named no document, no topic, and no existing deck, do NOT launch. First **offer what's already in `decks/`** (list those decks by title), **or ask what topic to build one from** (you can author a deck from just a topic — see B). Only move on once you have material in hand. A quick "what should I present?" beats a bot that joins with nothing to say. *(Suggesting the user's existing `decks/` and offering to build from a topic is the standard no-material flow — do it for every user, not just when you happen to know their decks.)* **0 · Preflight (once).** - **API key — check in this order, and DON'T ask if it's already there** (one config, the SAME file AgentCall uses; this is the exact order `load_api_key` uses): 1. `~/.agentcall/config.json` exists with an `api_key`? → ready. 2. `AGENTCALL_API_KEY` env var set? → ready. 3. Neither? → ask the user for their key (free at agentcall.dev/api-keys) and **write it to `~/.agentcall/config.json`** (see Setup) — creating that file exactly like AgentCall would — then continue. - **Dependencies** — `pip install -r requirements.txt` (fast; pure-Python wheels, no system tools). **1 · Turn the input into a deck. You (the agent) always own the narration — never let the bot read the slide off the screen.** Run `doc_to_deck.py` first, then look at what it produced: - **A `.pptx`/`.ppt`, or a slide-shaped PDF** → *show the real slides*, but the narration is yours: - If the file has **speaker notes on every slide** (`.pptx` only — legacy `.ppt` notes can't be read; ask for a `.pptx`), they become the narration — you're done, just present. Notes on only SOME slides → the deck is flagged; author the missing ones. - If it has **no notes**, the deck comes back flagged **`needs_narration`** with every `notes` empty (and a `decks/<name>/source.json`). **Author a spoken `notes` line per slide** (read `source.json` + glance at the slide images) before presenting — `present.py` **refuses to run** an un-authored converted deck, so there is no shortcut around this. Authoring IS the presenting. - **A `.docx`/`.doc`, a report-style PDF, or a topic** → *author a deck*: run `doc_to_deck.py --mode generate`, read **`decks/<name>/source.json`** (its text + extracted figures) and write `decks/<name>/deck.json`. **Do NOT try to open or render the file yourself** (no `pdftoppm`, no image tools) — `source.json` is your input. Keep authoring tight: one idea per slide, short bullets, natural narration — don't over-polish. - **Authored work is protected — but only in the DEFAULT deck folder.** Re-running the conversion (or `present.py --deck <same document>`) with an unchanged source **reuses the authored `deck.json`** in `decks/<name>/` — instant, nothing re-rendered, your narration survives. It only re-converts when the document itself changed (then re-author — the flag comes back). **Do NOT pass `--out <a different folder>` for a document you'll author and present** — a fresh folder has no deck to reuse, so you get an empty-notes draft (and the real authored deck goes stale). `--out` is only for throwaway/inspection conversions. Author, present, and re-present all from the default `decks/<name>/deck.json`. - Full detail + deck schema: sections **A** and **B** below. **Hard rules (each of these has burned a real session — the code enforces most of them, don't fight it):** 1. **Never one-shot a raw document into `present.py` and walk away.** Convert → check `needs_narration` → author `notes` → present. The refusal exit (code 2) is your signal to author, not an error to bypass. 2. **Never overwrite an authored `deck.json` by re-converting.** Unchanged source → it's reused automatically; only delete the deck folder if the user explicitly wants a fresh conversion. 3. **Never invent a placeholder deck or apologize on camera.** If conversion fails, present.py exits — fix the input (or install LibreOffice) and relaunch. A bot that never joins beats one that looks broken. 4. **Never leave a call running — and never hard-kill it to rejoin.** present.py stops billing on every exit path itself — but if YOU spawned it, confirm the process actually exited when the presentation ends. **To stop, restart, or rejoin, ALWAYS send `{"cmd":"leave"}` and WAIT for the process to fully exit** (you'll see `Billing stopped.` and the process ends) **before relaunching.** Killing the process (TaskStop / taskkill) to rejoin can skip the call's server-side teardown, so the old bot lingers and your fresh join lands *beside* it — two bots in the meeting (this burned a real session). A clean `{"cmd":"leave"}` is enough and never does that. 5. **The deck does NOT start by itself — YOU must reply `{"cmd":"present"}`.** The moment you launch, start tailing `link/heard.jsonl`. When anyone says "go ahead" / "you can start" / "begin", your ONLY correct action is to append `{"id":<that line's id>,"cmd":"present"}` to `link/commands.jsonl`. Nothing else starts the narration. If you don't, the bot just sits there greeting an audience that's waiting — this has happened in a real meeting. Do not get distracted mid-call, do not stop watching the file until the bot has left. (A human can rescue it by tapping **Start** on the phone remote, but that is their fallback, not your excuse.) **1b · Honor the user's brief — their instructions shape the deck AND how you drive it.** When the user says anything beyond "present this", capture it and apply it. It lands in one of two places: - **Authoring-time (bake into `deck.json`)** — anything about *content or delivery*: - *"keep it short / ~5 min / one-liner per slide"* → write shorter `notes`; the slide hold is length-based, so shorter notes = a faster talk. *"go deep"* → richer `notes`. - *"say ‹X› on the pricing slide"*, *"open with a joke"*, *"emphasise ‹Y›"* → put that line into that specific slide's `notes` so it's spoken exactly where intended. - *"skip the roadmap slide"* → drop/merge it while authoring. - **Live-driving policy (you enforce while running)** — anything about *reacting in the room*: - *"don't answer questions about ‹Z› / pricing / headcount"* → when such a question comes through `heard.jsonl`, DON'T answer it: reply with a brief deflection (`{"cmd":"say","text":"Good question — let's take that offline."}`) instead of a real answer. *"defer all Q&A to the end"* → acknowledge and keep presenting rather than answering mid-deck. - *"pause on slide 3 for discussion"*, *"only advance when I say so"* → adjust how eagerly you send `next`/`present`. Keep a short mental note of these rules for the whole session — they persist across the call, not just the first slide. If a request is ambiguous, ask the user before launching. **2 · (optional) Preview** — `present.py --local --deck <deck>` prints a localhost URL to watch it before going live. **3 · Launch live, in the background** — `python scripts/present.py "<url>" --deck <deck> &` (name/voice come from `~/.agentcall/config.json`; `--name`/`--voice` override per-run). The bot takes 30–90s to appear, then greets and waits. **4 · Drive it — start watching immediately.** Tail `link/heard.jsonl` and reply in `link/commands.jsonl` (full protocol under **Voice control**). It won't *start* until you reply `present` to a "go ahead". **5 · Finish** — it auto-leaves and stops billing when everyone leaves, on the time cap, or on a `leave` command. If you spawned it, make sure the process exits. ## Two modes (this is the key idea) | Mode | What it does | Default for | |---|---|---| | **show** | Renders the document's REAL pages/slides to images and shows them unchanged | `.pptx`/`.ppt`, and slide-shaped PDFs | | **generate** | Parses the document, then YOU author a meaningful deck (titles, bullets, spoken narration) and pull in the source's own figures | `.docx`/`.doc`, dense/report PDFs, and topics | Rule of thumb: **a file that's already a slide deck → show it. A document (prose) → generate a deck from it.** `auto` (the default) decides by file type, and for PDFs by page shape (landscape + sparse text = a deck → show; portrait + dense = a document → generate). Override with `--mode show|generate`. The user's intent always wins ("present my slides exactly" → show; "summarize this into a deck" → generate). ## A) Show a deck as-is (PowerPoint, or a slide-style PDF) Show mode keeps every slide's real design (a full-bleed image of the actual slide). What it **says** depends on whether the file carries speaker notes: ```bash python scripts/doc_to_deck.py "/path/deck.pptx" # render slides + detect notes ``` - **Has speaker notes on every slide** (`.pptx`) → they're the narration. Just present: ```bash python scripts/present.py "<MEETING_URL>" --deck decks/deck/deck.json ``` - **No notes — or notes on only SOME slides** → the deck is flagged `needs_narration` (the un-noted slides would otherwise show in dead silence). **Author a spoken `notes` line for every slide that lacks one** (from `decks/<name>/source.json` + the slide images), then present the authored `deck.json`. This is what makes it a *presenter* and not a screen-reader. **`present.py` REFUSES to present a `needs_narration` deck until every slide has `notes`** (it exits with an error pointing you at `source.json`). There is **no** "read the slide text aloud" fallback — by design. So never hand a raw notes-less file to `present.py` expecting it to talk: convert, author the `notes`, then present the authored `deck.json`. The refusal is the safety net; authoring is the job. ## B) Generate a meaningful deck (Word, dense PDF, or a topic) A document isn't slides — showing its pages makes a poor talk. So **you build the deck.** 1. **Convert to get the source content + figures:** ```bash python scripts/doc_to_deck.py "/path/file.docx" --mode generate # writes decks/<name>/deck.json — a mechanical draft with EMPTY notes, flagged needs_narration; # present.py REFUSES it until you author the notes (that's the next step, not optional) # and decks/<name>/source.json ← the parsed sections + extracted images, for you to author from # and decks/<name>/img/ ← the document's own figures, already pulled out ``` 2. **Author `deck.json` from `source.json`** — this is the important part. Do it well: - **Outline first, then fill.** Make a slide per main idea. Build a mental checklist of the document's sections/headings and make sure **every key point lands on a slide** — don't drop things. - Each slide: a short `title`, ≤6 short `bullets`, and `notes` = natural **spoken** narration (1–4 sentences; acronyms spelled phonetically like "A.P.I." so TTS says them right). - **Reuse the source's figures.** `source.json` lists images extracted from the document (in `sections[].images` and `all_images`). Put the relevant one in a slide's `image` field — the slide then shows your bullets beside that real figure (a split layout). Don't invent images. - Open with a one-line intro slide; close with a wrap-up (the bot leaves after the last slide). 3. **Preview and present** (sections C & D). For a topic with no file, skip the conversion and just author `decks/<name>.json` directly. Deck JSON schema (text slide, image slide, or both together): ```json { "title": "My Talk", "slides": [ { "title": "Intro", "notes": "Spoken intro." }, { "title": "A point", "bullets": ["short phrase", "another"], "notes": "Narration." }, { "title": "With a figure", "bullets": ["what it shows"], "image": "fig1.png", "notes": "Narration." }, { "image": "slide3.png", "notes": "A full-bleed real slide (show mode)." } ] } ``` (`bullets`/`notes`/`image` all optional; aliases `points`→bullets, `say`→notes; a title-only slide is a cover.) ## C) Preview (recommended) ```bash python scripts/present.py --local --deck decks/<name>/deck.json # or decks/<name>.json, or a raw file ``` Prints a `http://localhost:PORT/?ws=local` URL — open it to watch the slides render and auto-advance, with no meeting. ## D) Present live ```bash python scripts/present.py "<MEETING_URL>" --deck <file-or-deck> [--mode show|generate] ``` **Live meetings are interactive by default (consent-driven):** the bot joins, introduces itself, and **waits** until someone tells it to begin. Two ways to steer it: - **By voice → routed to YOU (the brain).** There is **no keyword matching in the code.** Whatever a participant says — once they address the bot by name, or during a short follow-up window — is handed to you to interpret, and you reply with one command. Protocol below. - **By the companion control page (direct).** A phone/browser page (Prev / Pause / Next / tap-a-slide / Restart / Dismiss) whose link the bot drops in the meeting chat, and ALSO prints to the console (`▸ Controls page: …`) — **relay the console URL to the user directly**; chat delivery on Meet can be unreliable. Buttons are unambiguous and act **in-process, instantly** (they don't go through you), so they're the reliable fallback and the way to drive it with no agent attached. The bot **auto-leaves and stops billing** when everyone else leaves, on a hard cap, or on a clean exit — an orphaned bot can't run up cost. Add **`--auto`** to present immediately without asking; `--pace <s>` sets the gap between slides. ## Voice control — YOU are the brain (the important part) In interactive mode the bot forwards what it hears to a file link and runs the command you write back. No hardcoded phrases: *you* understand the request and decide the action. Run it in the **background** and loop: 1. Start it (interactive is the default for live): `python scripts/present.py "<url>" --deck <file> &` It prints two paths: **`link/heard.jsonl`** (bot → you) and **`link/commands.jsonl`** (you → bot). **Start watching immediately** — the greeting plays on its own, but the presentation won't *start* until you reply to the first "go ahead". Read only NEW lines (track a byte/line offset, or block on the file — don't re-process old lines), and reply within **~45s** (after that the deck resumes on its own). The efficient, event-like way to watch, no busy-polling: ```bash tail -n +1 -f link/heard.jsonl # streams each new utterance as a line; handle it, then append your reply to link/commands.jsonl ``` (`&`/`tail` are POSIX — on Windows use your background-run facility and `Get-Content link/heard.jsonl -Wait`.) present.py runs whatever you append to `commands.jsonl` within ~0.3s. 2. **When the bot is addressed by name** (*"Presenter, …"*) — or during the **~20s follow-up window** after any exchange — a line is appended to **`link/heard.jsonl`**:
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub