| name | deck-new |
| description | Use when creating anything new for deck.md — either a brand-new deck from a Google Slides template (0→1) or a new slide added to an existing deck (n→n+1, with a freshly minted 4-hex `key`). Trigger when the user provides a Google Slides URL and asks to make a deck, or says "add a slide after X", "insert a quote slide here", "new section header before Appendix". Edits to existing slides (body rewrite, layout swap, freeze/skip toggle) go through `deck-edit`. |
deck creation skill
For anything that introduces new content. Covers two cases:
- Part A — new deck from a Google Slides template (0→1).
- Part B — new slide inserted into an existing deck (n→n+1, with a freshly minted
key).
Editing existing slides (body, layout, freeze/skip) is out of scope — that goes through deck-edit.
Part A: new deck (0→1)
Take a template URL and arrive at a state where slide authoring can begin. Two hard gates in series.
- Stage 1 (scaffolding) — proceeds with just the URL and a directory name. Title may be tentative.
- Stage 2 (authoring) — requires topic, real title, and outline before any slide content is written.
"Topic TBD" does not block Stage 1; it only blocks Stage 2.
A. Principles
Do not write slides before the deck is set up
Content authoring only begins after Stage 2 is complete. Do not invert the order.
Forbidden:
- ❌ Writing body content in deck.md and then running
deck new retroactively to get an ID
- ❌ Inventing layout names without running
ls-layouts
- ❌ Deciding the directory name without user confirmation (tentative title is OK)
- ❌ Running
deck new without first confirming credentials
Excuses not accepted:
- "Layout names can be guessed from the template" → run
ls-layouts
- "Any directory name will do" → may collide with an existing project
- "The smoke-test apply is skippable" → template compatibility is unverified until executed
- "Outline can come later" → restructuring later is the root cause of
index out of range
Do not substitute investigation for user-only input
Topic, real presentation title, and directory name are information only the user holds. Do not derive them from the web, APIs, files, or IDE hints.
The tentative title (passed to deck new --title in Stage 1) may be derived plainly from the directory name or template name in the URL. The frontmatter is rewritable later.
All of the following alternatives are forbidden:
- ❌ Calling the Slides API / Drive API directly to read the template or related decks
- ❌
WebFetch against the user's personal site or social accounts to look up upcoming talks
- ❌
gh api to infer from GitHub activity
- ❌ Inferring the topic from the contents of an existing
decks/<name>/ or files open in the IDE
- ❌ Generating the topic from date, season, or naming conventions
Gate granularity
The two gates are independent. Each unmet item only blocks the actions tied to its own gate.
Stage 1 gate (required inputs):
- Template URL (→ presentation ID extracted)
- Directory name
Once these are in place, the following may run:
deck doctor
mkdir -p decks/<name>/images
deck new decks/<name>/deck.md --base <TEMPLATE_ID> --title "<tentative title>"
deck ls-layouts decks/<name>/deck.md
Stage 2 gate (required inputs):
- Topic (event name, theme)
- Real presentation title (replaces the tentative one)
- Agreed outline (chapter structure, layout assignment, approximate slide count)
Until these are in place, the following are forbidden:
- Writing slide body content (anything below H1) in deck.md
- The minimal-slide
deck apply smoke test that closes Stage 2
User directives vs. gate requirements
Instructions like "don't ask", "don't wait", "go autonomous" do not authorize filling unmet gate inputs on your own. If the URL is missing in Stage 1 or the topic is missing in Stage 2, wait silently for that specific item — other actions in the same stage are unaffected.
If the user explicitly says "just scaffold" / "just create the files", and the Stage 1 gate is satisfied, proceed — this is within the Stage 1 scope, not a skill violation.
A. Procedure
1. Extract presentation ID from the template URL
https://docs.google.com/presentation/d/<ID>/edit?slide=...
^^^^
The ID is what sits between /d/ and the next /. Ignore query and fragment.
If extraction fails, do not guess — ask the user to resend a correct URL.
2. Environment check
deck doctor
If it reports credentials.json NOT FOUND, stop and report. Do not attempt deck new regardless.
3. Receive Stage 1 inputs
Needed:
- Directory name: the
<name> in decks/<name>/. Kebab-case recommended (e.g. gdg-tokyo-202507). If it collides with an existing entry, stop or propose an alternative.
- Tentative title: if the user supplies one, use it; otherwise derive plainly from the directory name or URL template name (e.g. directory
tskaigi-2026 → tentative title TSKaigi 2026).
If the directory name is missing, ask once in a single short line, then wait silently. Do not ask about topic or real title in Stage 1 — that belongs to Stage 2.
4. Prepare the directory
mkdir -p decks/<name>/images
Create images/ up front; creating it later leads to incorrect image paths.
5. Create the presentation from the template
deck new decks/<name>/deck.md --base <TEMPLATE_ID> --title "<tentative title>"
--base specifies the template (the older --from is DEPRECATED).
- Behavior of
deck new --base: Drive Files.Copy duplicates the template, all slides are deleted from the copy, and a single empty slide is created. The original template is untouched.
- On success, the new presentation ID is printed to stdout and the frontmatter is written into
deck.md.
- If an authorization error appears, ask the user to verify access to the template.
6. Layout discovery (required)
deck ls-layouts decks/<name>/deck.md
Share the full list of layout names with the user. It becomes the input to outline design.
If ls-layouts fails, stop and investigate. "Write generic layout names because this is a known template" is not allowed.
This completes Stage 1. If the topic has not been received yet, halt here and wait.
7. Receive Stage 2 inputs (topic, real title)
From the user:
- Topic: event name / theme (the precondition for chapter structure)
- Real presentation title: replaces the tentative
title: in the frontmatter. Edit deck.md directly; the Slides-side title is synced on the next deck apply.
Ask once in a single short line, then wait silently. Substitute investigation is forbidden.
8. Agree on the outline
Before writing any body content, agree with the user on:
- Chapter structure (what is established at H1, in what order)
- Layout per chapter (chosen from the
ls-layouts result)
- Approximate slide count / talk duration
- Use of code blocks / images / figures (add
codeBlockToImageCommand to frontmatter if needed)
Do not enter authoring without this agreement.
9. Smoke-test apply with a minimal slide
A frontmatter plus one test slide, then deck apply:
---
presentationID: <ID>
title: '<real title>'
breaks: true
---
<!-- { "key": "<4-hex>", "layout": "<chosen layout>" } -->
# Test slide
The key value is minted per Part B Principle 1 below.
deck apply decks/<name>/deck.md
If this fails, fix it before going further. Adding to something that does not work at minimal scale does not make it work.
A. Completion criteria
Stage 1 done (scaffolding done)
It is acceptable to stop here. "Just create the files" ends at this point.
Stage 2 done (ready to author)
Everything in Stage 1, plus:
Subsequent authoring follows Part B (new slide insertion). Reach for deck-edit once the work shifts to touching slides that already exist.
Part B: new slide (n→n+1)
Adding a single new slide to an existing deck. The Part A Stage 2 authoring loop also uses this workflow.
B. Principles
1. Mint the key before writing
- Generate a fresh 4-hex with
python3 -c "import secrets; print(secrets.token_hex(2))"
- Check for collision with
grep '"key":' deck.md; regenerate on collision
- A collision triggers
duplicate page key %q at pages %d and %d at parse time in deck v1.24+ and makes apply fail — a production failure
- Never reuse an existing slide's key, never guess at one, never change one (the full convention is in the Slide key convention section below)
2. No guessing on layout
- Run
deck ls-layouts deck.md (or read .deck-state.json) to confirm what is available
- Do not copy a layout name from an existing slide as a "known good" — the template may have been swapped underneath you
- "Use an existing layout because
ls-layouts is failing" is also forbidden — fix the error first
3. One slide at a time
- Do not bulk-add
- One slide →
deck apply → visual confirmation → next slide
object not found / index out of range from bulk apply often self-heals on a re-apply, but that is a recovery path, not the primary mode
4. Visually verify the result via PDF render
A valid layout name in the config object does not guarantee the slide looks right. Before reporting completion:
deck apply deck.md
deck export deck.md -o /tmp/verify.pdf
pdftoppm -png -r 100 -f <N> -l <N> /tmp/verify.pdf /tmp/check
If deck apply --watch deck.md is running in the background, apply is automatic; otherwise run deck apply explicitly before deck export (the export reflects the Slides-side state, not the local md).
B. Slide structure
<!-- { "key": "a7b5", "layout": "Layout Name" } -->
# Title
---
- Start with a one-line JSON page-configuration object comment (
<!-- { ... } -->)
- Use
--- as the separator
- Valid fields inside the config object:
key / layout / freeze / skip / ignore. The key value is a 4-hex string (see Slide key convention below).
B. Workflow
- Pin down the insertion point — which existing key goes before / after, which section it belongs to. Resolve any ambiguous reference back to a key (titles, line numbers, "this page" are not referents).
- Layout discovery —
deck ls-layouts deck.md
- Layout selection — pick the layout that fits the intent
- Mint the key — generate 4-hex →
grep '"key":' deck.md → regenerate on collision
- Insert —
Edit in the config-object comment and the body. Place the --- separators before and after correctly.
- Apply —
deck apply deck.md (no user approval needed; see deck apply policy below)
- PDF verification — use Principle 4 to inspect the rendered page image
- Report by key — tell the user the slide is added using its key (e.g. "
a7b5 inserted after c9d2")
Reference: deck CLI
Page configuration schema
Fields the CLI interprets inside the config-object comment (deck v1.24+, md/md.go):
| field | type | meaning |
|---|
key | string | Stable slide identifier (v1.24.0+). Opaque to the CLI. Empty string is treated as unset. Duplicates fail parsing (duplicate page key %q at pages %d and %d). |
layout | string | Slide layout name. |
freeze | bool | true protects the whole slide from changes. |
ignore | bool | true excludes the slide from generation entirely (drafts etc.). |
skip | bool | true generates the slide but skips it during playback. |
Unknown fields are silently dropped by json.Unmarshal. The JSON itself must remain syntactically valid (commas, quotes); an invalid object is not parsed as config and falls back to being treated as a speaker note. Changing key alone does not trigger a slide update because content equality ignores it.
Frontmatter
---
presentationID: <Google Slides ID>
title: '<title>'
breaks: true
---
breaks: true makes Markdown line breaks render as <br>. This skill set assumes breaks is on everywhere.
presentationID is required; commands that take a DECK_FILE positional argument resolve it from the frontmatter.
CLI commands (v1.24 series)
The positional argument is DECK_FILE; presentationID comes from the frontmatter by default. Override with -i <id>.
deck doctor — environment diagnostic (checks credentials.json location, etc.)
deck new deck.md --title "<title>" — create a new presentation, write frontmatter into the file
deck new deck.md --base <id> --title "<title>" — clone an existing presentation as the template (the older --from flag is DEPRECATED)
deck ls — list presentations
deck ls-layouts deck.md — list available layouts
deck apply deck.md — apply the local md to Slides (-vv for verbose; useful for full resync when order has drifted)
deck apply --watch deck.md — watch mode (at most one process at a time — a second concurrent watcher is the typical cause of broken slide order)
deck apply -p <page> deck.md — apply only the specified page
deck export deck.md -o out.pdf — render the current Slides state to PDF (does not read the local md; run after apply)
deck open deck.md — open the presentation in a browser
There is no --force / --reset flag. deck apply -vv is the de facto full resync.
Error handling
duplicate page key %q at pages %d and %d (v1.24+): two slides share the same key. The usual cause is a key-minting miss during a fresh insertion. Re-mint the newer slide; do not change an existing slide's key.
index out of range: slide count mismatch. Reduce, or add one at a time.
object not found: a referenced object does not exist.
- Try a re-apply first — often transient.
- If it persists, rebuild from a minimal working state.
- State feels wrong: run
deck apply -vv deck.md for a full sync. There is no .deck-state.json in normal operation — the truth lives on the Slides side.
- Runaway watcher:
pgrep -fl "deck apply --watch" → pkill -f "deck apply --watch" to stop all → restart at most one.
credentials.json NOT FOUND (from deck doctor): stop and ask the user. Do not attempt deck new until credentials are confirmed.
ls-layouts failure: stop and investigate. Writing layout names without the canonical list from ls-layouts is forbidden.
Slide key convention
Every slide carries a key field in its page-configuration object.
- Format: 4-hex string (e.g.
a7b5, c9d2). The CLI treats the value as opaque (any string works); this skill set standardizes on 4-hex.
- Mint procedure: generate with
python3 -c "import secrets; print(secrets.token_hex(2))", then grep '"key":' deck.md to check for collision; regenerate on collision.
- Duplicates fail at parse time in deck v1.24+ (
duplicate page key %q at pages %d and %d). A collision discovered at apply time is a production failure, not a soft warning.
- Immutable. Once written, a key is never rewritten — moving, deleting, relayouting, or otherwise editing a slide preserves its key. Keys are the stable identifiers used in commit messages and conversation history; renaming one breaks the trail.
- Conversation protocol: refer to slides by key, not by title fragment, body fragment, or page number. User-side: "move
a7b5 after c9d2", "delete 7f31", "freeze through 5a90". Agent-side confirmations use keys too: "moved a7b5 after c9d2". When the user names a slide ambiguously, resolve to a key via grep and confirm before acting.
deck apply policy
deck apply deck.md runs automatically — no user approval needed. The Slides-side state change is reversible by deck.md revert + apply.
- Keep a single
deck apply --watch deck.md running in the background per editing session. The watcher auto-applies on every file change, so explicit apply becomes unnecessary in steady state. Start it at session open, restart if it dies.
- Only one watcher at a time. A second concurrent watcher is the typical cause of broken slide order. Detect and stop runaways with
pgrep -fl "deck apply --watch" → pkill -f "deck apply --watch", then restart at most one.
git push is a separate rule — apply does not authorize push.
See also
deck-edit — editing existing slides (key preserved)