From utekari
Use when you are asked to explore, read, work on, update, or write/review tests for an adventure draft (might be called game, or czech "hra", "scénář", "dobrodružství"), or to check its language quality — typography, spelling, or grammar (typografie, překlepy, gramatika, korektura, proofreading)
How this skill is triggered — by the user, by Claude, or both
Slash command
/utekari:adventure-draft-authoringThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill owns **process** (how to work on a draft), **content-design
This skill owns process (how to work on a draft), content-design
guidance (what makes a good adventure), and test-design guidance (what
makes good tests). It does NOT restate technical reference — the
mcp__utekari server publishes that as MCP resources:
guides://draft/content — Markdown, Jinja2, and custom XML tag syntax for
every content block. Read before composing or editing content, hints, or
timer messages.guides://draft/expression — expression operators, functions, data types,
and the eight evaluation contexts. Read before writing or editing any
expression.guides://draft/editor — web-editor UI labels and lifecycle. Read when the
user asks "how do I do X in the editor" or "where is Y".schema://tool/<tool_name> — JSON schema for each tool's input/output.
Consult for exact field shapes and enums.Load the relevant guide into context before editing — don't rely on memory or paraphrase it in your own answers.
Honour the user's language across the whole session. If the user writes to you in Czech, respond in Czech — every status update, question, draft summary, hint discussion, content-design check-in. Don't mix English filler ("Loading draft...", "Done.", "Let me check...") into Czech replies; translate it. The same rule for any other language the user is using.
The adventure's own declared language governs content you write into the draft (quest text, hints, feedback, timer messages). That language is set per-draft and may differ from the chat language — always honour the draft's language for in-draft content, and honour the user's chat language for talking about the draft.
What stays verbatim regardless of language: tool names (mcp__utekari__…),
resource URIs (guides://draft/content), schema field names (agent_summary,
agent_instructions, chapter_id, quest_id), status enum values
(DRAFT, CHECKPOINT, PUBLISHED, PUBLISHED_TEST, UPLOADING), code
blocks, file paths, and JSON keys. These are identifiers, not prose.
mcp__utekari__list_adventure_drafts to enumerate drafts.DRAFT drafts are editable. For CHECKPOINT,
PUBLISHED, or PUBLISHED_TEST, stop and tell the user to fork an editable
copy via the editor's Save as Draft, then call the list tool again to get
the new id. UPLOADING drafts are never surfaced — you won't see them.get_adventure_draft_overview for the full adventure + chapter layout.get_adventure_draft_metadata when you only need name/description/status
(e.g. polling for changes, inspecting the changelog).get_adventure_draft_quests, get_adventure_draft_test_cases,
get_adventure_draft_test_macros for bodies — prefer one bulk call over
per-entity round-trips.get_adventure_draft_image when the puzzle is (or references) an image
and you need to see it to reason about the content.agent_instructions on the adventure and on the
quests you'll touch — those are standing direction from the user and
override this skill's defaults.validate_adventure_draft_merge / validate_adventure_draft_patch
periodically during large edits — same operations shape as the matching
update tool, dry-run only. Validation runs against the whole draft
(adventure + every quest) after the batch is applied, so one call per
batch is enough; per-quest re-validation after that is wasted work.
Validation catches schema and expression errors but not all runtime
issues — treat clean validation as necessary, not sufficient. Update tools
also accept validate: true to fold the same dry-run into the write
itself; use that when you want a single round-trip.operations
array — one call applies a batch of ops atomically (all succeed or
all roll back), and a single batch may touch the adventure and any
number of quests in one transaction. Each entity may appear at most
once across operations — to make several edits to the same entity,
either fold them into one op (merge body / patch ops list) or split
into separate calls.
update_adventure_draft_merge ops (RFC 7396 JSON Merge Patch):
upsert_adventure — partial body merged into the adventure;
requires adventure_hash.upsert_quest — partial body merged into existing quest
(quest_hash provided), or creates a new quest when quest_hash
is omitted (body must contain every required field; id is taken
from quest_id).delete_quest — removes a quest by id; requires quest_hash.
Use for: setting/clearing scalar or nested-object fields, replacing
an entity wholesale, creating or deleting quests.update_adventure_draft_patch ops (RFC 6902 JSON Patch with
add / remove / replace / move / copy / test):
patch_adventure — RFC 6902 ops applied to the adventure;
requires adventure_hash.patch_quest — RFC 6902 ops applied to one quest; requires
quest_hash.
Use for: edits inside arrays (hints, timers, solutions,
choices, set_variable_actions, chapters, prerequisites,
quest_ids_to_be_unlocked) — insert at index, append with /-,
remove or reorder one element, conditional updates with test.
Quest create/delete is not supported here; use merge.id and type are immutable. Both tools reject ops that
touch them. To change type, delete and recreate the quest.adventure_hash and quest_hash —
distinct from the metadata field adventure_definition_hash you
read them from (DraftOverviewMetadata.adventure_definition_hash →
payload adventure_hash; QuestIndexEntry.quest_hash → payload
quest_hash).conflict: refetch only the conflicting entities (the response
lists them), reapply your changes on top of the fresh bodies, use
the fresh hashes on the next attempt. The whole batch rolled back,
so unrelated ops in it must be re-sent too.schema_version differs from
what you hold): discard your in-memory payload, refetch, reapply
your intended changes on the fresh structure. Never migrate adventure
data yourself.name and description on every update call (both
write tools require them — they apply to the draft as a whole, not
to a touched entity):
name = the draft's current base name, unchanged, plus a
vMAJOR.MINOR.PATCH suffix. The base is everything before a
trailing v\d+\.\d+\.\d+ token; never rewrite, prefix, or
summarise it. If you do not already hold the current name in
context, fetch it via get_adventure_draft_metadata before the
write.
v0.0.1.0.x.y → 1.0.0 automatically — only when the user
explicitly says the draft is production-ready for the first
time.description is an additive changelog. Take the current
description verbatim and append a new line summarising what
this call changed, prefixed with the new version (e.g.
v0.1.3 — reworded hint ladder in opening quest). Never delete,
rewrite, reorder, or consolidate prior lines — every previous
entry stays exactly as it was. If the draft has no description
yet, this call's line becomes the first entry.These are discussion points with the user, not rules to apply silently.
Raise a bullet only when the current task touches it, and check agent_instructions
first — if a bullet is already covered there, treat it as settled. Once the
user answers, persist the decision into agent_instructions:
This way the same questions aren't re-asked in future sessions.
Topics to raise when relevant:
AnswerQuest: typos, accents, case, synonyms.
See guides://draft/expression for sanitize, levenshtein_distance, and
in [...].agent_summary.Expression tests. For any expression you add or edit, add a test covering
the edge cases you designed for — only when the expression is non-trivial
(branches, regex, list membership, context-specific variables, arithmetic).
Skip tests for literal constants and bare variable lookups. Run
validate_adventure_draft_merge (or _patch, matching the write you intend)
to confirm.
Be familiar with the adventure draft content and structure, and with the features being tested. Read the DSL resources from mcp__utekari before composing or editing tests — don't paraphrase from memory.
test or quest in every name) and descriptiveNavigation steps move the runner between pages, but no step type asserts the landing page on its own. Always pin location explicitly with assert_content checking content distinctive to the target chapter (e.g., its title, a unique line of intro text, or a quest prompt that only appears there). This catches silent miswiring of quest_ids_to_be_unlocked / chapter assignments — without the assertion, the next step runs against whatever page the runner happened to land on and may pass for the wrong reason.
Required pairings:
enter_chapter step must be followed by an assert_content asserting the chapter has actually been entered.continue_chapter that navigates directly into a specific chapter (single unlocked next chapter) must be followed by an assert_content asserting the new chapter. When continue_chapter lands on the chapter-list selection page (multiple chapters unlocked), this rule does not apply — the next enter_chapter carries the assertion instead.solve_text that closes its chapter (the text quest is the last quest in the chapter, so the runner auto-navigates per the DSL "Text quest at end of chapter" rule) must be followed by an assert_content asserting the landing page — either the next chapter or the chapter-list page, whichever applies. solve_answer and solve_choice never auto-navigate; a continue_chapter always follows them at chapter end, and the rule above for that step covers the assertion.tests.constantsUse tests.constants for strings the player sees in multiple places across the adventure — NPC names, recurring location labels, signature phrases, callsigns, item names. Define each repeated string once under tests.constants and reference it as {{NAME}} in test step expectations.
Why this matters:
{{NAME}} will fail at exactly the unconverted occurrences, pinpointing the drift.Naming: constants are uppercase, every other test identifier (case, macro, param, arg, …) is lowercase — the two namespaces are disjoint, so pick a name in the right register. Author the constant value in the language the player sees it in.
fail_fast parameter to stop on the first failure for faster feedback during investigation. Skip fail_fast when you want the full picture of which tests pass and fail across the selected subset.fail_fast the run may finish far sooner than the estimate — short polls catch that immediately. Start polling at a fraction of the estimate, then back off if still running.PUBLISHED_TEST draft_id directly instead of triggering from its descendant DRAFT successor. The successor is byte-identical, so reusing the published snapshot avoids spawning another snapshot per run and keeps subsequent runs grouped under one draft id.After a run finishes, query get_test_suite_run_coverage for misses and treat each one as actionable: either add a test that drives the path, or prune the dead branch in the adventure content. Don't write off a miss as "untestable" without confirming.
Self-check. If any of these are true, stop and revisit coverage before moving on.
enter_chapter not immediately followed by assert_content pinning the landing chapter.continue_chapter that navigates into a specific chapter (single unlocked next) without a following assert_content.solve_text closing a chapter without a following assert_content confirming the auto-navigation target.tests.constants and referenced as {{NAME}}.chapter_id: numeric prefix with leading zeroes + human-readable slug,
chosen so alphabetical order matches unlock order. For parallel branches,
discuss with the user whether to interleave or group per-branch.quest_id: unique across the whole draft, chosen so alphabetical order
within its chapter matches unlock order.agent_summary (adventure and quest): you MUST keep this up to date on
every object you edit, even if it was missing or wrong.
agent_instructions (adventure and quest): user-authored standing
direction. Do NOT rewrite on your own — only when the user asks, or to
record a decision they just made. Adventure-level stacks above quest-level;
quest-level can override.name / description on each update: preserve the base
name verbatim, manage a vMAJOR.MINOR.PATCH suffix per the
versioning rule, and treat description as an append-only
changelog. See Flow step 10.Player-facing prose should follow the typographic conventions of the draft's declared language and be free of spelling and grammar errors. Flag and propose only — never silently rewrite player-facing text. The agent proposes; the user disposes. Apply confirmed fixes through the normal merge/patch tools, which carries a PATCH version bump under the Flow step 10 versioning rules.
Applies to fields the MCP tool schemas flag userFacing: true (quest bodies,
hints, feedback, timer messages, choice labels, captions). Internal identifiers,
expressions, and technical metadata stay verbatim — see the Communication
language "stays verbatim" list. Honour the draft's declared language, not
the chat language. Never "fix" a deliberate in-story exception (a
foreign-language sign, a stylised misspelling).
scripts/typography.py.For published Czech content a human proofread is still warranted. Do not oversell the grammar coverage.
The conventions below are transcribed once from the authoritative pages cited
per rule. The high-confidence, mechanical subset (see the list under the tables)
is encoded in scripts/typography.py; the remaining rows — the number/unit
non-breaking space and the Czech inner single quotes — are intentionally left to
LLM judgment in the review pass rather than the script. Either way the runtime
agent relies on the script and this table and does not re-fetch the
conventions each session; the URLs remain only to resolve a genuine edge case the
table does not cover.
Czech — Internetová jazyková příručka (Ústav pro jazyk český AV ČR):
| Rule | Convention | Source |
|---|---|---|
| Quotation marks | outer „…“, inner ‚…‘ | https://prirucka.ujc.cas.cz/?id=162 |
| Ellipsis | …, never ... | https://prirucka.ujc.cas.cz/?id=166 |
| Spojovník (hyphen) | only inside compounds | https://prirucka.ujc.cas.cz/?id=164 |
| Pomlčka (en/em dash), ranges | – for ranges and parenthetical breaks | https://prirucka.ujc.cas.cz/?id=165 |
| Spacing around punctuation | no space before ,.!?, one after | https://prirucka.ujc.cas.cz/?id=160 |
Non-breaking space after k s v z o u a i | nbsp between a single-letter preposition/conjunction and the next word | https://prirucka.ujc.cas.cz/?id=880 |
| Number / unit / mark spacing | nbsp within number/unit groups | https://prirucka.ujc.cas.cz/?id=785 |
English — Butterick's Practical Typography:
| Rule | Convention | Source |
|---|---|---|
| Summary of key rules | — | https://practicaltypography.com/summary-of-key-rules.html |
| Quotes & apostrophes | curly “…”, apostrophe ’ | https://practicaltypography.com/straight-and-curly-quotes.html |
| Hyphens, en dash, em dash | — or spaced – for breaks, – for ranges | https://practicaltypography.com/hyphens-and-dashes.html |
| Ellipses | … | https://practicaltypography.com/ellipses.html |
scripts/typography.py applies the high-confidence corrections (ellipsis,
typographic quotes/apostrophe, collapsed spaces, space-before-punctuation,
Czech preposition non-breaking space, unambiguous dashes) and flags
context-sensitive hyphen↔dash choices as candidates without rewriting them. It
is a stdlib-only library:
import sys
sys.path.insert(0, "${CLAUDE_PLUGIN_ROOT}/scripts")
import typography
corrected, changes = typography.check(text, "cs") # or "en"
check returns the corrected string and a list of Change(kind, offset, original, suggestion, applied) — applied is True for the high-confidence
corrections folded into corrected, False for flagged candidates left for
you or the user to judge.
When writing or editing any userFacing field, apply the draft's-language
conventions as you write. Run non-trivial or bulk edits through
typography.py rather than eyeballing them.
Triggered when the user asks to check typography/spelling/grammar ("zkontroluj překlepy/typografii", "check grammar"), or offered before publishing.
get_* tools).typography.py for the deterministic typography
findings; apply LLM judgment for spelling and grammar.The flagged token comes from player-authored prose, so before interpolating it
into any lookup URL confirm it is a single word (Unicode letters only, no spaces
or path/query characters such as / ? # . @ ..) and percent-encode it. If it is
not a single clean word, skip the lookup rather than fetching a constructed URL —
keep it in the report as unconfirmed instead.
| Language | Lookup | How to use |
|---|---|---|
| Czech | https://prirucka.ujc.cas.cz/?slovo={slovo} | Resolves to the word entry: correct spelling, declension, and the i-y (vyjmenovaná slova) rationale. Authoritative for the model's weakest Czech cases. |
| English (US) | https://www.merriam-webster.com/dictionary/{word} | A valid entry confirms the spelling; "word not found" flags a likely misspelling. |
| English (GB) | https://dictionary.cambridge.org/dictionary/english/{word} | Same, for en-GB drafts. |
{slovo} / {word} is the validated, percent-encoded token from the step above.
A lookup that fails (network error, page unreachable) must not silently drop or auto-confirm the finding. The word stays in the report marked unconfirmed — dictionary unreachable, so the user still sees it and decides; a failed lookup never hides a possible error.
schema://tool/<tool_name> if your client
doesn't surface them directly. Check them for exact field shapes, enum
values, and hash placement rather than guessing.grep / jq
across quests. Structure:
- [draft_id]-[draft_name]
- adventure.json
- quests
- [quest_id1].json
- [quest_id2].json
...
Prefer this round-trip — dump, transform offline with grep / jq / sed
or a throwaway Python script, then reimport — when the edit is repetitive
or computed: find/replace across many quests, bulk relabeling, applying an
externally-supplied data dump, or schema-shaped transforms that are
awkward to keep straight in context. JSON keeps the hashes and field shapes
the server returned; only use YAML if the user supplied YAML.
On write-back, reuse the entity hashes captured at fetch time and choose
merge vs patch per the Flow section — nothing about tool selection changes.
On conflict, refetch only the conflicting entities, rebuild their files,
reapply, and continue; do not re-dump the whole draft. Skip the round-trip
for small targeted tweaks — the MCP round-trip is cheaper.operations payload on disk, send it to the server with the bundled
script instead of re-emitting it as a mcp__utekari__update_* tool call.
Routing a big payload back through model output is slow and risks corrupting
hashes or content; the script ships the file's bytes verbatim:
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/utekari_call.py" \
--tool update_adventure_draft_merge --args-file payload.json
arguments object — the
same object you would pass when calling mcp__utekari__<tool> directly,
matching that tool's input schema as published by the server. Don't
hardcode the shape from memory; take it from the live tool schema, since
it can drift. Build the operations per Flow steps 8–10 (right write tool,
per-entity hashes, name/description versioning). Pass - to read it from
stdin.--tool accepts the two write tools and their validate_* counterparts
(update_adventure_draft_merge, update_adventure_draft_patch,
validate_adventure_draft_merge, validate_adventure_draft_patch). Run
the matching validate_* first to dry-run the assembled payload, then the
update_* once it's clean.UTEKARI_MCP_TOKEN,
optional UTEKARI_MCP_URL). On success it prints the tool result (new
hashes, conflicts) as JSON and exits 0; a tool error, conflict, or
transport failure prints to stderr and exits non-zero. Read the printed
result and handle conflict/schema-version mismatch exactly as in Flow
step 9 — refetch, rebuild the affected files, resend. If it reports an
auth failure, run the utekari-auth skill.Surface the situation instead of guessing if:
conflict that recurs after one refetch-and-reapply;schema_version and your intended edit no longer cleanly
reapplies;agent_instructions;agent_instructions.After the user answers, persist the decision into the right agent_instructions
field before continuing.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub sedlar/escape-claude-code-plugin --plugin utekari