Shortcuts
Shortcuts are named references that give stable, short identifiers
to URIs. They follow CommonMark’s reference link pattern with an
@ prefix.
Two forms
Section titled “Two forms”Inline form (recommended)
Section titled “Inline form (recommended)”Defines a shortcut in a single line:
[@id Label](url)@id— the shortcut identifierLabel— human-readable display texturl— the topic URI
This is syntactic sugar. String decomposes it into a reference link and a reference definition internally.
Reference form
Section titled “Reference form”Separates the display from the definition, following CommonMark’s reference link syntax:
[Label][@id]
[@id]: urlThe reference definition ([@id]: url) MAY be placed anywhere in
the document — typically grouped at the bottom.
Both forms are equivalent
Section titled “Both forms are equivalent”# Inline form[@docs Documentation](https://docs.example.com)
# Reference form — same result[Documentation][@docs]
[@docs]: https://docs.example.comThe inline form is recommended for convenience. The reference form is useful when the same shortcut is referenced multiple times or when URLs are long and would clutter the content.
AI view
Section titled “AI view”Regardless of which form the author uses, String always presents shortcuts to the AI as reference links:
Author writes (inline):
Visit the [@docs Documentation](https://docs.example.com) for details.Author writes (reference):
Visit the [Documentation][@docs] for details.
[@docs]: https://docs.example.comAI sees (both cases):
Visit the [Documentation][@docs] for details.The URL never enters the AI’s context. The AI navigates with
/open @docs. String resolves the actual URI.
In a standard CommonMark viewer, the inline form renders as a normal link. The reference form renders as a standard reference link (CommonMark-compatible).
ID rules
Section titled “ID rules”Shortcut IDs MUST follow these rules:
| Rule | Valid | Invalid |
|---|---|---|
| Lowercase letters | @docs | @Docs |
| Numbers allowed | @section2 | — |
| Hyphens allowed | @getting-started | — |
| Underscores allowed | @api_ref | — |
| Must start with a letter | @api | @2api |
MUST NOT start with link- | @links | @link-1 |
Pattern: @[a-z][a-z0-9_-]*
The link- prefix is reserved for auto-generated shortcuts.
Named shortcuts MUST NOT use IDs starting with link-.
Where shortcuts can appear
Section titled “Where shortcuts can appear”Inline in documents
Section titled “Inline in documents”Visit the [@docs Documentation](https://docs.example.com) formore details, or see the [@changelog Changelog](./changelog.md).In nav menu files
Section titled “In nav menu files”Menu files consist of shortcut lines:
[@home Home](../index.md)[@api API Reference](../api/index.md)As reference definitions
Section titled “As reference definitions”Grouped at the bottom of a document or section:
[@docs]: https://docs.example.com[@api]: https://api.example.com/v2[@pricing]: ./pricing.mdNamespacing
Section titled “Namespacing”Shortcuts are namespaced by their source.
Page namespace
Section titled “Page namespace”Shortcuts defined in the document (inline or reference) belong to
the page namespace. The prefix is implicit:
@docs = @page.docsBoth forms are valid. The short form @docs is preferred.
Menu namespace
Section titled “Menu namespace”Shortcuts from nav menus are prefixed with the menu name:
Menu file (nav/main.md, menu name main):
[@home Home](../index.md)[@api API Reference](../api/index.md)AI sees:
[Home][@main.home][API Reference][@main.api]| Source | Namespace | AI sees | Full form |
|---|---|---|---|
Document [@docs ...] | page | [@docs] | [@page.docs] |
Menu main [@home ...] | main | [@main.home] | [@main.home] |
Menu api [@auth ...] | api | [@api.auth] | [@api.auth] |
Auto-generated shortcuts
Section titled “Auto-generated shortcuts”Regular Markdown links without explicit shortcut IDs are automatically assigned identifiers by the runtime.
Which links are converted
Section titled “Which links are converted”Only links with long or external topics are converted:
| Link topic | Converted? | Example |
|---|---|---|
https:// or http:// URL | Yes | [Docs](https://docs.example.com) |
| Relative path > 40 chars | Yes | [Guide](./very/deep/nested/path/to/doc.md) |
| Short relative path | No | [Home](./index.md) |
Short relative paths are already readable — no shortcut needed.
Author-defined shortcuts ([@id Label](url)) are never converted;
they keep their explicit ID.
Slug assignment
Section titled “Slug assignment”The runtime attempts to derive a readable slug from the link label. A slug is used when the label meets all of these conditions:
- 20 characters or fewer
- Contains only letters, numbers, spaces, or hyphens
- Not a duplicate of an already-assigned slug in the same document
The slug is produced by: lowercase → remove non-word characters (except spaces and hyphens) → trim → replace spaces with hyphens.
GitHub → githubMDN Web Docs → mdn-web-docsExample API → example-apiDuplicate slugs
Section titled “Duplicate slugs”When the same slug appears more than once, a numeric suffix is appended starting from 2:
@docs ← first occurrence@docs-2 ← second@docs-3 ← thirdThe suffix is per-slug — duplicates of @docs and duplicates of
@github maintain independent counters.
Non-sluggable fallback (@link-N)
Section titled “Non-sluggable fallback (@link-N)”When the label does not qualify for a slug (too long, special
characters, or empty after sanitizing), the runtime assigns
@link-N where N increments from 1 per document:
| Condition | Label | Result |
|---|---|---|
| Exceeds 20 chars | A Very Long Label That Exceeds Limit | @link-1 |
| Special characters | C++ Reference (2024) | @link-2 |
| Empty slug | Label produces empty string after sanitizing | @link-3 |
The link-N counter is independent from slug duplicate counters.
Examples
Section titled “Examples”Source:
Visit [GitHub](https://github.com) for code.Read the [MDN Web Docs](https://developer.mozilla.org) reference.See [Docs](https://docs.first.com) and [Docs](https://docs.second.com).And [Docs](https://docs.third.com) too.Check [A Very Long Documentation Title Here](https://example.com/long).Stay on [Home](./index.md).AI view:
Visit [GitHub][@github] for code.Read the [MDN Web Docs][@mdn-web-docs] reference.See [Docs][@docs] and [Docs][@docs-2].And [Docs][@docs-3] too.Check [A Very Long Documentation Title Here][@link-1].Stay on [Home](./index.md).@github,@mdn-web-docs,@docs— label-derived slugs@docs-2,@docs-3— duplicate slug with suffix@link-1— non-sluggable fallback (label exceeds 20 chars)[Home](./index.md)— short relative path, not converted
- Slugs and
@link-NIDs belong to thepagenamespace. - The
link-prefix is reserved — named shortcuts MUST NOT use it. - Auto-shortcuts are scoped to the current document. When the document changes, previous auto-shortcuts are invalidated.
- Same document content always produces the same auto-shortcuts (deterministic, top-to-bottom order).
Drill-in pattern ({@shortcut} = {value} in action responses)
Section titled “Drill-in pattern ({@shortcut} = {value} in action responses)”Principle — dual-mode legibility. SFMD documents stay useful without the String runtime: an AI reading the raw file can parse the actions, follow the
next:hints, and reason about what the app does. With String, the same document becomes interactive (shortcuts resolve, actions dispatch, navigation works). To preserve this dual mode:
/opentargets (pages, external resources) MUST appear as valid markdown links in source —[Label](url)or[Label][@id]. A plain markdown viewer renders them as clickable links; an AI without String still parses them as navigation./acttargets (action-only entities — no public URL, runtime binding required) can render as plain-text tokens (@post-1: Title). A markdown viewer can’t execute actions anyway, and an AI reading the source sees the action declarations plus thenext:line and knows what would happen under String.Don’t dress action dispatch in link form — it’s a broken link to any viewer that isn’t String, and it confuses the intent.
/act drill-in — plain-text tokens
Section titled “/act drill-in — plain-text tokens”Action response templates register inline shortcuts that bind opaque ids (post ids, record keys, tuples) to short, human-grade names the agent then passes to subsequent action calls. Pattern:
for: p in Response.body.posts{@post} = {p.id}- {@post}: {p.title} — by {p.author.name} in /{p.submolt_name}end:
next: /act.read @post-N · /act.upvote @post-N · /act.comment @post-N "..."Each loop iteration registers @post-1, @post-2, … pointing at
the post id. The agent sees @post-1: <title> in the output and
runs /act.read @post-3 to drill in — the runtime resolves
@post-3 back to the id when binding the action’s required field.
The next: line tells the agent which actions chain naturally
from this result.
Why plain text is correct here: a plain markdown viewer can’t run
/act.read anyway, so a [label](something) would just be a broken
or misleading link. The token form (@post-1: Title) is honest about
what the runtime is providing.
Tuple value shortcuts
Section titled “Tuple value shortcuts”When one logical handle needs to carry more than one identifier
— a comment addressed by (post_id, comment_id), a board cell
addressed by (column, row), anything composite — register the
shortcut as a tuple: comma-separated expressions inside
parentheses on the right-hand side.
for: c in Response.body.comments{@reply} = ({post}, {c.id})- {@reply}: {c.author.name}: {c.content}end:
next: /act.reply @reply-N "..."{post} here is the parent post id taken from the calling
action’s input fields. {c.id} is each comment’s id from the
response. Iteration N registers @reply-N with value
[post_id, comment_id_N].
The agent then runs /act.reply @reply-3 "thanks". The action’s
field receives the whole tuple; its URI/body template addresses
individual slots with {name[N]} indexing:
```act.replyPOST https://api.example.com/posts/{reply[0]}/comments/{reply[1]}/replies reply, -r: tuple (required) "(post_id, comment_id) from /act.comments" text, -t: string (required) "Reply body"```{reply[0]} substitutes the post id, {reply[1]} the comment id.
The agent never sees either id directly — only the opaque
@reply-3 handle. (Same indexing works inside body: templates;
see Actions → Body template.)
- One-element tuple normalizes to scalar.
(x)andxare equivalent — useful when the same template sometimes produces a single id and sometimes a pair. - Empty tuple
()is allowed (zero-element shortcut). - Nested braces are respected when splitting on commas.
({a}, {b}, {c})is three elements;({a, b})is one element whose value happens to contain a literal comma. {name}without an index emits the tuple joined by,— a debug fallback. In real templates, always address slots with{name[N]}.{name[N]}returns empty string whenNis out of range — fail-quiet rather than fail-loud. Validate at the API boundary./open @<tuple>is rejected withINVALID_TARGET: “Shortcut X is a value tuple, not a navigable target.” Tuples carry no URL, so navigation has nowhere to go. The error message suggests passing it to an action instead.
/open navigation — standard markdown links
Section titled “/open navigation — standard markdown links”Pages and external resources that an agent can /open MUST be
written as standard markdown links so they remain followable in any
viewer. Inline form:
[Home](string.md) · [Communities](communities.md) · [Profile](profile.md)Or reference form when the URL is dynamic:
for: s in Response.body.submolts- [{s.display_name}][@sub] — {s.subscriber_count} subscribers[@sub]: https://www.moltbook.com/sub/{s.name}end:
next: /open @sub-N to browse a communityThe agent runs /open @sub-3 (or /open communities.md), and a
plain markdown viewer renders the same line as a clickable link.
See cookbook/apps/moltbook for a complete reference: nav between
pages uses markdown links (string.md → communities.md); drill-in
into posts uses plain-text @post-N tokens.
Uniqueness
Section titled “Uniqueness”- Inline shortcut IDs MUST be unique within a document.
- Menu shortcut IDs MUST be unique within a menu file.
- Same IDs in different menus are allowed (namespacing prevents
collision:
@main.homevs@admin.home). - An inline shortcut MAY have the same ID as a menu shortcut. The inline shortcut takes priority during resolution.
Resolution priority
Section titled “Resolution priority”When a shortcut reference like [@id] is resolved:
- Inline shortcuts — defined in the current document
- Auto-generated shortcuts — slugs,
@slug-Nduplicates, and@link-Nfallbacks - Menu shortcuts — namespaced entries from nav menus
If a reference includes a namespace (@main.home), it resolves
directly to that menu. No priority lookup is needed.
Rules summary
Section titled “Rules summary”- Inline form:
[@id Label](url)— defines and displays in one line. - Reference form:
[Label][@id]+[@id]: url— separated. - AI always sees:
[Label][@id]— URL never in AI context. - IDs:
[a-z][a-z0-9_-]*— MUST NOT start withlink-. - Inline IDs MUST be unique within a document.
- Menu IDs MUST be unique within a menu file.
- Menu shortcuts are namespaced:
@menu_name.id. - Inline shortcuts use implicit
pagenamespace. - Auto-generated: external URLs and long paths only (short relative paths excluded).
- Slug from label if ≤ 20 chars, alphanumeric/space/hyphen only, and non-empty.
- Duplicate slugs:
@slug-2,@slug-3(per-slug counter). - Non-sluggable fallback:
@link-N(reserved prefix, global counter). - Resolution order: inline > auto > menu.
- In CommonMark viewers, both forms render as standard links.