mq is a command-line tool that processes Markdown using a syntax similar to jq. It’s written in Rust, allowing you to easily slice, filter, map, and transform structured data.

Why mq?

mq makes working with Markdown files as easy as jq makes working with JSON. It’s especially useful for:

  • LLM Workflows: Efficiently manipulate and process Markdown used in LLM prompts and outputs
  • Documentation Management: Extract, transform, and organize content across multiple documentation files
  • Content Analysis: Quickly extract specific sections or patterns from Markdown documents
  • Batch Processing: Apply consistent transformations across multiple Markdown files

Features

  • Slice and Filter: Extract specific parts of your Markdown documents with ease.
  • Map and Transform: Apply transformations to your Markdown content.
  • Command-line Interface: Simple and intuitive CLI for quick operations.
  • Extensibility: Easily extendable with custom functions.
  • Built-in support: Filter and transform content with many built-in functions and selectors.
  • REPL Support: Interactive command-line REPL for testing and experimenting.
  • IDE Support: VSCode Extension and Language Server Protocol (LSP) support for custom function development.

This section guides you through the installation of mq.

Install

To install mq, you can use cargo:

cargo install --git https://github.com/harehare/mq.git mq-cli
# Installing from cargo is under preparation.
cargo install mq-cli

Docker

$ docker run --rm ghcr.io/harehare/mq:0.1.0-preview

Visual Studio Code Extension

You can install the VSCode extension from the Visual Studio Marketplace.

Playground

An Online Playground is available, powered by WebAssembly.

Example

under preparation

Hello world

# Hello world
select(or(.[], .code, .h)) | upcase() | add(" Hello World")

Markdown TOC

.h
| let link = to_link(add("#", to_text(self)), to_text(self), "")
| if (is_h1()):
  to_md_list(link, 1)
elif (is_h2()):
  to_md_list(link, 2)
elif (is_h3()):
  to_md_list(link, 3)
elif (is_h4()):
  to_md_list(link, 4)
elif (is_h5()):
  to_md_list(link, 5)
else:
  None

Exclude code

select(not(.code))

Extract js code

.code("js")

Extract table

.[1][]

Extract list

.[1]

Extract MDX

select(is_mdx())

Custom function

def snake_to_camel(x):
  let words = split(x, "_")
  | foreach (word, words):
      let first_char = upcase(first(word))
      | let rest_str = downcase(slice(word, 1, len(word)))
      | add(first_char, rest_str);
  | join("");
| snake_to_camel()

Generate sitemap

def sitemap(item, base_url):
  let path = replace(to_text(item), ".md", ".html")
  | let loc = add(base_url, path)
  | s"<url>
  <loc>${loc}</loc>
  <priority>1.0</priority>
</url>";

This is a reference documentation for the mq.

CLI

The mq command-line interface provides tools for querying and manipulating markdown content. Below is the complete reference for all available commands and options.

Usage: mq [OPTIONS] [QUERY OR FILE] [FILES]... [COMMAND]

Commands:
  repl        Start a REPL session for interactive query execution
  fmt         Format mq or markdown files based on specified formatting options
  completion  Generate shell completion scripts for supported shells
  docs        Show functions documentation for the query
  help        Print this message or the help of the given subcommand(s)

Arguments:
  [QUERY OR FILE]  
  [FILES]...       

Options:
  -f, --from-file                       load filter from the file
  -R, --raw-input                       Reads each line as a string
  -n, --null-input                      Use empty string as the single input value
  -L, --directory <MODULE_DIRECTORIES>  Search modules from the directory
  -M, --module-names <MODULE_NAMES>     Load additional modules from specified files
      --args <NAME> <VALUE>             Sets string that can be referenced at runtime
      --rawfile <NAME> <FILE>           Sets file contents that can be referenced at runtime
      --mdx                             Enable MDX parsing
  -c, --compact-output                  pretty print
  -F, --output-format <OUTPUT_FORMAT>   Compact instead of pretty-printed output [default: markdown] [possible values: markdown, html, text]
  -U, --update                          Update the input markdown
      --unbuffered                      Unbuffered output
      --list-style <LIST_STYLE>         Set the list style for markdown output [default: dash] [possible values: dash, plus, star]
  -o, --output <FILE>                   Output to the specified file
  -v, --verbose...                      Increase logging verbosity
  -q, --quiet...                        Decrease logging verbosity
  -h, --help                            Print help
  -V, --version                         Print version

