SLIM Documentation
Complete reference for Structured LLM Instruction Markup — syntax, parser API, plugins, and migration from Markdown.
Start Getting Started
SLIM is a plain-text format for AI prompts, agent configs, and tool schemas. It replaces Markdown in AI pipelines, stripping orchestrator metadata before the LLM call and saving an average of 43% of tokens.
Install the Python parser
pip install slim-parser
Write your first .slm file
@slim: 1.0 @model: claude-opus-4-7 @+agent: Greeter @+name: Alice ~ This comment is stripped before the LLM call # Role You are $agent. Greet $name warmly. # Instructions - Be concise and friendly - Respond in one sentence
Parse and use it
from slim_parser import SLIMParser
doc = SLIMParser().parse_file("hello.slm")
# Orchestrator reads metadata
model = doc.headers["model"] # "claude-opus-4-7"
agent = doc.headers["agent"] # "Greeter"
# Send only the LLM text (stripped)
llm_text = doc.stripped_text
# → "@+agent: Greeter\n@+name: Alice\n# Role\nYou are Greeter..."
# Call the LLM
response = client.messages.create(
model=model,
messages=[{"role": "user", "content": llm_text}]
)
@key headers are for the orchestrator — they are stripped before the LLM call.
@+key headers are for the LLM — they appear at the top of the prompt text.
Intro Overview
Every SLIM file is divided into three strictly ordered zones:
| Zone | Content | Who reads it? |
|---|---|---|
| Header Zone | Lines starting with @key or @+key |
Orchestrator (@key) and LLM (@+key) |
| Body Zone | Prose, headings, bullets, directives, tables | LLM (primary) — variables interpolated first |
| Block Zone | === NAME … === /NAME regions in the body |
LLM sees content; parser extracts named blocks |
Files use the .slm extension with MIME type text/slim and UTF-8 encoding.
.slm · MIME: text/slim · Encoding: UTF-8 · Line endings: LF preferred, CRLF tolerated
Syntax Header Zone
The header zone starts at line 1 and ends at the first # heading or non-blank, non-@ line.
@key: value ~ orchestrator-only, stripped before LLM @+key: value ~ LLM-visible, also available as $key variable @include: ./base.slm ~ import another .slm file
Strip semantics
| Prefix | LLM sees? | Variable interpolated? | Use for |
|---|---|---|---|
| @key | stripped | Yes — $key | Model, retry, timeout, secrets |
| @+key | visible | Yes — $key | Agent name, task ID, context |
Type coercion
Header values are automatically typed — no quotes required for most types.
@retry: 3 ~ → int @threshold: 0.7 ~ → float @async: true ~ → bool @context: null ~ → None/null @stack: python, js ~ → list["python", "js"] @id: "42" ~ → str (quoted prevents int coercion) @desc: First line second line ~ → "First line second line" (continuation)
@include
Import another .slm file. Resolved before any other processing. Included @+ headers merge into the current LLM context; included @ headers are defaults the current file can override.
@include: ./base/agent.slm @include: ./tools/mcp_tools.slm
- Max include depth: 5 levels
- Circular includes: parse error
@+includeis invalid — includes are always orchestrator-only
Multi-document files
Use --- on its own line to separate independent documents in one file. Each is parsed with its own header zone.
@slim: 1.0 @+agent: Planner # Role You plan tasks. --- @slim: 1.0 @+agent: Executor # Role You execute tasks.
Syntax Body Zone
Everything after the header zone. This is the main content the LLM reads. Variables are interpolated before delivery.
| Sigil | Meaning |
|---|---|
| # Heading | H1–H3 section headings (#, ##, ###) |
| - item | Bullet point |
| 1. step | Ordered step |
| | col | | Table row — no separator row needed (unlike Markdown) |
| > KEYWORD | Directive — see Directives |
| $key | Variable reference — see Variables |
| ~ text | Comment — always stripped before LLM call |
| \X | Literal escape — \@, \$, \=== etc. |
| `code` | Inline code (passed through verbatim) |
| *bold* / _italic_ | Inline formatting (passed through verbatim) |
Tables
Pipe-delimited rows. No |---| separator row — one of SLIM's token savings over Markdown.
| Format | Tokens | Readable | | JSON | 340 | Medium | | SLIM | 175 | High |
Syntax Block Zone
Named, typed regions that can appear anywhere in the body zone. Block boundaries are injection-safe — user content can't escape them.
=== BLOCK_NAME content === /BLOCK_NAME === BLOCK_NAME [type] typed content === /BLOCK_NAME
Rules
- Block names:
SCREAMING_SNAKE_CASE, ASCII only,[A-Z][A-Z0-9_]* - Closing tag must mirror the opening name exactly
- Type tag is optional:
[python],[json],[sql],[bash],[raw],[csv],[ascii],[base64:png] - Nesting: single level only (one block inside another is valid; deeper is a parse error)
- Content is captured verbatim — whitespace and blank lines preserved
- Escape
===inside a block: prefix with\→\===becomes literal
Example with nested blocks
=== EXAMPLES === EXAMPLE_1 user: Is this SQL safe? assistant: No — SQL injection on line 2. === /EXAMPLE_1 === EXAMPLE_2 user: How do I fix it? assistant: Use parameterized queries. === /EXAMPLE_2 === /EXAMPLES
sanitize_user_content() before embedding user-provided text in a block.
An unescaped === /BLOCK_NAME in user input would break out of the block boundary.
Syntax Directives
Lines beginning with > followed by a reserved ALL_CAPS keyword. Used to express agent workflow steps.
> CALL tool_name(param: value, param2: $var) > ASSERT $result.status == complete > YIELD $result.report > EMIT event_name(payload: $data) > LOG "Step completed" > ABORT "Reason for stopping" > WAIT 5s > RETRY 3
| Keyword | Purpose |
|---|---|
| CALL | Invoke a tool or function (see Schema Definitions) |
| ASSERT | Assert a condition — pipeline fails if false |
| YIELD | Return a value from this step to the caller |
| EMIT | Emit a named event to the orchestrator event bus |
| LOG | Log a message (orchestrator handles, never sent to LLM) |
| ABORT | Halt execution with a reason string |
| WAIT | Pause for a duration (5s, 2m) |
| RETRY | Retry the current step N times |
> followed by anything other than a reserved keyword is treated as literal prose.
For example: > 90% of bugs are caught early — not a directive.
Syntax Variables
$key references are resolved by the parser before the body text is sent to the LLM. Values come from @key or @+key headers.
@slim: 1.0 @+agent: SecurityBot @+task: PR-942 You are $agent reviewing $task. ~ → "You are SecurityBot reviewing PR-942."
| Syntax | Meaning |
|---|---|
| $key | Simple reference — resolved at parse time from headers |
| $result.field | Runtime dotpath — resolved by orchestrator during execution |
| $list[0] | Indexed access — runtime |
| \$amount | Literal dollar sign — escape to prevent interpolation |
Syntax Schema Definitions
First-class syntax for AI function call schemas. Define tools the agent can call using the :tool_name prefix.
:tool_name desc: Human-readable description of what this tool does param1!: str ~ required parameter param2?: int = 10 ~ optional with default param3?: [a|b|c] = a ~ enum with default -> result: json ~ return type
Property suffixes
| Suffix | Meaning |
|---|---|
| ! | Required — parse error if missing at call time |
| ? | Optional |
| = value | Default value (implies optional) |
| -> | Return type declaration |
Complete example
@slim: 1.0s
:search_web
desc: Search the internet for recent information
query!: str
max_results?: int = 10
safe_search?: bool = true
-> results: json
:run_code
desc: Execute code in a sandboxed environment
language!: [python|javascript|bash]
code!: str
timeout?: int = 30
-> output: str
-> exit_code: int
Custom type definitions
=== TYPE_DEF Finding: line!: int severity!: [low|medium|high|critical] message!: str fix?: str === /TYPE_DEF :analyze_code desc: Run static analysis on a code snippet code!: str language?: [python|js|java|go|rust] = python -> score: float, findings: list<Finding>
@slim: 1.0s (note the s suffix) to indicate schema extensions are active.
Reference Type System
Primitive types
| Type | Examples | Notes |
|---|---|---|
| str | hello, "hello" | Default for unrecognized values. Quoted forces string. |
| int | 3, -10 | Auto-coerced in headers |
| float | 0.7, 3.14 | Requires decimal point |
| bool | true, false | Case-sensitive lowercase |
| null | null, none | Both accepted |
First-class types (schema params)
| Type | Description | Example |
|---|---|---|
| datetime | ISO 8601 | 2026-05-10T14:30:00Z |
| url | Valid URL | https://example.com |
| uuid | UUID v4 | 550e8400-e29b-41d4-a716 |
| json | Arbitrary JSON | Any valid JSON object |
Collection types
| Type | Syntax | Example |
|---|---|---|
| Enum | [a|b|c] | [prod|staging|dev] |
| List | list<T> | list<str>, list<Finding> |
| Dict | dict<K,V> | dict<str,int> |
Syntax Comments
Lines starting with ~ (tilde) are stripped by all SLIM parsers. They never reach the LLM. Use them for author notes, TODOs, disabled content, or debugging hints.
~ Author: Jane Doe ~ TODO: add edge case for empty input ~ @+agent: DisabledAgent ← never parsed, entire line stripped
Comments are valid anywhere in the file — header zone, body zone, inside blocks.
Security Security Model
Zone ordering enforcement
The header zone must precede the body zone. Any @key found after the first # heading or non-blank non-@ line is treated as literal body text, not a header. This prevents header injection — user content placed before headers cannot override @model, @include, or other orchestrator fields.
Block boundary injection
All parsers validate that block content does not contain unescaped === lines. An unescaped === inside a block is a parse error in strict mode, a warning in lenient mode.
sanitize_user_content()
Always call this before embedding user-provided text in a SLIM document. It escapes all SLIM sigils:
from slim_parser import sanitize_user_content
user_input = "@model: evil-model\n> ABORT everything"
safe = sanitize_user_content(user_input)
# → "\@model: evil-model\n\> ABORT everything"
doc_text = f"""
@slim: 1.0
@+task: review
=== USER_CONTENT
{safe}
=== /USER_CONTENT
"""
Characters escaped: @, $, >, ===, ~ → \@, \$, \>, \===, \~
@include path traversal
- Paths resolved relative to the including file's directory
- Absolute paths: allowed but linters should flag them
- Network URLs: disabled by default; parser-implementation-dependent
- Circular includes: parse error
API Python Parser
Parsing a file
from slim_parser import SLIMParser, ParseMode
parser = SLIMParser()
# From file
doc = parser.parse_file("agent.slm")
# From string
doc = parser.parse(slm_text)
# Strict mode (raises on errors)
doc = parser.parse(slm_text, mode=ParseMode.STRICT)
# Lenient mode (collects warnings, returns partial)
doc = parser.parse(slm_text, mode=ParseMode.LENIENT)
SLIMDocument properties
| Property | Type | Description |
|---|---|---|
| version | str | Value of @slim header |
| headers | dict[str, Any] | All @key headers (stripped, typed) |
| llm_headers | dict[str, Any] | All @+key headers (LLM-visible, typed) |
| sections | list[Section] | # headings with their content |
| blocks | dict[str, Block] | Named blocks by block name |
| schemas | dict[str, Schema] | :tool definitions |
| directives | list[Directive] | > KEYWORD calls in order |
| body_text | str | Full body, variables interpolated |
| stripped_text | str | body_text minus ~ comments — send this to LLM |
| token_estimate | int | Approximate token count (len(stripped_text) / 4) |
| warnings | list[str] | Parse warnings (lenient mode only) |
Utility functions
from slim_parser import sanitize_user_content, SLIMParser # Sanitize user content before embedding safe = sanitize_user_content(user_text) # Format a .slm file (canonical form) parser = SLIMParser() canonical = parser.format(slm_text) # Validate without parsing full doc errors = parser.validate(slm_text) # list[str], empty = valid
API JavaScript Library
The browser/Node.js library is available in website/js/slim.js and exposed as window.SLIM.
// Parse a SLIM document
const doc = SLIM.parse(slmText);
// → { headers, llmHeaders, sections, blocks, schemas,
// directives, bodyText, strippedText, tokenEstimate }
// Convert Markdown → SLIM
const slm = SLIM.mdToSlm(markdownText);
// Convert JSON → SLIM
const slm = SLIM.jsonToSlm(jsonText);
// Syntax-highlight SLIM text (returns HTML string)
const html = SLIM.highlight(slmText);
// Estimate token count
const tokens = SLIM.estimateTokens(text);
// → Math.round(text.length / 4)
// Sanitize user content before embedding
const safe = SLIM.sanitizeUserContent(userText);
Plugins VS Code Extension
From Marketplace
Search "SLIM Language" in the Extensions panel and click Install.
From VSIX
Build from source and install via Extensions → ··· → Install from VSIX.
Build from source
cd vscode-slim npm install npm run package # produces slim-language-1.0.0.vsix code --install-extension slim-language-1.0.0.vsix
Features
| Feature | Description |
|---|---|
| Syntax highlighting | All SLIM constructs colored using TextMate grammar |
| Code folding | === BLOCK … === /BLOCK regions fold/collapse |
| Bracket matching | [type] tags and pipe tables |
| Snippets | Common SLIM patterns: headers, blocks, schemas, directives |
| Line comments | Ctrl+/ inserts ~ |
| Auto-indent | Schema property indentation |
Plugins IntelliJ IDEA Plugin
From JetBrains Marketplace
Settings → Plugins → Marketplace → search "SLIM Language" → Install.
From ZIP
Settings → Plugins → ⚙ → Install Plugin from Disk → select ZIP file.
Build from source
cd intellij-slim ./gradlew buildPlugin # → build/distributions/intellij-slim-1.0.0.zip ./gradlew runIde # opens sandbox IDE for testing
Requirements
- IntelliJ IDEA 2023.2+ (Community or Ultimate)
- Java 17+
- Gradle 8.5+
Features
| Feature | Description |
|---|---|
| Syntax highlighting | All SLIM constructs colored, configurable per theme |
| Code folding | === BLOCK … === /BLOCK regions collapse |
| Color customization | Settings → Editor → Color Scheme → SLIM |
| Line commenting | Ctrl+/ inserts ~ |
| File type icon | .slm files shown with SLIM icon |
| Bracket matching | [type] tags highlighted |
Plugins Notepad++ UDL
SLIM syntax highlighting for Notepad++ via a User Defined Language (UDL) XML file.
Install
- Open Notepad++
- Go to Language → User Defined Language → Define your language…
- Click Import and select
notepadpp-slim/slim.udl.xml - Restart Notepad++
- Open any
.slmfile — highlighting applies automatically
Features
- Syntax highlighting for all SLIM constructs using dark theme colors
=== BLOCK … === /BLOCKfold regions- Line commenting with
~ - Keyword highlighting:
CALL ASSERT YIELD EMIT LOG ABORT WAIT RETRY
Reference BNF Grammar
document ::= header_zone body_zone
header_zone ::= (header_line | blank_line)*
header_line ::= at_key ": " value newline
| "@include" ": " path newline
at_key ::= ("@" | "@+") [a-z][a-z0-9_-]*
body_zone ::= (section | block | schema | directive
| table_row | bullet | ordered_step
| paragraph | comment | blank_line)*
section ::= "#"+ " " text newline
block ::= block_open block_content block_close
block_open ::= "===" " " BLOCK_NAME (" [" type "]")? newline
block_close ::= "===" " /" BLOCK_NAME newline
block_content ::= (escaped_line | non_boundary_line)*
escaped_line ::= "\===" text newline
schema ::= ":" identifier newline property+
property ::= " " identifier ("!" | "?")? ": " type
(" = " value)? newline
| " " "->" " " return_spec newline
directive ::= ">" " " KEYWORD (" " args)? newline
table_row ::= "|" (cell "|")+ newline
bullet ::= "-" " " text newline
ordered_step ::= [0-9]+ "." " " text newline
comment ::= "~" text? newline
variable_ref ::= "$" identifier ("." identifier | "[" integer "]")*
BLOCK_NAME ::= [A-Z][A-Z0-9_]*
KEYWORD ::= "CALL" | "ASSERT" | "YIELD" | "EMIT"
| "LOG" | "ABORT" | "WAIT" | "RETRY"
Reference Reserved Header Keys
| Key | Type | Description |
|---|---|---|
| @slim | str | Format version. Required on first line. Current: 1.0 |
| @model | str | Target LLM. Always stripped — never use @+model. |
| @agent | str | Agent identifier. Use @+agent to expose to LLM. |
| @mode | str | Execution mode (e.g. strict, lenient) |
| @retry | int | Retry limit for the orchestrator |
| @timeout | str | Timeout duration: 30s, 5m |
| @tags | list<str> | Metadata tags for indexing and routing |
| @include | url/path | Import another .slm file |
| @version | str | Document version for your own versioning |
Custom keys are permitted. All reserved keys may be used with @+ prefix to expose to LLM, except @model and @include.
Guide Migration from Markdown
Structural equivalences
| Markdown | SLIM | Notes |
|---|---|---|
| --- frontmatter | @key: value | Use @+key to keep in LLM text |
| # Heading | # Heading | Identical |
| - bullet | - bullet | Identical |
| 1. step | 1. step | Identical |
| ```lang code ``` | === BLOCK [lang] … === /BLOCK | Named + typed + injection-safe |
| <!-- comment --> | ~ comment | Shorter, always stripped |
| | col | \n |---| | | col | | No separator row — saves ~6 tokens/table |
| *bold* | *bold* | Identical |
| `inline` | `inline` | Identical |
Step-by-step migration
- Rename the file from
.mdto.slm - Add
@slim: 1.0as the very first line - Convert YAML frontmatter (
---block) to@key:or@+key:headers and remove the---delimiters - Replace
``` lang ```fenced blocks with=== BLOCK_NAME [lang] … === /BLOCK_NAME - Replace
<!-- comments -->with~comment lines - Remove
| --- |separator rows from all tables - Run
slim validate yourfile.slmto verify
SLIM-only features (no Markdown equivalent)
@keyheader stripping — tokens never reach LLM$variableinterpolation resolved at parse time> DIRECTIVEcommand syntax for workflow steps:toolschema definitions with typed parameters=== BLOCK [type]type tags and injection boundaries~comments — always stripped, zero LLM overheadlist<T>,dict<K,V>,[enum|values]type system- Multi-document files with
---separators @includeimports
.md file and download the converted .slm output.