ikat
Weave Markdown into camera-ready LaTeX. ikat is an md→tex/pdf
pipeline whose compute core is Rust (via PyO3/maturin) with thin Python
wrappers: Markdown documents go in, conference-ready PDFs come out, and
every figure is generated — never screenshotted.
The name is a fabric pattern: in ikat weaving, threads are patterned before they reach the loom, so the design is carried by the material itself. Likewise, an ikat document carries its figures as data and diagram source: the pipeline weaves them into TikZ/pgfplots at build time, set in the document's own fonts. No matplotlib, no pasted PNGs, no font mismatch between text and figures.
What it does
- Markdown → LaTeX (
ikat.pipeline): headings, tables, citations, code, footnotes, and fenced elements → a complete.texdocument. - Mermaid → TikZ (
ikat._core.flowchart_to_tikz, Rust): thegraph TD/LRflowchart subset → atikzpicturewith deterministic layered layout. No Node.js, no browser, no SVG round-trip. - Data → pgfplots (
ikat._core, Rust): bar charts with min/max whiskers, log axes, line plots →tikzpicturecode compiled by your LaTeX installation, so figure fonts always match the paper. Legend placement is a keyword:auto(default) tries the inside corners against the drawn data and falls back below the axis;belowpins the under-axis row; the four corners andoutside-rightplace it by hand. Anything else is a build error. - Per-element spanning (
ikat.toml): one/two-column layout is not global — each element kind (diagram, plot, table) declarescolumnorwide, overridable per element from Markdown. - Compile (
ikat.compile):pdflatex/bibtexdriver that turns the woven tree into PDF.
Showcase paper
Read the ikat showcase paper (PDF), or browse its Markdown source and build instructions.
Layout
ikat/
src/ Rust core (pyo3): mermaid.rs (flowcharts), plot.rs
(pgfplots + legend auto-placement), table.rs, doc.rs,
esc.rs (unicode/citations), config.rs (ikat.toml),
texenv.rs (package scan), tectonic.rs (engine),
md_spike.rs (parser-spike harness, tests only)
python/ikat/ thin wrappers: pipeline.py (fences/spans/floats),
document.py (BuildSpec assembly), compile.py (pdflatex
driver), texenv.py (kpsewhich/tlmgr), cli.py (`ikat`
script), mcp_server.py (FastMCP stdio),
templates/ (9 heads + skeleton-plain)
tests/ pytest suite (runs against maturin develop build)
docs/ ARCHITECTURE.md, QUICKREF.md, RESEARCH.md, ROADMAP.md, FORMAT-DRAFT.md
skills/ Agent Skills: ikat guide, ikat-api, ikat-cli, ikat-mcp
examples/ mini.md starter + ikat-paper/ showcase (6-page paper
built by ikat itself), plot/float/skeleton proofs
Develop
Requires Rust (cargo) and Python ≥ 3.10.
&&
VIRTUAL_ENV=/.venv
LLM agent surface: API, MCP, CLI, skills
Use ikat programmatically through its Python API, automate it with the CLI, or connect the optional stdio MCP server as agent tools. The repo also includes focused Python API, CLI, and MCP skills, plus a general ikat guide. These interfaces use the same Rust-backed pipeline; the skills guide the agent but do not install or run ikat.
Same core operations across API, CLI, and MCP. Python API is the reference;
the CLI and MCP server call it directly (ikat[mcp] pins mcp>=1,<2).
|
|
|
|
--spec is BuildSpec JSON (diagrams/plots/table_captions/bib_style/…;
unknown keys ignored). MCP server over stdio:
// Claude Desktop / pi client config
{ "mcpServers": { "ikat": {
"command": "/path/to/.venv/Scripts/python.exe",
"args": ["-m", "ikat.mcp_server"],
"cwd": "/path/to/ikat" } } }
| operation | Python API | CLI | MCP tool |
|---|---|---|---|
| weave document | build_document / build_from_paths |
ikat build / weave |
weave_document |
| flowchart | flowchart_to_tikz |
ikat flowchart |
flowchart_to_tikz |
| bar chart | barchart_to_tikz |
ikat barchart (JSON, --legend) |
barchart_to_tikz |
| line plot | lineplot_to_tikz |
ikat lineplot (JSON, --legend) |
lineplot_to_tikz |
| check packages | check_tex_env |
ikat check |
check_tex_packages |
| install packages | ensure_tex_packages |
ikat check --install |
ensure_tex_packages |
| templates | list_templates / template_path / template_preset |
ikat templates / template |
list_templates / get_template |
| skeletons | list_skeletons / template_path |
ikat skeletons / skeleton |
list_skeletons / get_skeleton |
compile_pdf stays out of MCP (workdir-bound, minutes-long);
drive it from the CLI.
Floats: span, position, width
Every floatable element (diagram, picture, plot, table) takes
{...} attributes on its fence — tables via a %% table {...}
comment on the line above:
```mermaid {span=column pos=both width=0.8}
%% table {pos=barrier}
| attr | values | default |
|---|---|---|
span |
column | wide |
per-kind [spans] (picture falls back to diagram) |
pos |
top | bottom | both([!tb]) | page | here | force([H]) | barrier |
[floats] pos_default (top) |
width |
fraction of span (0.8) or TeX length (5cm) |
span width |
captionpos |
top | bottom |
figures bottom, tables top |
Hard LaTeX rules are build errors, not log mysteries: figure*
with here/force is illegal (use span=column); wide+bottom
auto-loads dblfloatfix; force/barrier auto-load
float/placeins (generated head) or are required in template
heads (validated). Document-wide tuning lives in [floats]
(fractions, counters, barrier_sections, pos_default) and is
emitted only when non-default — default documents weave
byte-identically.
Templates
The generated preamble fits the common case; journals are not
the common case. [template] in ikat.toml hands you the head
(level 1) or the whole document (level 3):
[]
= "journal-head.tex" # replaces the generated head
= ["\\usepackage{natbib}"] # extra lines before \\begin{document}
preamble_file(resolved relative to the toml) replaces everything before\begin{document}— class, packages, title, author. The body tail (\maketitle… bibliography …\end{document}) stays generated.- Safety over silence: ikat validates the replacement carries a
\documentclassand every package the woven body provably needs (tikzfor diagrams,pgfplotsfor plots,graphicxfor precompiled figures,tabularxfor tables). A head that drops one fails fast naming it, instead of dying in a TeX log. - Path-based builds resolve it for you:
build_from_paths(md, toml, spec);spec.preamble_overrideset directly beats the toml value.
Shipped library
Nine starting heads in ikat/templates/ (each documents its
companion config). Tokens {{{title}}}, {{{author}}} and
{{{thanks}}} fill from the manuscript H1 + spec author/thanks;
an emptied \\thanks{} is dropped (it kills IEEEtran titles).
| name | class | bib_style | needs flag |
|---|---|---|---|
arxiv |
article 11pt, arXiv-safe | IEEEtran | — |
article-1col / -2col |
article + geometry | IEEEtran | — |
article-3col |
article + multicol (all spans column) | IEEEtran | — |
ieee-conference |
IEEEtran conference (the generated default, explicit) | IEEEtran | — |
acm-sigconf |
acmart sigconf (no hyperref/lmodern/geometry: class-owned) | ACM-Reference-Format | abstract first |
springer-llncs |
llncs runningheads | splncs04 | — |
elsevier |
elsarticle preprint | elsarticle-num | abstract first |
aps |
revtex4-2 (title+authors ride AfterEndPreamble; no tabularx: REVTeX clashes) | apsrev4-2 | abstract first |
abstract_before_maketitle = true (in [template]) hoists the
body's abstract env before \\maketitle for top-matter classes
(ACM, Elsevier, APS set the abstract in the title block — a late
abstract is silently dropped, proven by probe). template_preset(name)
returns each head's bib_style + toml snippet:
=
# {'bib_style': 'elsarticle-num', 'toml': '[template]\nabstract_before_maketitle = true\n'}
Every head is compile-proven locally (abstract + TikZ + table
through pdflatex, title/abstract text verified in the PDF);
examples/headsproof.py re-runs the proof. Publisher classes
were installed from TeX Live archives into the local texmf tree
(same trick as IEEEtran/pgfplots) — on a full TeX Live they just
work. Heads are starting points, not submissions: always verify
against the publisher's proof.
Level 3: whole-document skeletons
skeleton = "paper-skel.tex" (in [template], mutually
exclusive with preamble_file) replaces the WHOLE document —
head and tail. The skeleton is complete .tex with tokens:
| token | fills from |
|---|---|
{{title}} {{author}} {{thanks}} |
as level 1 (brace them: \title{{{title}}}; empty \thanks{} drops) |
{{body}} |
woven body — required exactly once |
{{bibliography}} |
bib style + name — required exactly once unless bib_name is empty (biblatex skeletons print refs themselves) |
{{abstract}} |
the whole woven abstract env — required exactly once when the manuscript has one (needs abstract_before_maketitle) |
Unknown {{word}} tokens and duplicates are build errors;
% comments may document tokens freely (counting and rendering
skip them). Package validation scans the whole skeleton, and
preamble_append splices before \begin{document} as usual.
This unlocks multicol wraps, per-journal tails, and biblatex —
whatever the generated tail cannot express. skeleton-plain.tex
(article + the tokens, placeins/float preloaded) is the
starting point, compile-proven to PDF; level-1 heads stay the
recommended path.
LaTeX requirements
ikat emits standard LaTeX; it never bundles classes or packages.
You need a working TeX installation with pdflatex (and bibtex
if you stage a .bib). TeX Live scheme-full covers everything;
minimal schemes (TinyTeX included) need at least:
| what | packages | needed when |
|---|---|---|
| base document | amsmath amssymb tabularx graphicx hyperref lmodern textcomp + your document class (IEEEtran, …) |
always |
| inline diagrams | pgf (tikz) + TikZ libraries shapes.geometric arrows.meta positioning |
any woven tikzpicture |
| data plots | pgfplots (compat 1.18) |
any woven axis environment |
Precompiled-PDF figures need nothing beyond the base set — one reason to prefer them for journal submissions.
Ask ikat what a given body needs instead of guessing:
# ['\\usepackage{tikz}',
# '\\usetikzlibrary{shapes.geometric,arrows.meta,positioning}']
The document builder does this automatically: build_document
scans the woven body and injects only the packages it contains.
Never debug a missing .sty by hand again — the texliveonfly-style
helper probes any .tex preamble and installs what's absent:
# {'missing': ['pgfplots.sty'], 'unprobed': [], 'ok': False, ...}
# tlmgr install + re-probe
compile_pdf(workdir, main, ensure_packages=True) runs the same
check before compiling and raises with a fix-it hint (manual
texmf install or MiKTeX Portable) instead of a cryptic TeX error.
Notes:
- If
tlmgrfails (broken mirror/GPG — seen in the wild), install from a TeX Live archive instead: unpacksystems/texlive/tlnet/archive/<pkg>.tar.xz, copy itstex/tree into your texmf-local tree, runmktexlsr. That is how the dev setup gainedIEEEtranandpgfplots. Windows alternative: MiKTeX Portable needs no admin rights and auto-installs missing packages at compile time (its own manager/mirrors); ikat'scompile_pdfshells topdflatex, so it works unchanged. - arXiv's TeX Live ships everything above, so an ikat bundle
(
.tex+.bbl+ figure PDFs, no class/style files) compiles there as-is.
License
MIT OR Apache-2.0.