Examples:

To filter markdown nodes:
$ mq 'query' file.md

To read query from file:
$ mq -f 'file' file.md

To start a REPL session:
$ mq repl

To format mq file:
$ mq fmt --check file.mq

Types and Values

Values

  • 42 (a number)
  • "Hello, world!" (a string)
  • array(1, 2, 3) (an array)
  • true, false (a boolean)
  • None

Types

TypeDescriptionExamples
NumberRepresents numeric values.1, 3.14, -42
StringRepresents sequences of characters, including Unicode code points and escape sequences in the form of \{0x000}."hello", "123", "😊", "\u{1F600}"
BooleanRepresents truth values.true, false
ArrayRepresents ordered collections of values.array(1, 2, 3)
FunctionRepresents executable code.def foo(): 42; let name = def foo(): 42;

Environment Variables

A module handling environment-specific functionality.

  • __FILE__: Contains the path to the file currently being processed.

Conditional expressions and comparison operators in mq allow for decision-making based on the evaluation of conditions, enabling dynamic behavior in your queries.

Conditionals

mq supports standard conditional operations through the following functions:

  • and(a, b) - Returns true if both a and b are true
  • or(a, b) - Returns true if either a or b is true
  • not(a) - Returns true if a is false

Examples

# Basic comparisons
and(true, true, true)
# => true
or(true, false, true)
# => true
not(false)
# => true

Comparisons

mq provides comparison functionality through built-in functions.

Basic Comparisons

Standard comparison operators are supported:

  • eq(a, b) - Returns true if a equals b
  • ne(a, b) - Returns true if a does not equal b
  • gt(a, b) - Returns true if a is greater than b
  • gte(a, b) - Returns true if a is greater than or equal to b
  • lt(a, b) - Returns true if a is less than b
  • lte(a, b) - Returns true if a is less than or equal to b

Examples

# Basic comparisons
eq(1, 1)
# => true
gt(2, 1)
# => true
lte("a", "b")
# => true

# String comparisons
eq("hello", "hello")
# => true
gt("xyz", "abc")
# => true

# Numeric comparisons
gte(5.5, 5.0)
# => true
lt(-1, 0)
# => true

# Logical operations
and(true, false)
# => false
or(true, false)
# => true
not(false)
# => true

# Complex conditions
and(gt(x, 0), lt(x, 10))
# =>  true if 0 < x < 10

This section outlines the syntax rules in mq, providing a clear reference for writing valid code.

Pipe Operator

A functional operator that allows chaining multiple filter operations together.

Usage

The pipe operator (|) enables sequential processing of filters, where the output of one filter becomes the input of the next filter.

Examples

# Basic pipe usage
42 | add(1) | mul(2)
# => 86

# Multiple transformations
let mul2 = def mul2(x): mul(x, 2);
let gt4 = def gt4(x): gt(x, 4);
array(1, 2, 3) | map(mul2) | filter(gt4)
# => [6]

# Function composition
let double = def _double(x): mul(x, 2);
let add_one = def _add_one(x): add(x, 1);
5 | double(self) | add_one(self)
# => 11

? Operator

The ? operator is a safe navigation operator that provides null-safe operations.

Usage

When applied to a None value, the ? operator prevents errors by returning None instead of raising an exception.

Examples

# Safe access with ? operator
let x = None | x | add?(1)
# => None

# Chaining with ? operator
None | add?(1) | mul?(2)
# => None

# Normal operation when value exists
42 | add?(1)
# => 43

