Macros
Macros are named, reusable sequences of queries — the same prompts and slash
commands you’d otherwise type by hand — that you define in your configuration or
profile and invoke from the prompt with the ! sigil:
!explain_folder path=./src/When a macro runs, its queries: execute in order, each one treated exactly as
if you had typed it yourself. That includes slash commands (/toolset load ...,
/session push ...) and calls to other macros: a nested macro call is expanded
at its call site and executed in sequence, and when the inner macro finishes (or
breaks out early), Enkaidu resumes the calling macro where it left off.
Where macros live
A macro can be defined in either place:
- Your main config file, under a top-level
macros:section (see Configuration). - Your profile folder
.enkaidu/: either amacros.yaml/macros.ymlfile, or any YAML file inside themacros/folder (files in sub-folders count too).
When a macro name exists in both places, the config definition wins, and
each profile macro with that name is superseded. /macro ls lists every macro
you have, along with its origin (Config or Profile) and description.
Every macro definition is a map with just two properties:
description:— a description of the macro (required).queries:— an array of queries, executed in order when the macro is invoked.
compact:
description: Compact the current session with a summary
queries:
- /session push system_prompt_name=compact
- /prompt use compact
- /session pop_and_take response_only=yes reset_parent=yesParameters
Macros can take positional and named parameters. In a macro’s queries, patterns
of the form %{WORD} are replaced with arguments collected at the call site at
invocation time — before any query executes.
- Positional parameters are
%{N}whereNis a positive integer — but note the macro call itself counts as argument0, so your first argument is%{1}. Positional arguments must precede named ones at the call site. - Named parameters are
%{WORD}patterns, filled fromword=valuepairs at the call site.
macros:
explain_folder:
description: Explain the contents of a folder
queries:
- |
Explain the purpose and structure
of the code in %{path}This macro is invoked like !explain_folder path=./src/, with %{path} replaced
by ./src/.
A macro call is aborted (with a warning naming the missing parameter) if any required positional or named argument is not supplied.
To include text that merely looks like a parameter pattern without it being
interpolated, double the percent: %%{not_a_param} stays literal.
Branching inside macros
Macros run their queries strictly in order, so out of the box they can’t react to
what happened mid-run. That’s what conditional commands are for: a ?break if... /
?break unless... line terminates the current (innermost) macro, skipping its
remaining queries.
Consider an example where the guard is the macro.
add_dependency:
description: |
Add a dependency to requirements.txt, skipping the addition if the
file already lists it.
- Usage: `!add_dependency httpx`
queries:
- ?break if file=requirements.txt contains=%{1}
- Add `%{1}`, pinned to its latest stable version, to requirements.txt.Here ?break sits at the macro’s top level: if the dependency is already
listed, firing the guard terminates the whole macro and the second query never runs.
All the forms of conditional commands are documented in Conditional Commands.
Blocks New in 0.9.11 Experimental
Query entries can also be blocks — nested groups of
queries keyed on <enter> — which ?break exits early while execution
continues after the block.
The
<enter>block-entry syntax, and whether block-entry commands will eventually support parameters, may still change.
One motivation for blocks and breaks is the idempotent macro: one you can safely run again tomorrow without redoing the setup.
changelog_entry:
description: |
Add a bullet under "Unreleased" in CHANGELOG.md, creating the file
first only when it doesn't exist yet.
- Usage: `!changelog_entry "Added block-aware macros"`
queries:
- <enter>:
- ?break if file_exists=CHANGELOG.md
- |
Create a CHANGELOG.md in the project root with a "# Changelog"
heading and an empty "## [Unreleased]" section.
- |
Add this entry as a bullet under the "Unreleased" section of
CHANGELOG.md: %{1}Read it the way you’d run it. On the first run there’s no CHANGELOG.md,
the guard doesn’t fire, so the block scaffolds the file and the block ends
naturally; execution continues and the entry gets appended. On every run
after, the guard fires immediately, the block’s creation query is skipped,
and execution resumes at the append. Without the break, the “create the file”
query would clobber an existing changelog on every run.
At the prompt
- Type
!and pressTabto see all available macros (auto-completion works on macro names as you keep typing). - While you type, Enkaidu highlights macro calls so you can see that a query will be treated as a macro invocation.