Working with AI Agents
Agents use the same CLI you do. There is no MCP server and no tool schema to install. A coding agent already has a shell and a file editor; a slide is a forty-line HTML file, so the agent's own editor is the most precise and cheapest way to change it. dek adds only what an agent cannot do well on its own: seeing how the slide renders, hearing how it reads, and the fragile operations of renaming and reordering.
What the agent reads
dekc init writes a short AGENTS.md at the project root, and dekc new, dekc ref, and dekc sync keep it current: the principles, the conventions, what to check before reporting a deck as done, and a pointer to the CLI's own reference. It names no class, token, or layout a theme decides: each deck owns its theme, so the agent reads the deck's own with dekc theme. The only classes it names are the player's state classes, is-current and is-shown, which no theme defines and markup never writes. It stays under a hundred lines. Anything more detailed is pulled from the CLI when needed.
dek owns only the block between <!-- dek:begin … --> and <!-- dek:end -->, and rewrites it in place. Write your team's own notes for agents above or below it; they stay. An AGENTS.md you already had keeps its text and gets dek's block appended.
dekc help --agentThat is enough for Claude Code, Codex, OpenCode, or any other agent to work in a dek project with no extra configuration.
Before the project: llms.txt and the skill
An agent that does not know dek yet, or works outside any project, has two ways in.
llms.txtsays how to install and run dek, what every page of these docs covers, how many lint rules there are, and every command asdekc help --agentprints it. It is built from the docs, the rule table, and the commands withbun run llms, and a test fails while the published copy is out of date.- The official skill teaches the loop: script first, then one slide at a time with
dekc show, an edit,dekc check --shot, anddekc lint --visual, and when to stop. It also covers the traps: the command isdekc, notdek, and dek runs on Bun, withbunx, notnpx. It names only commands every dek since 0.2 has, and sends the agent todekc help --agentandAGENTS.mdfor the rest, so an older dek in a project is never told to run what it lacks.
npx skills add hajimism/dekThe skills CLI installs it for the agents it supports. In Claude Code, /plugin marketplace add hajimism/dek and then /plugin install dek@dek install it as a plugin. The npm package ships the same file, so a project always has the copy for its own dek at node_modules/@hajimism/dek/skills/dek/SKILL.md. There is no MCP server; the skill teaches the CLI. Both are in English, like AGENTS.md; an agent answers in whatever language you write to it.
What the CLI promises
- Every result command accepts
--json, and every result has one shape. An agent branches onok, readserrorwhen it isfalse, and works throughdiagnostics.dekcanddekc rehearsestay running and are the only exceptions: given--json, they refuse at once rather than start a server a JSON reader would wait on forever. Nothing forces an agent to parse prose; the contract is below. - Every error names the next command. "
slides/intro.htmlis missing; rundekc sync." The hint is something you can run as-is, from wherever you ran the command: a command that takes a deck names it when you are not inside that deck, asdekc theme demoat the project root. Diagnostics carry a hint too when the fix is known, such asuse hook, turn, or 1-2for adata-stepthat names no beat. dekc help --agentis a few hundred tokens. The CLI documents itself.AGENTS.mdstays short.
The JSON contract
Success is { "ok": true, ... } with exit code 0. Failure is { "ok": false, "error": { ... }, ... } with exit code 1, whether the command could not run or lint and check found an error:
{ "ok": false, "error": { "message": "deck \"nope\" not found", "path": "decks/nope", "hint": "run `dekc ls`" } }{
"ok": false,
"error": { "message": "lint found 1 error", "hint": "fix each error in diagnostics, then run `dekc lint` again" },
"diagnostics": [
{
"id": "DEK011",
"severity": "error",
"message": "slide contains an onclick attribute",
"path": "slides/intro.html",
"line": 6,
"column": 11,
"slug": "intro",
"hint": "remove it; a slide takes no input, and motion goes in slides/intro.ts as a draw(t) function",
"data": { "kind": "attribute", "name": "onclick", "value": "go()" }
}
]
}erroralways has amessage, ahintwhenever dek knows the next step, andpathandlinewhen the failure has a place.- Commands that report diagnostics (
lint,check,build,ls,cues) always carry adiagnosticsarray, pass or fail. Each diagnostic has itsseverity, apath,line, andcolumnwhen it has a place, ahintwhen the fix is known, and the values its message names asdata. Only errors fail; warnings alone are"ok": true. - A check that did not run is an entry in
skipped, never an empty pass:{ "check": "rumdl", "reason": "rumdl is not installed", "hint": "bun add -d rumdl; …" }. The field is absent when everything ran.dekc lintwithout--visual, anddekc build, always listvisual: they did not measure overflow or contrast, and the hint is the command that does. - A command that writes files for every deck in scope returns a list, one deck or many:
buildandpdfreturnouts. dekc lint --format sarifreturns the same diagnostics as SARIF 2.1.0.- Every command's shape is published as a JSON Schema at
cli.schema.json: one entry under$defsper command, plusfailurefor a line that could not run. Every object is closed, so a field it does not name is a change to the contract. The schema, the CLI's own types, and what dek prints in its tests are checked against one definition, so they cannot drift apart.
While dek is 0.x, the --json shape and the rule ids may still change between releases. A rule id is never reused: a retired id stays retired.
One round trip
dekc check architecture --shot
dekc check architecture --voicecheck lints one slide, including the rendering rules when Playwright is available, writes a screenshot, and returns the diagnostics and the image path. The path contains a hash of the rendered content, so an agent that opens the returned path never sees a stale image. --voice returns the kana reading and duration for each sentence. Write the HTML, run check, look at the picture, read the pronunciation, fix. Write, see, hear, fix: one loop, closed.
dekc shot --sheet
dekc shot architecture --motionTwo views no single shot gives. --sheet puts the whole deck on one image, for the balance across slides. --motion spreads a slide's beats across time, each held at a few moments through everything it moves, so an entrance that hides text or a line that never draws shows up in stills. Both images are sized to be read whole without being scaled down, and both are cached by what they show.
Without Playwright, check still lints and lists visual in skipped with the reason and a hint that installs it, so an agent never mistakes a skipped measurement for a pass. --voice on a deck without voice/ does the same: voice is skipped with the reason and how to set it up, and the rest of the check still runs.
Anything geometry can decide is a lint rule. Overflow and contrast have definite answers once a browser measures them, and a measured verdict is more reliable than showing an agent a screenshot and asking whether the text fits.
Pointing goes both ways
dekc goto architecture
dekc currentAfter editing a slide, the agent can jump the human's browser to it. When the human says "make the figure on this slide smaller", the agent runs dekc current, learns the slug, and fixes it without asking which slide.
A slug names the slide but not the shape, such as one arrow among eight on a roadmap. For that, the human points at it: a on the dev server, a click on the shape, and a note (how). Then they can say "deal with my notes", and the agent reads them itself:
dekc annotations --jsonEach note comes with its number, as the page's markers show it, the human's text, each element's path, line, and column in the file as it is now, its name and its text was and is now, its box, and the shot that shows the slide at the beat the human saw. Fix the slide, then list them again: status turns edited once the slide's own HTML, CSS, or script changed since the note (changed names which), and gone once none of its elements is left. edited means the slide changed, not that the note is dealt with; check the shot against the note. Leave dekc annotations clear to the human, who wrote the notes and judges the result.
An agent with no access to the project, such as one in a chat on another machine, gets the same notes from Copy. What the human pastes names each element by the file and line it is written at:
## Notes on decks/plan
Boxes and points are in the slide's own pixels, from its top left.
### Slide 4 of 12, roadmap ("Next quarter's roadmap"), at beat `milestones`
1. div.arrow.arrow-up at decks/plan/slides/roadmap.html:58:7 (box 1188,286 24×290)
> Make the arrowhead larger
2. 2 elements:
- div.chevron "UI design" at decks/plan/slides/roadmap.html:30:7 (box 150,380 220×24)
- div.chevron "Design review" at decks/plan/slides/roadmap.html:31:7 (box 150,410 220×24)
> Line up their right edges
To see it: `dekc shot plan roadmap --step milestones`Every value is read from the source or measured on the page. The name is the tag and its classes as the file writes them, without any a script adds as the deck plays. The box is in the slide's own pixels, and the last line shows the slide at the beat the human saw. A place on the slide rather than an element is the slide itself at a point. An element an edit took where it could not be followed says so, and keeps the line it had.
After a rehearsal
dekc marks
dekc marks clearRehearse aloud on the dev server and press m on each beat whose words trip you up. dekc marks then hands the agent what it needs to rewrite them: the heading's line in script.md, was (what the beat said when you marked it), and text (what it says now). A mark follows its beat by id, or by title, so beats added before it do not move it, and status turns edited once the words change, or gone if the beat itself went away. Say the new words aloud before you run dekc marks clear: only that tells whether they work.
Using a slide as a model
dekc show why-dek timingshow returns everything one slide is made of in one call, each part labeled with its file: the section's script, the slide's HTML, CSS, and TS, the rules of theme.css the slide actually uses with only the tokens and keyframes they reach, and the assets it references. An agent learns how the slide is built without reading the whole theme, then writes its own slide in its own deck's theme. Lint catches any class or token it carried over by mistake.
The model can be someone else's deck. dekc ref owner/repo/deck pins it, and ls, show, theme, and shot then take owner/repo/deck as the deck. AGENTS.md lists the pinned refs, so the agent knows which decks it may learn from. A ref is read-only; the agent copies what it needs into its own deck.
Prompts that work
Shrink the right column of architecture.html, then run
dekc check architecture --shot and confirm nothing overflows.On the slide from dekc current, reveal the elements beat by beat with
data-step, following the script's ### headings.I added ## recap {#recap} to script.md. Do not run dekc sync;
the dev server will generate the skeleton. Fill it in.Carry the figure from problem into architecture with data-morph.
Run dekc shot problem --to architecture --at 0.5 and check the
interpolated position looks right.Make the number on files count up as the slide enters. Put it in
slides/files.ts, drawing from t alone, then run dekc check files --shot.Use hajimism/dek/why-dek as a model. Find the slide closest to what
the budget section needs with dekc ls, read it with dekc show, and write
budget in this deck's theme.Make a slide like timing in why-dek for this deck's budget section.
Read it with dekc show why-dek timing, write it with this deck's
theme, then run dekc check budget --shot.To hand the whole documentation site to an agent, give it /llms.txt.