Environment variables

Environment variables can be referenced using $XXX syntax, where XXX represents the name of the environment variable. For example:

  • $PATH - References the PATH environment variable
  • $HOME - References the HOME environment variable
  • $USER - References the current user’s username

This syntax is commonly used in shell scripts and configuration files to access system-level environment variables.

Def Expression

The def expression defines reusable functions with parameters:

Examples

# Function that doubles input
def double(x):
  mul(x, 2);

# Function with conditional logic
def is_positive(x):
  gt(x, 0);

# Composition of functions
def add_then_double(x, y):
  add(x, y) | double(self);

Let Expression

The let expression binds a value to an identifier for later use:

# Binds 42 to x
let x = 42
# Uses x in an expression
let y = add(x, 1)
# Binds `add` function to z
let z = def _add(x): add(x, 1); | z(1)

If Expression

The if expression evaluates a condition and executes code based on the result:

 if (eq(x, 1)):
   "one"
 elif (eq(x, 2)):
   "two"
 else:
   "other"

The if expression can be nested and chained with elif and else clauses. The conditions must evaluate to boolean values.

While Expression

The while loop repeatedly executes code while a condition is true:

let i = 0 |
while (lt(i, 3)):
  let i = add(i, 1) | i;
# => [1, 2, 3]

The while loop in this context returns an array containing all elements processed during the iteration. As the loop executes, it collects each processed value into an array, which is then returned as the final result once the loop condition becomes false.

Key points:

  • Creates a new array from loop iterations
  • Each loop cycle’s result is added to the array
  • Returns the complete array after all iterations
  • Similar to map/collect functionality but with while loop control

Until Expression

The until loop repeatedly executes code until a condition becomes true:

let x = 5 |
until(gt(x, 0)):
  let x = sub(x, 1) | x;
# => 0

Until loops are similar to while loops but continue until the condition becomes true instead of while the condition remains true.

Foreach Expression

The foreach loop iterates over elements in an array:

let items = array(1, 2, 3) |
foreach (x, items):
   sub(x, 1);
# => array(0, 1, 2)

Foreach loops are useful for:

  • Processing arrays element by element
  • Mapping operations across collections
  • Filtering and transforming data

Comments

Similar to jq, comments starting with # are doc-comments.

# doc-comment
let value = add(2, 3);

Include

Loads functions from an external file using the syntax include "module_name". The include directive searches for .mq files in the following locations:

  • $HOME/.mq - User’s home directory mq folder
  • $ORIGIN/../lib/mq - Library directory relative to the source file
  • $ORIGIN/../lib - Parent lib directory relative to the source file
include "module_name"

Examples

# Include math functions from math.mq
include "math"
# Now we can use functions defined in math.mq
let result = add(2, 3)

Self

The current value being processed can be referenced as self. When there are insufficient arguments provided in a method call, the current value (self) is automatically passed as the first argument.

Examples

# These expressions are equivalent
"hello" | upcase()
"hello" | upcase(self)

String Interpolation

String Interpolation allow embedding expressions directly inside string literals. In mq, an interpolated string is prefixed with s" and variables can be embedded using ${} syntax.

Syntax

s"text ${ident} more text"

Examples

let name = "Alice"
| let age = 30
| s"Hello, my name is ${name} and I am ${age} years old."
# => Output: "Hello, my name is Alice and I am 30 years old."

This page provides an introduction to the built-in selectors and functions available in mq. These are predefined components that you can use in your queries without having to define them yourself.

Builtin functions

