runxmd — add state and execution to your .md files. .xmd is plain Markdown with a run-intent label.
Project description
runxmd — run your Markdown files
You already write
.mdfiles.runxmdmakes them stateful. Add@memory,@tasks, and@workflowsections to any Markdown doc andrunxmdexecutes it — state persists back into the same file your team reads. Runs both.mdand.xmdfiles identically;.xmdis plain Markdown too, just a label that signals "this file is meant to be run."
The idea in one sentence
runxmd adds state and execution to .md files. You keep writing Markdown exactly
as you do today — it still renders in GitHub, VS Code, and every viewer. The
difference is that @memory persists across runs, @tasks track progress, and
@workflow steps actually execute. Static doc → stateful doc. Same file.
.md and .xmd — same Markdown, one is a signal
runxmd runs both .md and .xmd files. They use the exact same Markdown
grammar — there is no second syntax to learn, and the runtime parses them
identically. The only difference is the name, and the name is just a convention:
.md— a regular Markdown file. It might be plain prose, or it might have a@workflowsection or two. Either way,runxmdruns whatever executable sections it finds..xmd— also plain Markdown, byte-for-byte the same format. The.xmdextension is an opt-in label that says "this file is the runnable kind — it has the workflows, code, and memory."
Think of it like script.js vs script.test.js: both are 100% JavaScript, parsed
by the same engine. The .test. part doesn't change the language — it just tells
you (and your tools) the file's role at a glance. .xmd is exactly that:
"this is the executable kind of Markdown."
runxmd run notes.md # runs — finds the @workflow inside
runxmd run pipeline.xmd # runs — identical engine, identical grammar
Why bother with the .xmd label?
- A person scanning a repo sees
README.mdvsdeploy.xmdand instantly knows which one does something — without opening it. - An agent can be told "run the
.xmdfiles" without cracking open every.mdto check whether it contains workflows. - Editors and CI can give
.xmda "run" affordance while still rendering it as ordinary Markdown everywhere.
Bottom line: use .md if you just want your existing docs to run; use .xmd
when you want to flag "this Markdown is meant to be executed." The runtime treats
them the same — the choice is purely about intent and legibility.
What changes in your Markdown file? Almost nothing.
Take a doc you already have. Add @workflow, @memory, and @tasks sections.
They look like normal Markdown sections — because they are. The runtime just knows
how to execute them.
Before — a plain Markdown doc:
# Deploy Staging
Steps:
1. Pull latest image
2. Run migration
3. Restart service
Last run: manually on 2024-01-15
Status: unknown
Useful to read. Does nothing. To actually run it you need a separate shell script, a CI pipeline, or someone following the steps by hand. The "Last run" date goes stale the moment you forget to update it.
After — the same doc, now runnable:
# Deploy Staging
@goal
Deploy the latest image to staging and verify it.
@memory
last_run: "never"
runtime.status: "unknown"
@tasks
- [ ] pull image
- [ ] run migration
- [ ] restart service
@workflow deploy
- @shell
run: docker pull myapp:latest
- @shell
run: docker exec myapp python manage.py migrate
- @shell
run: docker restart myapp
- @http
url: https://staging.myapp.com/health
@on_done
set: memory.runtime.status = "deployed"
runxmd run deploy.md # or name it deploy.xmd — same result
Now the steps run. runtime.status is written back into the file. Next time
anyone opens it — human or agent — they can see exactly what happened and when.
The doc and the system are the same thing.
What you gain without giving anything up
Plain .md |
.md + runxmd |
|
|---|---|---|
| Reads in GitHub / VS Code | ✅ | ✅ |
| Human-readable prose | ✅ | ✅ |
| Edit in any text editor | ✅ | ✅ |
| Steps actually execute | ❌ | ✅ |
| State persists between runs | ❌ | ✅ |
{{ variables }} resolve live |
❌ | ✅ |
| File updates itself after running | ❌ | ✅ |
| AI agent can author and run it | ❌ | ✅ |
No new file format. No new tool to learn. Just your Markdown, with a runtime behind it.
Install
pip install runxmd
Zero third-party dependencies — pure Python standard library (≥ 3.9).
Or from source:
git clone https://github.com/shubham10divakar/xmd.git
cd xmd
pip install -e .
Running the tests
pip install -e ".[test]"
pytest
The suite includes a parametrized check that the same content runs identically
whether it's named .md or .xmd — the extension-agnostic guarantee, pinned down.
Quick start
Add runnable sections to any .md file:
# Hello
@memory
name: "world"
@workflow hello
- @print
text: "hello {{ memory.name }}"
- @shell
run: echo "shell works too"
- @python
run: |
print("and so does python")
@on_done
set: memory.runtime.ran_at = "now"
Run it:
runxmd run hello.md
▶ workflow: hello
step 1 @print ✓
hello world
step 2 @shell ✓
shell works too
step 3 @python ✓
and so does python
· memory written back
Open the file again — runtime.ran_at is now in @memory. The document updated
itself.
The runnable sections
You add these to any Markdown file. Each one uses the grammar most natural to what it is:
| Section | What it does | Grammar |
|---|---|---|
@goal |
Describes the intent (human/agent) | free prose |
@memory |
State that persists across runs | key: value lines |
@tasks |
A checklist the agent can tick | - [ ] / - [x] |
@workflow <name> |
Steps to execute in order | - @plugin + params |
@on_done |
Runs after the workflow finishes | set: memory.runtime.x = value |
Everything else in the file — prose, headings, tables, links — is untouched.
The runtime only acts on @ sections.
Plugins (what a step can do)
A step is - @plugin plus indented params:
| Plugin | Does | Key params |
|---|---|---|
@print |
Print text (with substitution) | text |
@shell |
Run a shell command | run |
@python @node @ruby @bash |
Run inline code | run (multiline block) |
@http |
Make an HTTP request | url, method, body |
@write |
Write a file (creates dirs) | path, content |
@read |
Read a file into output | path |
@llm |
Ask an LLM (needs ANTHROPIC_API_KEY) |
prompt, model, max_tokens |
Detect, don't install. Language plugins use whatever is already on your machine.
If ruby is missing, the step fails clearly and the run continues — runxmd never
installs anything for you.
Memory — state that lives in the file
@memory is what makes a Markdown file a system instead of a static doc.
- Read-in — memory keys are loaded when the run starts.
- Substitution —
{{ memory.key }}anywhere in a step is replaced live. - Write-back — changes are written back into the file after the run.
Safe co-editing (field ownership)
Both you and the runtime write to the same file, so ownership is split:
runtime.*keys — only the runtime writes these (via@on_done).- Everything else — yours. The runtime reads and preserves them but will never overwrite them. A hook that tries is refused with a warning.
Your {{ memory.notes }} is safe. {{ memory.runtime.status }} is the runtime's
to manage. Safe by design, not by convention.
Watch mode — re-run on save
runxmd watch report.md # re-run whenever you save
runxmd watch report.md --interval 0.5 # poll faster
runxmd watch report.md --max-runs 3 # stop after 3 runs (great for CI)
watch detects saves and re-runs. It will not loop on its own write-back —
only your edits trigger a re-run.
Agent mode — let the goal drive itself
runxmd agent turns the doc from a program you run into a goal that runs
itself:
Read @goal → plan @tasks → execute each task → update @memory → write back
runxmd agent project.md # plan (if no tasks), then run linked workflows
runxmd agent project.md --replan # regenerate tasks from the goal
runxmd agent project.md --autonomous # LLM generates AND runs steps for unlinked tasks
runxmd agent project.md --dry-run # show the plan without executing or writing
Two ways a task gets done:
- Workflow link (safe, default):
- [ ] run migration -> deploy— the agent runs the named workflow and ticks the task. - LLM-emitted steps (
--autonomous): the LLM generates steps for unlinked tasks and the agent runs them. Opt-in, because it executes model-generated commands.
Planning requires ANTHROPIC_API_KEY. Without it, the agent skips planning and
runs any pre-existing linked tasks. See examples/AGENT.xmd.
Commands
runxmd run <file> [--workflow NAME] [--no-write]
runxmd watch <file> [--interval S] [--max-runs N] [--no-write]
runxmd agent <file> [--replan] [--autonomous] [--model M] [--max-tokens N] [--dry-run]
runxmd parse <file>
runxmd validate <file>
runxmd --version
Why this matters now
AI agents already live in Markdown. They read instruction files, write notes,
track tasks with - [ ] checklists — but when they write a doc, nothing runs.
The doc and the system are always two separate things that drift apart.
runxmd closes that gap. An agent can write a .md file, you can point the
runtime at it, and it executes. The doc is the system. No translation layer.
Design principles
- Your Markdown, not a new format. The runtime reads
@sections; everything else is untouched prose. - Inline-first. Code lives in the document; the runtime is a thin dispatcher.
- Detect, don't install. Use what's on the machine; fail clearly when it's not.
- Stdlib only. Zero dependencies, including
@http. - Two-test rule for every feature: Could an agent write this with no examples? Does it give a human a reason to trust it with real work today?
Status & roadmap
Current: v1.0.1 — see SPEC-v0.0.3.md for the full contract.
- ✅ Parser, executor, CLI (
run/watch/agent/parse/validate) - ✅ Plugins: shell, inline languages, http, filesystem, llm
- ✅ Memory: read / substitute / write-back, with field-ownership safety
- ✅ Reactive
runxmd watch - ✅ Agent engine (
@goal→ auto-generate@tasks→ execute → update memory) - ⏳ Declarative events (
@on_file_change,@daily,@on_commit) - ⏳ Portable
@taskabstraction - ⏳ Multi-agent / distributed
Contributions and ideas welcome.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file runxmd-1.0.1.tar.gz.
File metadata
- Download URL: runxmd-1.0.1.tar.gz
- Upload date:
- Size: 40.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
08b7b6902b249554d9c253877ec3ec62d8565b4d73cc67c6ac226ebb496ccb09
|
|
| MD5 |
75c9f3a271e480fa5f3eb5845907ec33
|
|
| BLAKE2b-256 |
4f853340b2d046aff6d406d3a2c8e7694f714bbaf44984a3d3b6f591bcdc77e6
|
File details
Details for the file runxmd-1.0.1-py3-none-any.whl.
File metadata
- Download URL: runxmd-1.0.1-py3-none-any.whl
- Upload date:
- Size: 22.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
59080cd98dac24fb24cd2571ac2ff0cae73d722e0de225ad0f975f8d044f1d4f
|
|
| MD5 |
c85b9a6c20cbce77fd61757cc5546691
|
|
| BLAKE2b-256 |
067501e2b78f3629113a786b4f2bba3ab08f9d9e652020133dd594b2c3ae8a75
|