Seven-skill presentation system: ingest talks into a rhetoric vault, run interactive clarification, generate a speaker profile, create presentations that match your documented patterns, produce the deck illustrations + thumbnail visual layer, publish talk pages to a Jekyll shownotes site, and verify a recorded screencast against its storyboard. Includes a 113-entry Presentation Patterns taxonomy (83 observable: 64 patterns + 19 antipatterns; 30 unobservable: 21 patterns + 9 antipatterns) for scoring, brainstorming, and go-live preparation.
74
93%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
First-time setup for the PowerPoint-native deck-editing tooling
(RunDeckOps.bas + run-deck-ops.sh; see rules/deck-editing-rules.md).
macOS + Microsoft PowerPoint only.
Steps 1–4 are manual GUI actions only the user can perform; the agent presents each and the user acts, then the agent proceeds. The Step 5 smoke test verifies the whole setup end-to-end before any real edit — so the agent does not pause to confirm each manual step, it confirms once via the smoke test. Steps 1–4 run once per machine. Step 6 is the recurring per-build flow — read it before every real deck build.
Every path below is absolute. {speaker_toolkit_root} is resolved in
SKILL.md; {vault_root} is config.vault_root from the tracking database.
Expand both before showing a command or a path to the user — they cannot resolve
a placeholder, and Step 3 in particular hands them a path to type.
Never assume this is a first run, and never assume it isn't:
"{python_path}" "{speaker_toolkit_root}/skills/presentation-creator/scripts/deckops-doctor.py" \
--vault-root "{vault_root}"It touches no deck, no template, no macro container, and never launches
PowerPoint. It does restore missing drivers into the plugin's own scripts
directory — the same install-restore the .sh wrappers do, and what keeps a
fresh install from reading as driver drift. That restore runs under --offline
too, and never overwrites an existing driver.
It prints a JSON report whose status says what to do — next_step carries the
same thing as one sentence:
status | What it means | Go to |
|---|---|---|
ok | Set up and current | Step 6 |
setup_required | No macro container on this machine | Step 1 |
macro_unreachable | Container not open, macros off, or the module was NEVER imported | Steps 1–3 |
powerpoint_not_running | Set up; PowerPoint just isn't open | Step 6 |
macro_stale | The macro answered with an old stamp — setup is done, it needs a re-import | Step 3 (Updating) |
macro_stale_inferred | The OPEN container holds a module with no version macro. Inferred, not observed: disabled macros look identical | Step 3 (Updating), then Step 1 if it persists |
driver_drift | Shipped drivers don't match their mirrors | Read drivers.problems; it names the fix |
probe_failed | The probe could not run, so the state is UNKNOWN | Read live.detail — usually denied Automation consent (Step 4) |
probe_missing | The probe driver or osascript is absent | Reinstall the plugin |
unsupported_platform | Not macOS | No deck build is possible here |
Exit code separates a diagnosis from a non-diagnosis: 0 when a verdict was
reached (setup_required included — that is a finding, not a script failure),
1 when the probe could not run and the state is unknown.
Pass --offline to skip the live PowerPoint probe; you then get setup_required
versus everything-else, and no reading on whether the macro is current. A
container open from a path other than the canonical one still counts as set up —
container.canonical_mismatch reports the difference, and next_step names the
file the user actually has open rather than sending them to create a second one.
Heads-up for installed plugins: tessl install ships only .md/.py/.json/.sh/.txt
and STRIPS .bas/.applescript, so RunDeckOps.bas and the .applescript
drivers aren't on disk after install. The doctor and the .sh wrappers restore
them automatically from their committed .txt mirrors; a dev checkout already
has them. Nothing here requires a manual restore.
Ask the user to open PowerPoint → Settings → Security & Privacy and enable macros ("Enable all macros", or enable + trust). Proceed to Step 2.
DeckOps.pptmThe macro must live in a macro-enabled file, never in a real deck. The container goes at ONE canonical path, reused across every talk — the doctor and every error message look for it there:
{vault_root}/.deckops/DeckOps.pptmCreate the directory yourself first, so the Save dialog has somewhere to land:
mkdir -p "{vault_root}/.deckops"Then ask the user to:
DeckOps.pptm and save it to the path above.
In the Save dialog, ⇧⌘G opens "Go to Folder" — paste the expanded directory
there. A dot-directory does not appear in the file list; ⇧⌘. toggles hidden
files if they need to see it.Gotcha to warn about up front: if PowerPoint ever shows "Visual Basic macros
will be removed if you save the file in this format", the user is saving a
.pptx — tell them to click Cancel and save as .pptm instead. A real
deck must never carry the macro.
First, put the .bas somewhere the user can actually reach it. On an
installed plugin the module lives under a hidden .tessl/ directory, which
PowerPoint's Import panel does not show — so export a copy next to the container:
"{python_path}" "{speaker_toolkit_root}/skills/presentation-creator/scripts/sync-deck-drivers.py" \
export --to "{vault_root}/.deckops"It prints the exported path as JSON ({"exported": "..."}). Materializes the
.bas from its mirror first if the install stripped it, and refreshes a stale
earlier export. Give the user that exact absolute path.
Then ask the user to open the VBA editor (Tools → Macro → Visual Basic Editor,
or ⌥F11), select DeckOps.pptm's project in the left pane, then
File → Import File… and choose the exported RunDeckOps.bas. In that panel
⇧⌘G opens "Go to Folder" and ⇧⌘. toggles hidden files — the .deckops directory
starts with a dot, so one of the two is needed. Save DeckOps.pptm (⌘S).
Updating the macro later. A plugin update ships a new macro; the copy already
imported into DeckOps.pptm keeps running the OLD code, silently, because nothing
can read VBA source back out of a saved .pptm. Two statuses cover this, and they
are not interchangeable:
macro_stale — the macro ANSWERED with an old stamp. Macros are provably on,
so the re-import below is the whole fix.macro_stale_inferred — the open container READS as holding a module with no
version macro, which is what a container imported before the stamp existed looks
like. Disabled macros silence the probe identically, so this one is an inference:
do the re-import, and if it does not clear, confirm macros are enabled (Step 1).How the two are classified is
skills/presentation-creator/scripts/deckops-doctor.py's inspect_container /
verdict.
The re-import, for either: re-run the export command above, then in the VBA
editor right-click the DeckOps module → Remove (No to export) →
Import File… the refreshed .bas → save. Re-run Step 0 to confirm ok.
The first time a script drives PowerPoint, macOS shows an Automation consent
prompt. Tell the user it is GUI-only — it cannot be approved headless — so they
should run the first invocation themselves (or be at the machine to click
OK): System Settings → Privacy & Security → Automation → allow the terminal
to control PowerPoint. After consent is granted once, the agent may run
run-deck-ops.sh itself.
Given the history of lost work with other tools, always test before touching a real deck. One script does the whole thing — it copies the template to a uniquely-named base, builds the shipped 3-slide op sequence (layout 0 and a free text box only, so it runs against any template), and prints where the output landed:
bash "{speaker_toolkit_root}/skills/presentation-creator/scripts/deckops-smoke-test.sh" \
"{template_pptx_path}"{template_pptx_path} is infrastructure.template_pptx_path from the speaker
profile; the template is read-only. An optional second argument sets the output
directory. It prints {"ok":true,"output":"<path>","slides":3,"base":"<path>"}
and exits non-zero when no deck was produced. DeckOps.pptm must be open
(Step 6).
It passes when all four hold:
{"ok":true,...}.Anything else: re-run Step 0 and follow next_step. Only after a clean pass run
the real edit. Delete the throwaway files.
Steps 1–4 are one-time per machine; this is what happens on EVERY real deck build:
DeckOps.pptm first and keep it open for the entire build. Every pass
(BuildDeck, ExpandBuilds, speaker notes, backgrounds, QR) drives a macro
that lives in this file; the scripts call it in the running PowerPoint instance.
If DeckOps.pptm is closed (or PowerPoint quits) mid-build, the next pass fails
with a macro-not-found error — reopen it and re-run that pass.ok. It is the cheap way to
catch a closed container or a stale import before a 40-slide build half-finishes.ExpandBuilds → speaker notes → backgrounds → QR.
ExpandBuilds renumbers later slides, so it runs before the by-index passes
(see rules/deck-editing-rules.md).If decks live in a Google Drive "My Drive" folder (the macOS Google Drive
File-Provider mount), sandboxed PowerPoint can OPEN/read them but cannot
create a new file there via VBA — SaveCopyAs fails with E_FAIL
(-2147467259). run-deck-ops.sh works around this by saving to a local
staging folder (~/.deckops-staging/) and then moving the result into the Drive
destination with the shell (which writes to Drive normally). Keep using
uniquely-named copies for the base/import/output so PowerPoint's
filename-keyed open-deck cache never hands back the wrong deck.
This applies to DeckOps.pptm itself only for creation: saving it into a Drive
vault in Step 2 is a normal GUI save, which Drive accepts. The macro never writes
to its own container.
Sandboxed PowerPoint shows a Powerbox "grant access" / "select file" prompt
whenever a VBA macro touches a file OUTSIDE its container — opening a Google-Drive
base deck or template (Presentations.Open), reading an illustration
(UserPicture), or writing the output (SaveCopyAs to a Drive folder E_FAILs, and
to a local ~/.deckops-staging subdir prompts every run). On a 40-slide deck that
is dozens of clicks.
Every deck-ops wrapper avoids this by routing ALL macro file I/O through
PowerPoint's own sandbox container (~/Library/Containers/com.microsoft.Powerpoint/Data/.deckops-stage/<pid>/),
which a sandboxed app reads and writes with no prompt:
container-stage.sh (sourced by every wrapper) provides stage_base — it copies
the base deck / template / QR image into the container and opens them from there.stage-images-into-container.py stages illustration backgrounds the same way.OUT_STAGE_DIR), then the shell (not
sandboxed) moves it to the Drive destination.container-stage.sh removes the per-run staging dir;
wrappers must NOT set their own EXIT trap (it would override that one and leak
copies).So a full build runs with ZERO prompts and no Full Disk Access grant. If the container is absent (PowerPoint never launched), the wrappers fall back to the original paths and a local output dir — prompts return, but nothing breaks.
Mac PowerPoint VBA has no Application.FileDialog, so a "grant one folder" macro is
impossible; container-staging is the supported no-prompt path.
.tessl-plugin
rules
skills
illustrations
presentation-creator
references
patterns
build
deliver
prepare
scripts
screencast-recorder
shownotes-publisher
vault-clarification
vault-ingress
references
scripts
vault-profile