Function NameDescriptionParametersExample
absReturns the absolute value of the given number.numberabs(number)
addAdds two values.value1, value2add(value1, value2)
andPerforms a logical AND operation on two boolean values.value1, value2and(value1, value2)
arrayCreates an array from the given values.valuesarray(values)
arraysReturns array if input is array, None otherwiseaarrays(a)
assertVerifies that a condition is true and raises an error if it’s false.condassert(cond)
assertAsserts that two values are equal, returns the value if true, otherwise raises an error.value1, value2assert(value1, value2)
base64Encodes the given string to base64.inputbase64(input)
base64dDecodes the given base64 string.inputbase64d(input)
booleansReturns boolean if input is boolean, None otherwisebbooleans(b)
ceilRounds the given number up to the nearest integer.numberceil(number)
compactRemoves None values from the given array.arraycompact(array)
containsChecks if string contains a substringhaystack, needlecontains(haystack, needle)
csv2tableConvert csv string to markdown tablecsvcsv2table(csv)
debugPrints the debug information of the given value.valuedebug(value)
delDeletes the element at the specified index in the array or string.array_or_string, indexdel(array_or_string, index)
divDivides the first value by the second value.value1, value2div(value1, value2)
downcaseConverts the given string to lowercase.inputdowncase(input)
ends_withChecks if the given string ends with the specified substring.string, substringends_with(string, substring)
eqChecks if two values are equal.value1, value2eq(value1, value2)
errorRaises a user-defined error with the specified message.messageerror(message)
explodeSplits the given string into an array of characters.stringexplode(string)
filterFilters the elements of an array based on a provided callback function.v, ffilter(v, f)
firstReturns the first element of an arrayarrfirst(arr)
floorRounds the given number down to the nearest integer.numberfloor(number)
from_dateConverts a date string to a timestamp.date_strfrom_date(date_str)
get_md_list_levelReturns the indent level of a markdown list node.listget_md_list_level(list)
get_titleReturns the title of a markdown node.nodeget_title(node)
gsubReplaces all occurrences matching a regular expression pattern with the replacement string.pattern, from, togsub(pattern, from, to)
gtChecks if the first value is greater than the second value.value1, value2gt(value1, value2)
gteChecks if the first value is greater than or equal to the second value.value1, value2gte(value1, value2)
haltTerminates the program with the given exit code.exit_codehalt(exit_code)
halt_errorHalts execution with error code 5halt_error()
implodeJoins an array of characters into a string.arrayimplode(array)
indexFinds the first occurrence of a substring in the given string.string, substringindex(string, substring)
is_arrayChecks if input is an arrayais_array(a)
is_boolChecks if input is a booleanbis_bool(b)
is_codeChecks if markdown is code blockmdis_code(md)
is_emChecks if markdown is emphasismdis_em(md)
is_emptyChecks if string or array is emptysis_empty(s)
is_hChecks if markdown is headingmdis_h(md)
is_h1Checks if markdown is h1 headingmdis_h1(md)
is_h2Checks if markdown is h2 headingmdis_h2(md)
is_h3Checks if markdown is h3 headingmdis_h3(md)
is_h4Checks if markdown is h4 headingmdis_h4(md)
is_h5Checks if markdown is h5 headingmdis_h5(md)
is_htmlChecks if markdown is htmlmdis_html(md)
is_listChecks if markdown is listlistis_list(list)
is_list1Checks if markdown is list with indentation level 1listis_list1(list)
is_list2Checks if markdown is list with indentation level 2listis_list2(list)
is_list3Checks if markdown is list with indentation level 3listis_list3(list)
is_markdownChecks if input is markdownmis_markdown(m)
is_mdxChecks if markdown is MDXmdxis_mdx(mdx)
is_mdx_flow_expressionChecks if markdown is MDX Flow Expressionmdxis_mdx_flow_expression(mdx)
is_mdx_js_esmChecks if markdown is MDX Js Esmmdxis_mdx_js_esm(mdx)
is_mdx_jsx_flow_elementChecks if markdown is MDX Jsx Flow Elementmdxis_mdx_jsx_flow_element(mdx)
is_mdx_jsx_text_elementChecks if markdown is MDX Jsx Text Elementmdxis_mdx_jsx_text_element(mdx)
is_mdx_text_expressionChecks if markdown is MDX Text Expressionmdxis_mdx_text_expression(mdx)
is_noneChecks if input is Nonenis_none(n)
is_numberChecks if input is a numbernis_number(n)
is_stringChecks if input is a stringsis_string(s)
is_textChecks if markdown is texttextis_text(text)
is_tomlChecks if markdown is tomlmdis_toml(md)
is_yamlChecks if markdown is yamlmdis_yaml(md)
joinJoins the elements of an array into a string with the given separator.array, separatorjoin(array, separator)
lastReturns the last element of an arrayarrlast(arr)
lenReturns the length of the given string or array.valuelen(value)
ltChecks if the first value is less than the second value.value1, value2lt(value1, value2)
lteChecks if the first value is less than or equal to the second value.value1, value2lte(value1, value2)
ltrimstrRemoves prefix string from input if it existss, leftltrimstr(s, left)
mapApplies a given function to each element of the provided array and returns a new array with the results.v, fmap(v, f)
markdownsReturns markdown if input is markdown, None otherwisemmarkdowns(m)
matchFinds all matches of the given pattern in the string.string, patternmatch(string, pattern)
maxReturns the maximum of two values.value1, value2max(value1, value2)
minReturns the minimum of two values.value1, value2min(value1, value2)
modCalculates the remainder of the division of the first value by the second value.value1, value2mod(value1, value2)
mulMultiplies two values.value1, value2mul(value1, value2)
neChecks if two values are not equal.value1, value2ne(value1, value2)
notPerforms a logical NOT operation on a boolean value.valuenot(value)
nowReturns the current timestamp.now()
nthGets the element at the specified index in the array or string.array_or_string, indexnth(array_or_string, index)
numbersReturns number if input is number, None otherwisennumbers(n)
orPerforms a logical OR operation on two boolean values.value1, value2or(value1, value2)
powRaises the base to the power of the exponent.base, exponentpow(base, exponent)
rangeCreates an array of numbers within the specified range.start, endrange(start, end)
repeatRepeats the given string a specified number of times.string, countrepeat(string, count)
replaceReplaces all occurrences of a substring with another substring.string, from, toreplace(string, from, to)
reverseReverses the given string or array.valuereverse(value)
rindexFinds the last occurrence of a substring in the given string.string, substringrindex(string, substring)
roundRounds the given number to the nearest integer.numberround(number)
rtrimstrRemoves suffix string from input if it existss, rightrtrimstr(s, right)
selectReturns value if condition is true, None otherwisev, fselect(v, f)
set_md_checkCreates a markdown list node with the given checked state.list, checkedset_md_check(list, checked)
sliceExtracts a substring from the given string.string, start, endslice(string, start, end)
sortSorts the elements of the given array.arraysort(array)
splitSplits the given string by the specified separator.string, separatorsplit(string, separator)
starts_withChecks if the given string starts with the specified substring.string, substringstarts_with(string, substring)
subSubtracts the second value from the first value.value1, value2sub(value1, value2)
testTests if string matches a patterns, patterntest(s, pattern)
to_arrayConverts input to an arrayato_array(a)
to_codeCreates a markdown code block with the given value and language.value, languageto_code(value, language)
to_code_inlineCreates an inline markdown code node with the given value.valueto_code_inline(value)
to_csvConverts the given value to a CSV.valueto_csv(value)
to_dateConverts a timestamp to a date string with the given format.timestamp, formatto_date(timestamp, format)
to_date_iso8601Formats a date to ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)dto_date_iso8601(d)
to_emCreates a markdown emphasis (italic) node with the given value.valueto_em(value)
to_hCreates a markdown heading node with the given value and depth.value, depthto_h(value, depth)
to_hrCreates a markdown horizontal rule node.to_hr()
to_htmlConverts the given markdown string to HTML.markdownto_html(markdown)
to_imageCreates a markdown image node with the given URL, alt text, and title.url, alt, titleto_image(url, alt, title)
to_linkCreates a markdown link node with the given url and title.url, value, titleto_link(url, value, title)
to_mathCreates a markdown math block with the given value.valueto_math(value)
to_math_inlineCreates an inline markdown math node with the given value.valueto_math_inline(value)
to_md_listCreates a markdown list node with the given value and indent level.value, indentto_md_list(value, indent)
to_md_nameReturns the name of the given markdown node.markdownto_md_name(markdown)
to_md_table_rowCreates a markdown table row node with the given values.cellsto_md_table_row(cells)
to_md_textCreates a markdown text node with the given value.valueto_md_text(value)
to_numberConverts the given value to a number.valueto_number(value)
to_stringConverts the given value to a string.valueto_string(value)
to_strongCreates a markdown strong (bold) node with the given value.valueto_strong(value)
to_textConverts the given markdown node to plain text.markdownto_text(markdown)
to_tsvConverts the given value to a TSV.valueto_tsv(value)
trimTrims whitespace from both ends of the given string.inputtrim(input)
truncTruncates the given number to an integer by removing the fractional part.numbertrunc(number)
tsv2tableConvert tsv string to markdown tabletsvtsv2table(tsv)
typeReturns the type of the given value.valuetype(value)
uniqRemoves duplicate elements from the given array.arrayuniq(array)
upcaseConverts the given string to uppercase.inputupcase(input)
updateUpdate the value with specified value.target_value, source_valueupdate(target_value, source_value)
url_encodeURL-encodes the given string.inputurl_encode(input)

