Cleans a Quarto (.qmd) file by replacing LaTeX holdovers with proper Quarto/Pandoc syntax so the document renders correctly to HTML, PDF (Typst/LaTeX), and Word. Fixes citations (\citep → [@key]), cross-references (\ref → @label), figures (\includegraphics → markdown), tables, text formatting (\textbf → **bold**), inline math (\( \) → $ $), hyperlinks (\href → [text](url)), footnotes (\footnote → ^[]), list environments, section headings, and R Markdown chunk options (dot-style → #| YAML). Us...
Scanned 5/27/2026
Install via CLI
openskills install christopherkenny/skills---
name: quarto-clean
description: 'Cleans a Quarto (.qmd) file by replacing LaTeX holdovers with proper Quarto/Pandoc syntax so the document renders correctly to HTML, PDF (Typst/LaTeX), and Word. Fixes citations (\citep → [@key]), cross-references (\ref → @label), figures (\includegraphics → markdown), tables, text formatting (\textbf → **bold**), inline math (\( \) → $ $), hyperlinks (\href → [text](url)), footnotes (\footnote → ^[]), list environments, section headings, and R Markdown chunk options (dot-style → #| YAML). Use when asked to clean, fix, modernize, or port a Quarto or R Markdown document; when LaTeX commands appear in a .qmd file; when a document fails to render to Word or Typst; or when citations/cross-references do not appear in non-LaTeX output.'
metadata:
author: Christopher T. Kenny
version: 1.1
---
# Quarto Clean
You clean a Quarto (`.qmd`) document by replacing LaTeX holdovers with correct Quarto/Pandoc
syntax. Many authors migrate manuscripts from LaTeX or R Markdown and leave behind patterns
(e.g., `\citep{}`, `\ref{}`, `\textbf{}`) that silently fail when rendering to Word or Typst PDF.
This skill finds and fixes them all in a single pass.
**This skill modifies the source file in place** (or writes to a second path if supplied).
Before writing, tell the user what file will be changed and remind them that `git diff` or
file history can recover the original.
---
## Input Arguments
| Position | Required | Description |
|----------|----------|-------------|
| 1 | **Yes** | Path to the `.qmd` file to clean (e.g., `paper/paper.qmd`). Cleaned in place by default. |
| 2 | No | Output path. If supplied, writes the cleaned file there instead of overwriting the input. |
**Example invocations:**
```
/quarto-clean paper/paper.qmd
/quarto-clean paper/paper.qmd drafts/paper-clean.qmd
```
---
## Step-by-Step Workflow
### 1. Parse Arguments
Identify the input `.qmd` path (arg 1) and output path (arg 2, or same as arg 1 if omitted).
### 2. Read the File
Use the `Read` tool to load the entire file. Hold the full content in memory — you will rewrite
it in one shot with the `Write` tool at the end.
### 3. Apply All Transformations
Work through the full content and apply every applicable transformation from the reference
tables below. Key principles:
- **Preserve protected regions**: Never modify content inside fenced code blocks (` ``` ` or
`~~~`), inline code (`` ` `` ), display math (`$$...$$`), inline math (`$...$`), or YAML
front matter (`---` to `---`). Treat these regions as opaque.
- **Handle nesting correctly**: Because you understand the full document structure, handle nested
commands naturally. `\textbf{something \cite{key}}` → `**something [@key]**`.
`\footnote{See \cite{key} for details}` → `^[See [@key] for details]`.
- **Apply all categories**: Work through every category in the reference below. A single command
(`\textbf{\emph{text}}`) may require multiple rules (`***text***`).
- **Be complete**: Scan the entire file. Do not stop after the first occurrence of a pattern —
fix every instance.
### 4. Write the Cleaned File
Use the `Write` tool to overwrite the output path with the fully cleaned content. Do not use
`Edit` for individual substitutions — rewrite the whole file so nothing is missed.
### 5. Report to the User
Tell the user:
- **Output path** written
- **Summary of fixes** by category (how many of each type were converted)
- **Items requiring manual review** — any patterns you could not safely convert automatically
(list with approximate line numbers so the user can find them)
- **Reminder** to run `quarto render` to verify
---
## Transformation Reference
Apply every applicable rule. Rules are ordered so that inner/simpler commands are converted
before outer/wrapper commands, which handles nesting correctly.
### Citations
Convert all natbib/biblatex citation commands to Pandoc bracket syntax. These must work in
all output formats (HTML, Word, Typst PDF).
| LaTeX | Quarto | Notes |
|-------|--------|-------|
| `\cite{key}` | `[@key]` | |
| `\citep{key}` | `[@key]` | |
| `\citep{a,b}` | `[@a; @b]` | Split comma-separated keys |
| `\citep[p.~5]{key}` | `[@key, p. 5]` | Locator (suffix only) |
| `\citep[see][p.~5]{key}` | `[see @key, p. 5]` | Prefix + locator |
| `\citet{key}` | `@key` | Narrative / in-text citation |
| `\citet{a,b}` | `@a and @b` | Multiple narrative keys |
| `\citealt{key}` | `@key` | No parentheses |
| `\citeauthor{key}` | `@key` | Loses "author-only" semantics; flag in report |
| `\citeyear{key}` | `[-@key]` | Year only; suppresses author |
| `\nocite{key}` | Move to YAML `nocite: '@key'` | Remove from body; add to front matter |
| `\nocite{*}` | Move to YAML `nocite: '@*'` | |
### Cross-References
Convert `\label` and `\ref` commands. The label prefix maps directly:
| LaTeX prefix | Quarto prefix | Example LaTeX | Example Quarto |
|--------------|--------------|---------------|----------------|
| `fig:` | `fig-` | `\label{fig:map}` → `{#fig-map}` | `\ref{fig:map}` → `@fig-map` |
| `tab:` or `table:` | `tbl-` | `\label{tab:results}` | `@tbl-results` |
| `sec:` or `section:` | `sec-` | `\label{sec:intro}` | `@sec-intro` |
| `eq:` or `eqn:` | `eq-` | `\label{eq:ols}` | `@eq-ols` |
| `lst:` | `lst-` | | |
| `thm:`, `lem:`, `prop:`, etc. | same with `-` | | |
Rules:
- `\ref{type:name}` → `@type-name` (replace `:` with `-`, map prefix per table)
- `\autoref{type:name}` → `@type-name`
- `\eqref{eq:name}` → `@eq-name`
- **Drop redundant type words**: `Figure \ref{fig:x}` → `@fig-x` (Quarto auto-generates
"Figure N"). Same for "Table", "Section", "Equation", "Appendix".
- `\label{type:name}` must be moved to the appropriate Quarto element as `{#type-name}`:
- On a figure: `{#fig-name}`
- On a table caption: `: Caption {#tbl-name}`
- On a heading: `## Title {#sec-name}`
- On display math: `$$...$$ {#eq-name}` (on the line after `$$`)
- Replace underscores with hyphens in the name part: `fig:my_fig` → `{#fig-my-fig}`
### R Markdown Chunk Options
Convert old-style inline chunk options to `#|` YAML comments. The fence header becomes bare
(language only), and each option becomes a `#|` line inside the chunk.
**Before:**
```
```{r my-label, echo=FALSE, fig.cap="My figure", fig.width=6, out.width="80%"}
```
**After:**
```
```{r}
#| label: my-label
#| echo: false
#| fig-cap: "My figure"
#| fig-width: 6
#| out-width: "80%"
```
Option name mapping (dots → dashes, booleans → lowercase):
| Old | New |
|-----|-----|
| `fig.cap` | `fig-cap` |
| `fig.width` | `fig-width` |
| `fig.height` | `fig-height` |
| `fig.align` | `fig-align` |
| `fig.alt` | `fig-alt` |
| `fig.subcap` | `fig-subcap` |
| `fig.pos` | `fig-pos` |
| `fig.env` | `fig-env` |
| `out.width` | `out-width` |
| `out.height` | `out-height` |
| `echo=FALSE` | `echo: false` |
| `eval=FALSE` | `eval: false` |
| `warning=FALSE` | `warning: false` |
| `message=FALSE` | `message: false` |
| `include=FALSE` | `include: false` |
| `cache=TRUE` | `cache: true` |
| `results='hide'` | `output: false` |
| `results='asis'` | `output: asis` |
| `tidy=TRUE` | *(remove — deprecated)* |
The first unnamed token in the old header is the label: `{r my-label, echo=FALSE}` → `#| label: my-label`.
### Figures
| LaTeX | Quarto |
|-------|--------|
| `\includegraphics{file}` | `` |
| `\includegraphics[width=0.8\textwidth]{file}` | `{width=80%}` |
| `\includegraphics[width=5cm]{file}` | `{width=5cm}` |
| `\centering` inside figure env | `fig-align="center"` attribute on the figure |
**Full figure environment:**
```latex
\begin{figure}[h]
\centering
\includegraphics[width=0.8\textwidth]{figs/map.pdf}
\caption{A map of the study region.}
\label{fig:map}
\end{figure}
```
Becomes:
```markdown
{#fig-map width=80% fig-align="center"}
```
- `\begin{figure*}` → add `fig-env="figure*"` to the attribute block
- For computational figures in code chunks, use `#| fig-cap:` and `#| label: fig-...`
### Tables
**Markdown / pipe tables:**
```markdown
| Col A | Col B |
|:------|------:|
| val | 1.23 |
: My table caption. {#tbl-results}
```
- Column alignment from tabular spec: `l` → `:---`, `r` → `---:`, `c` → `:---:`
- Caption goes *below* the table, after `: `, with `{#tbl-label}` at the end
- For computational tables (knitr/kable output), add chunk options instead:
```
#| label: tbl-results
#| tbl-cap: "My table caption."
```
**LaTeX table environment → Quarto pipe table:**
```latex
\begin{table}
\caption{My table caption.}
\label{tab:results}
\begin{tabular}{lrr}
\hline
Method & Estimate & SE \\
\hline
OLS & 1.23 & 0.45 \\
IV & 1.89 & 0.67 \\
\hline
\end{tabular}
\end{table}
```
Becomes:
```markdown
| Method | Estimate | SE |
|:-------|--------:|-----:|
| OLS | 1.23 | 0.45 |
| IV | 1.89 | 0.67 |
: My table caption. {#tbl-results}
```
### Text Formatting
Handle nesting: `\textbf{\emph{text}}` → `***text***`.
| LaTeX | Quarto |
|-------|--------|
| `\textbf{text}` | `**text**` |
| `\textit{text}` | `*text*` |
| `\emph{text}` | `*text*` |
| `\texttt{text}` | `` `text` `` |
| `\underline{text}` | `[text]{.underline}` |
| `\textsc{text}` | `[text]{.smallcaps}` |
| `\textsuperscript{x}` | `^x^` |
| `\textsubscript{x}` | `~x~` |
| `\sout{text}` | `~~text~~` |
| `\footnote{text}` | `^[text]` |
### Math
| LaTeX | Quarto |
|-------|--------|
| `\(x + y\)` | `$x + y$` |
| `\[x + y\]` | `$$x + y$$` |
| `\begin{equation}` / `\end{equation}` | `$$` / `$$` with `{#eq-name}` after closing `$$` |
| `\begin{align}` / `\end{align}` | `$$\begin{aligned}` / `\end{aligned}$$` |
| `\label{eq:name}` inside math | Remove from math body; add `{#eq-name}` after closing `$$` |
### Hyperlinks
| LaTeX | Quarto |
|-------|--------|
| `\url{https://...}` | `<https://...>` |
| `\href{url}{text}` | `[text](url)` |
### Special Characters and Spacing
| LaTeX | Quarto | Notes |
|-------|--------|-------|
| `\&` | `&` | Outside math |
| `\%` | `%` | Outside math |
| `\ldots` or `\dots` | `...` | |
| `\footnote{text}` | `^[text]` | (also in Text Formatting above) |
| `\noindent` | *(remove)* | |
| `\par` | blank line | |
| `\newline` or `\\` | blank line or two trailing spaces | Paragraph break in prose |
| `\newpage` or `\clearpage` | `{{< pagebreak >}}` | |
| `~` (tie space) | ` ` or ` ` | Context-dependent |
### Section Headings
| LaTeX | Quarto |
|-------|--------|
| `\section{Title}` | `# Title` |
| `\subsection{Title}` | `## Title` |
| `\subsubsection{Title}` | `### Title` |
| `\paragraph{Title}` | `#### Title` |
| `\section*{Title}` | `# Title {.unnumbered}` |
| `\section{Title}\label{sec:x}` | `# Title {#sec-x}` |
If a `\label{sec:x}` appears on the line following a heading, merge it into the heading's
attribute block: `## Methods {#sec-methods}`.
### List Environments
| LaTeX | Quarto |
|-------|--------|
| `\begin{itemize}` / `\item` | Unordered list with `-` |
| `\begin{enumerate}` / `\item` | Ordered list with `1.` |
| `\begin{description}` / `\item[Term]` | `: Term\n: definition` |
Nested lists: indent continuation lines by two spaces per level.
### Abstract and Front Matter
Move these from the document body to YAML:
```latex
\begin{abstract}
We study the effect of X on Y...
\end{abstract}
```
→ YAML field:
```yaml
abstract: |
We study the effect of X on Y...
```
Similarly for `\title{}`, `\author{}`, `\date{}` appearing in the body — move to YAML
`title:`, `author:`, `date:` fields.
---
## Patterns That Need Judgment
Flag these in your report if you encounter them, with a suggested fix but no automatic
conversion (the content depends too much on context):
- **Custom LaTeX environments** (`\begin{theorem}`, `\begin{proof}`, `\begin{lemma}`,
`\begin{frame}`, etc.) → suggest converting to `::: {.callout-note}` or a theorem div
- **`\vspace{}` / `\hspace{}`** → no direct Quarto equivalent; suggest removing or using CSS
- **`\multicolumn{}{}{}` in tables** → requires restructuring the table manually
- **`\citeauthor{key}`** → converted to `@key` but the "author only" rendering is lost;
suggest rephrasing (e.g., "Smith [-@smith2020]")
- **`cite-method: natbib` or `cite-method: biblatex` in YAML** → should likely be removed
for non-LaTeX output formats; flag for user decision
- **`\\[6pt]` or `\\[1em]`** (spacing in math display) → the spacing argument is not valid
in Quarto; convert to `\\` and note the spacing is lost
---
## What to Preserve Unchanged
- All content inside ` ```...``` ` fenced code blocks (including chunk body code)
- All content inside `` `...` `` inline code
- All content inside `$...$` inline math and `$$...$$` display math (except `\label{}` which
must be moved outside, and `\( \)` / `\[ \]` delimiters which must be converted)
- YAML front matter between `---` delimiters
- Quarto shortcodes `{{< ... >}}`
- Existing `@ref` cross-references and `[@citation]` citations that are already correct
- Pandoc div/span syntax `:::` and `[]{}` that is already valid Quarto
No comments yet. Be the first to comment!