v1.0 · Stable

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

terminal
pip install slim-parser

Write your first .slm file

hello.slm
@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

Python
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 concept: two audiences @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:

ZoneContentWho 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.

📄
File metadata Extension: .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.

Syntax
@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

PrefixLLM sees?Variable interpolated?Use for
@keystrippedYes — $keyModel, retry, timeout, secrets
@+keyvisibleYes — $keyAgent name, task ID, context

Type coercion

Header values are automatically typed — no quotes required for most types.

Examples
@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
  • @+include is 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.

SigilMeaning
# HeadingH1–H3 section headings (#, ##, ###)
- itemBullet point
1. stepOrdered step
| col |Table row — no separator row needed (unlike Markdown)
> KEYWORDDirective — see Directives
$keyVariable reference — see Variables
~ textComment — always stripped before LLM call
\XLiteral 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.

Syntax
=== 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

few-shot.slm
=== 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
⚠️
Injection safety Always call 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
KeywordPurpose
CALLInvoke a tool or function (see Schema Definitions)
ASSERTAssert a condition — pipeline fails if false
YIELDReturn a value from this step to the caller
EMITEmit a named event to the orchestrator event bus
LOGLog a message (orchestrator handles, never sent to LLM)
ABORTHalt execution with a reason string
WAITPause for a duration (5s, 2m)
RETRYRetry the current step N times
📌
Not all > are directives A > 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."
SyntaxMeaning
$keySimple reference — resolved at parse time from headers
$result.fieldRuntime dotpath — resolved by orchestrator during execution
$list[0]Indexed access — runtime
\$amountLiteral dollar sign — escape to prevent interpolation
📌
Variable values containing newlines: only the first line is used. This prevents multi-line injection through variable values.

Syntax Schema Definitions

First-class syntax for AI function call schemas. Define tools the agent can call using the :tool_name prefix.

Syntax
: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

SuffixMeaning
!Required — parse error if missing at call time
?Optional
= valueDefault value (implies optional)
->Return type declaration

Complete example

tools.slm
@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>
📌
Schema files use @slim: 1.0s (note the s suffix) to indicate schema extensions are active.

Reference Type System

Primitive types

TypeExamplesNotes
strhello, "hello"Default for unrecognized values. Quoted forces string.
int3, -10Auto-coerced in headers
float0.7, 3.14Requires decimal point
booltrue, falseCase-sensitive lowercase
nullnull, noneBoth accepted

First-class types (schema params)

TypeDescriptionExample
datetimeISO 86012026-05-10T14:30:00Z
urlValid URLhttps://example.com
uuidUUID v4550e8400-e29b-41d4-a716
jsonArbitrary JSONAny valid JSON object

Collection types

TypeSyntaxExample
Enum[a|b|c][prod|staging|dev]
Listlist<T>list<str>, list<Finding>
Dictdict<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:

Python
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
⚠️
Variable value injection Variable values containing newline characters: only the first line is used. Multi-line values cannot inject new headers or directives.

API Python Parser

Parsing a file

Python
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

PropertyTypeDescription
versionstrValue of @slim header
headersdict[str, Any]All @key headers (stripped, typed)
llm_headersdict[str, Any]All @+key headers (LLM-visible, typed)
sectionslist[Section]# headings with their content
blocksdict[str, Block]Named blocks by block name
schemasdict[str, Schema]:tool definitions
directiveslist[Directive]> KEYWORD calls in order
body_textstrFull body, variables interpolated
stripped_textstrbody_text minus ~ comments — send this to LLM
token_estimateintApproximate token count (len(stripped_text) / 4)
warningslist[str]Parse warnings (lenient mode only)

Utility functions

Python
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.

JavaScript
// 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

terminal
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

FeatureDescription
Syntax highlightingAll SLIM constructs colored using TextMate grammar
Code folding=== BLOCK … === /BLOCK regions fold/collapse
Bracket matching[type] tags and pipe tables
SnippetsCommon SLIM patterns: headers, blocks, schemas, directives
Line commentsCtrl+/ inserts ~
Auto-indentSchema 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

terminal
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

FeatureDescription
Syntax highlightingAll SLIM constructs colored, configurable per theme
Code folding=== BLOCK … === /BLOCK regions collapse
Color customizationSettings → Editor → Color Scheme → SLIM
Line commentingCtrl+/ 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

  1. Open Notepad++
  2. Go to LanguageUser Defined LanguageDefine your language…
  3. Click Import and select notepadpp-slim/slim.udl.xml
  4. Restart Notepad++
  5. Open any .slm file — highlighting applies automatically

Features

  • Syntax highlighting for all SLIM constructs using dark theme colors
  • === BLOCK … === /BLOCK fold regions
  • Line commenting with ~
  • Keyword highlighting: CALL ASSERT YIELD EMIT LOG ABORT WAIT RETRY

Reference BNF Grammar

SLIM v1.0 BNF
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

KeyTypeDescription
@slimstrFormat version. Required on first line. Current: 1.0
@modelstrTarget LLM. Always stripped — never use @+model.
@agentstrAgent identifier. Use @+agent to expose to LLM.
@modestrExecution mode (e.g. strict, lenient)
@retryintRetry limit for the orchestrator
@timeoutstrTimeout duration: 30s, 5m
@tagslist<str>Metadata tags for indexing and routing
@includeurl/pathImport another .slm file
@versionstrDocument 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

MarkdownSLIMNotes
--- frontmatter@key: valueUse @+key to keep in LLM text
# Heading# HeadingIdentical
- bullet- bulletIdentical
1. step1. stepIdentical
```lang code ```=== BLOCK [lang] … === /BLOCKNamed + typed + injection-safe
<!-- comment -->~ commentShorter, always stripped
| col | \n |---|| col |No separator row — saves ~6 tokens/table
*bold**bold*Identical
`inline``inline`Identical

Step-by-step migration

  1. Rename the file from .md to .slm
  2. Add @slim: 1.0 as the very first line
  3. Convert YAML frontmatter (--- block) to @key: or @+key: headers and remove the --- delimiters
  4. Replace ``` lang ``` fenced blocks with === BLOCK_NAME [lang] … === /BLOCK_NAME
  5. Replace <!-- comments --> with ~ comment lines
  6. Remove | --- | separator rows from all tables
  7. Run slim validate yourfile.slm to verify

SLIM-only features (no Markdown equivalent)

  • @key header stripping — tokens never reach LLM
  • $variable interpolation resolved at parse time
  • > DIRECTIVE command syntax for workflow steps
  • :tool schema definitions with typed parameters
  • === BLOCK [type] type tags and injection boundaries
  • ~ comments — always stripped, zero LLM overhead
  • list<T>, dict<K,V>, [enum|values] type system
  • Multi-document files with --- separators
  • @include imports
💡
Use the Playground The SLIM Playground has a built-in Markdown → SLIM converter. Paste your .md file and download the converted .slm output.