Builtin selectors

Selector NameDescriptionParametersExample
.h, .h(depth)Selects a heading node with the specified depth.None, depth.h, .h(6)
.h1Selects a heading node with the 1 depth.None.h1
.h2Selects a heading node with the 2 depth.None.h2
.h3Selects a heading node with the 3 depth.None.h3
.h4Selects a heading node with the 4 depth.None.h4
.h5Selects a heading node with the 5 depth.None.h5
.#Selects a heading node with the 1 depth.None.#
.##Selects a heading node with the 2 depth.None.##
.###Selects a heading node with the 3 depth.None.###
.####Selects a heading node with the 4 depth.None.####
.#####Selects a heading node with the 5 depth.None.#####
.codeSelects a code block node with the specified language.lang.code "rust"
.code_inlineSelects an inline code node.None.code_inline
.inline_mathSelects an inline math node.None.inline_math
.strongSelects a strong (bold) node.None.strong
.emphasisSelects an emphasis (italic) node.None.emphasis
.deleteSelects a delete (strikethrough) node.None.delete
.linkSelects a link node.None.link
.link_refSelects a link reference node.None.link_ref
.imageSelects an image node.None.image
.headingSelects a heading node with the specified depth.None.heading 1
.horizontal_ruleSelects a horizontal rule node.None.horizontal_rule
.blockquoteSelects a blockquote node.None.blockquote
.[][]Selects a table cell node with the specified row and column.row, column.[1][1]
.html ,.<>Selects an HTML node.None.html, .<>
.footnoteSelects a footnote node.None.footnote
.mdx_jsx_flow_elementSelects an MDX JSX flow element node.None.mdx_jsx_flow_element
.list,.[]Selects a list node with the specified index and checked state.indent.list(1), .[1]
.mdx_js_esmSelects an MDX JS ESM node.None.mdx_js_esm
.tomlSelects a TOML node.None.toml
.text Selects a Text node.None.text
.yamlSelects a YAML node.None.yaml
.breakSelects a break node.None.break
.mdx_text_expressionSelects an MDX text expression node.None.mdx_text_expression
.footnote_refSelects a footnote reference node.None.footnote_ref
.image_refSelects an image reference node.None.image_ref
.mdx_jsx_text_elementSelects an MDX JSX text element node.None.mdx_jsx_text_element
.mathSelects a math node.None.math
.math_inlineSelects a math inline node.None.math_inline
.mdx_flow_expressionSelects an MDX flow expression node.None.mdx_flow_expression
.definitionSelects a definition node.None.definition