Skip to content

Plugins

Brett Terpstra edited this page Mar 19, 2026 · 8 revisions

Plugins

Apex supports a lightweight plugin system that lets you add new syntax and post‑processing behavior without patching the core. Plugins can be small scripts (Ruby, Python, etc.) or simple declarative regex rules defined in YAML.

This page covers:

  • Where plugins live
  • How to enable/disable plugins
  • Processing phases
  • Plugin manifest format (plugin.yml)
  • External handler plugins (scripts/commands)
  • Declarative regex plugins (pure YAML)
  • Plugin bundles (bundle key)
  • Support directory and environment variables
  • Plugin metadata for directories and installers
  • Installing plugins from the directory or a Git URL
  • Examples: kbd liquid tag and :emoji: spans

If you are using plugins from Swift/Xcode, see Xcode Integration for guidance on when to use high-level ApexOptions vs low-level apex_options and required imports (Apex + ApexC) for direct C struct field access.


Enabling and disabling plugins

By design, plugins are disabled by default so Apex’s performance and behavior are unchanged unless you explicitly opt in.

  • CLI flags

    • Enable plugins: apex --plugins input.md
    • Disable plugins: apex --no-plugins input.md
  • Metadata keys (in the document’s front matter)

    • Any of these keys are recognized (case-insensitive):

      • plugins
      • enable-plugins
      • enable_plugins
    • Example:

      ---
      title: Plugin demo
      plugins: true
      ---
  • Precedence

    • If metadata enables or disables plugins, you can still override it from the CLI:
      • --plugins forces plugins on
      • --no-plugins forces plugins off
    • CLI flags always win over metadata.

If you never set plugins: true or pass --plugins, Apex will not load or run any plugins.


Where plugins live

Plugins are discovered from project and user locations, in this order:

  • Project-local plugins (current directory)

    • Directory: .apex/plugins/ under the current working directory.
    • Structure: one subdirectory per plugin, for example:
      • .apex/plugins/kbd/plugin.yml
      • .apex/plugins/kbd/kbd_plugin.rb
      • .apex/plugins/emoji/plugin.yml
  • Project-local plugins (base directory)

    • If Apex knows a base_directory (from --base-dir, metadata, or the input file’s directory), it also looks in:
      • <base_directory>/.apex/plugins/
  • Project-local plugins (Git repository root)

    • If the current directory is inside a Git repository and git rev-parse --show-toplevel succeeds, Apex will also look in:
      • <git-top-level>/.apex/plugins/
    • This is only used when the Git top-level is a parent of (or equal to) the current directory.
  • Global (user) plugins

    • Directory (XDG-aware):
      • If $XDG_CONFIG_HOME is set:
        • $XDG_CONFIG_HOME/apex/plugins/
      • Otherwise:
        • ~/.config/apex/plugins/
    • Same structure: one subdirectory per plugin:
      • ~/.config/apex/plugins/kbd/plugin.yml
      • ~/.config/apex/plugins/emoji/plugin.yml

Plugin IDs must be unique. If a project plugin and a global plugin share the same id, the project plugin wins and the global one is ignored.


Disabling a global plugin within a project (shadow/no-op)

Sometimes you may want to disable a plugin that is installed globally, but only for a specific project. Apex’s lookup order makes this easy: you can shadow a global plugin with a no-op project plugin that has the same id.

Key points:

  • Project plugins are loaded before global plugins.
  • If two plugins share the same id, the first one wins and the later one is ignored.
  • A plugin that has no handler and no regex is a true no-op: it is loaded, but never changes the text.

To disable a global plugin called emoji-span just for one project:

  1. Create a project plugins directory:

    .apex/
      plugins/
        emoji-span/
          plugin.yml
    
  2. Add a minimal no-op manifest that declares the same id and a phase, but no handler or pattern:

    ---
    id: emoji-span
    phase: post_render   # or pre_parse, depending on the original plugin
    # No handler.command, pattern, or replacement on purpose:
    # this plugin will be loaded, but it does nothing.
    ---

When you run Apex with plugins enabled in that project:

  • The project-local emoji-span plugin is discovered first.
  • Because a plugin with id emoji-span already exists, the global emoji-span plugin is skipped.
  • Since the project version has no handler or regex, it is effectively a no-op, which means the global plugin is disabled for this project only.

Processing phases

Apex exposes several phases in its pipeline. Plugins can hook into one or more phases; for now, two are wired up:

  • pre_parse

    • Runs on the raw Markdown text before it is parsed.
    • Good for:
      • Custom syntax (e.g. {% kbd ... %})
      • Textual rewrites
      • Adding/removing markup before Apex sees it
  • post_render

    • Runs on the final HTML output after Apex finishes rendering.
    • Good for:
      • Wrapping elements in spans/divs
      • Adding CSS classes
      • Simple HTML post-processing (e.g. turning :emoji: into <span>)

Internally, plugins for each phase are run in a deterministic order:

  1. Sorted by priority (lower numbers first; default is 100).
  2. Ties broken by plugin id (lexicographically).

Plugin manifest: plugin.yml

Each plugin is defined by a manifest file:

  • File name: plugin.yml

  • Location: inside the plugin’s directory, e.g.:

    .apex/plugins/kbd/plugin.yml
    ~/.config/apex/plugins/emoji/plugin.yml
    

At minimum, a plugin needs:

---
id: my-plugin
# Optional, but strongly recommended metadata:
# title: Human-friendly name (if omitted, tools may fall back to id)
# author: Name or handle of the plugin author
# description: Short, human-readable description of what the plugin does
# homepage: URL for the plugin landing page or README
# repo: Canonical Git repository URL used for installation/updates
phase: pre_parse           # or post_render
priority: 100              # optional, lower runs earlier
timeout_ms: 0              # optional, 0 = no extra timeout logic (best-effort)
---

From there, you choose one of two plugin types:

  • External handler plugin
    • Runs an external command (Ruby, Python, shell, etc.).
    • Declared with a handler.command field.
  • Declarative regex plugin
    • No external code; in-process regex search/replace.
    • Declared with pattern and replacement fields.

You can’t mix both styles in a single plugin; if handler.command is present, the plugin is treated as external.

Metadata fields for directories and installers

To support plugin directories, automatic installation, and future auto-update tools, Apex understands several optional metadata fields in plugin.yml:

  • title: Short, human-friendly name for the plugin (e.g. Keyboard Shortcuts).
  • author: Free-form author string (your name, handle, or organization).
  • description: One–two sentence description of what the plugin does. This is already used in examples and is what directory listings will usually display.
  • homepage: Informational URL where users can learn more about the plugin—often a README, documentation site, or the GitHub project page. This field is not used for cloning, but may be shown in --list-plugins output.
  • repo: Canonical Git URL for the plugin repository, used by Apex when installing plugins from the central directory (for example, https://github.com/ApexMarkdown/apex-kbd-plugin.git). The repository must contain the plugin.yml manifest (and any supporting files) at its root.
  • post_install: Optional command that Apex will run after cloning the plugin during --install-plugin. See “Post-install hooks” below.

Only id, phase, and either handler.command (for external plugins) or pattern/replacement (for declarative plugins) are required for execution—everything else is optional metadata used by tools.

Post-install hooks (post_install)

External plugins can define an optional post_install command in plugin.yml that Apex will run immediately after cloning the plugin during --install-plugin:

---
id: kbd
# ... other fields ...
post_install: ./post_install.sh
---

When you run:

apex --install-plugin kbd

Apex will:

  1. Clone the plugin repository into your user plugin directory (e.g. ~/.config/apex/plugins/kbd).
  2. Look for plugin.yml (or plugin.yaml) in the root of that directory.
  3. If it finds a post_install key, run the value as a shell command from the plugin directory (equivalent to cd <plugin_dir> && <post_install>).

This hook is useful for:

  • Creating initial configuration files or support data under APEX_SUPPORT_DIR.
  • Printing a welcome or usage message via echo.
  • Running an interactive setup script that asks the user questions and writes config.

If the post_install command exits with a non-zero status, Apex prints a warning but considers the plugin installed successfully (the clone is not rolled back).


Plugin bundles (bundle key)

Sometimes it is convenient for a single repository to provide multiple related plugins as a bundle—for example, a “documentation” bundle that contains:

  • kbd – expands {% kbd @3 %} keyboard shortcuts
  • prefpane – expands {% prefspane Advanced, Processor %}
  • menubar – expands {% menubar File, Open %}

Instead of creating a separate repo (and plugin.yml) for each, Apex supports a bundle syntax in plugin.yml when built with full YAML (libyaml) support.

Bundle structure

A bundle manifest has:

  • Top-level metadata that applies to the bundle as a whole.
  • A bundle: key whose value is a YAML sequence (array) of per-plugin configs.

Example:

---
id: documentation
title: Documentation helpers
author: Brett Terpstra
description: A bundle of documentation-related helpers (kbd, menubar, prefpane).
homepage: https://github.com/ApexMarkdown/apex-plugin-documentation
repo: https://github.com/ApexMarkdown/apex-plugin-documentation.git

# Each entry in bundle defines a child plugin
bundle:
  - id: kbd
    title: Keyboard Shortcuts
    description: Render {% kbd ... %} key combos to HTML <kbd> elements
    phase: pre_parse
    priority: 100
    handler:
      command: "ruby kbd_plugin.rb"

  - id: menubar
    title: Menubar Paths
    description: Render {% menubar File, Open %} to a styled menu path
    phase: pre_parse
    handler:
      command: "ruby menubar_plugin.rb"

  - id: prefspane
    title: Preferences Pane
    description: Render {% prefspane Advanced, Processor %} to a styled preference path
    phase: pre_parse
    handler:
      command: "ruby prefspane_plugin.rb"
---

Apex will treat this as three distinct plugins:

  • kbd
  • menubar
  • prefspane

all sourced from the same repository and manifest.

Inheritance and overrides

When loading a bundle, Apex:

  1. Parses the top-level mapping (the “bundle header”), and the bundle: sequence, using full YAML.
  2. For each entry in bundle:
    • Starts with the top-level metadata.
    • Applies the child entry’s fields as overrides.

Concretely:

  • Top-level author, homepage, and repo are used as defaults for every child plugin.
  • Each child must define its own:
    • id
    • phase
    • and either:
      • handler.command (external plugin), or
      • pattern + replacement (declarative plugin)
  • A child may also define its own:
    • title
    • description
    • priority
    • timeout_ms
    • flags
  • If a child defines author, homepage, or repo, those values override the bundle-level defaults for that child only.

After merging, each child is turned into a normal internal plugin record, just like if it had its own standalone plugin.yml.

Behavior and limitations

  • Discovery:
    • Bundle manifests live in the same places as normal plugins:
      • Project-local: .apex/plugins/documentation/plugin.yml
      • Global: ~/.config/apex/plugins/documentation/plugin.yml
  • Phases and IDs:
    • Each child plugin’s id and phase are used when registering and running plugins.
    • All normal rules apply:
      • IDs must be unique across all loaded plugins.
      • Phase must be pre_parse or post_render (for now).
  • libyaml requirement:
    • The bundle syntax relies on full YAML parsing via libyaml.
    • If Apex is built without libyaml support, bundle: is not recognized as a special structure; in that case, you should fall back to separate manifests for each plugin if you need cross-platform behavior on builds that omit libyaml.

External handler plugins (scripts/commands)

An external handler plugin defines a command to run, which receives JSON on stdin and writes the transformed text to stdout.

Manifest fields

---
id: kbd
title: Keyboard Shortcuts
author: Brett Terpstra
description: Render {% kbd ... %} key combos to HTML
homepage: https://github.com/ApexMarkdown/apex-kbd-plugin
repo: https://github.com/ApexMarkdown/apex-kbd-plugin.git
phase: pre_parse
priority: 100
timeout_ms: 0        # optional
handler:
  command: "ruby kbd_plugin.rb"
---
  • id: unique identifier (no spaces recommended).
  • title/author/description/homepage/repo: optional metadata used by listing/installation tools and plugin directories.
  • phase: pre_parse or post_render.
  • priority: integer; lower runs first.
  • timeout_ms: optional; host may use this as a soft cap.
  • handler.command:
    • Executed via sh -c from the plugin’s directory (APEX_PLUGIN_DIR).
    • Must be runnable in your environment (e.g. Ruby installed if you use ruby).

JSON protocol

For text phases (pre_parse, post_render), Apex sends your command a JSON object on stdin:

{
  "version": 1,
  "plugin_id": "kbd",
  "phase": "pre_parse",
  "text": "raw or rendered text here"
}

Your plugin should:

  1. Read all of stdin.
  2. Parse the JSON.
  3. Transform the text field.
  4. Print the new text only to stdout (no extra JSON, headers, or logging).

If your plugin fails, times out, or prints nothing, Apex will treat it as a no-op and continue gracefully.


Declarative regex plugins (no scripting)

For many cases, you don’t need a script at all. A declarative regex plugin uses regex.h inside Apex for fast in-process search/replace.

Manifest fields

---
id: emoji-span
title: Emoji span wrapper
author: Brett Terpstra
description: Wrap :emoji: markers in a span for styling
homepage: https://github.com/ApexMarkdown/apex-emoji-plugin
repo: https://github.com/ApexMarkdown/apex-emoji-plugin.git
phase: post_render
pattern: "(:[a-zA-Z0-9_+-]+:)"
replacement: "<span class=\"emoji\">$1</span>"
flags: "i"          # optional: e.g. i, m, s
priority: 200
timeout_ms: 0
---
  • pattern: POSIX regular expression (compiled via regcomp).
  • replacement:
    • Replacement string with capture groups like $1, $2, etc.
    • Runs repeatedly across the text until no more matches.
  • flags (optional):
    • Currently supports:
      • i – case-insensitive
      • m – multi-line
      • s – dot matches newline (if supported by underlying regex flavor)

This is ideal when:

  • You only need straightforward pattern substitution.
  • Performance matters and you want to avoid fork/exec.

Support directory and environment variables

Plugins sometimes need a place to store cache/data files or know which file is being processed. To keep plugin code directories clean and give plugins enough context, Apex exposes a support directory and several environment variables.

Support directory (APEX_SUPPORT_DIR)

Base path:

  • If $XDG_CONFIG_HOME is set:
    • $XDG_CONFIG_HOME/apex/support/
  • Otherwise:
    • ~/.config/apex/support/

For each plugin, Apex creates:

APEX_SUPPORT_DIR/<plugin-id>/

You can safely write caches, logs, or temporary files there.

Environment variables for external plugins

When Apex runs an external handler plugin, it sets:

  • APEX_PLUGIN_DIR

    • Filesystem path to the plugin’s directory (where plugin.yml lives).
    • Useful for loading sidecar files, templates, etc.
  • APEX_SUPPORT_DIR

    • Base support directory as described above.
    • You’ll usually combine it with your plugin id, e.g. $APEX_SUPPORT_DIR/kbd.
  • APEX_FILE_PATH

    • When Apex is invoked on a file (for example apex path/to/file.md), this is the original path that was passed on the command line. Plugins can use this to make decisions based on path, directory, or extension.
    • When Apex reads from stdin (for example cat file.md | apex), APEX_FILE_PATH is set to:
      • The current base_directory (if one was set via --base-dir or metadata), or
      • An empty string ("") if no base directory is known.
    • An empty APEX_FILE_PATH is a clear signal that there is no concrete source file path.

All of these variables apply only during the external command’s execution and are restored afterward.


Installing plugins from the directory

Apex can install plugins directly from a central directory hosted in the ApexMarkdown/apex-plugins GitHub repository. The directory is published as a JSON file:

  • https://github.com/ApexMarkdown/apex-plugins (repository)
  • https://raw.githubusercontent.com/ApexMarkdown/apex-plugins/refs/heads/main/apex-plugins.json (JSON index)

Each entry in apex-plugins.json looks like this:

{
  "plugins": [
    {
      "id": "kbd",
      "title": "Keyboard Shortcuts",
      "description": "Renders {% kbd %} tags as <kbd> elements.",
      "author": "Brett Terpstra",
      "homepage": "https://github.com/ApexMarkdown/apex-plugin-kbd",
      "repo": "https://github.com/ApexMarkdown/apex-plugin-kbd"
    }
  ]
}
  • id must match the plugin id declared in that repo’s plugin.yml.
  • repo is the canonical Git URL that Apex will pass to git clone.
  • title, author, description, and homepage are used for human-readable listings.

Listing available plugins

To see what plugins are available in the central directory, use:

apex --list-plugins

This command:

  • Fetches apex-plugins.json using curl.
  • Parses the plugins array.
  • Prints a concise listing to stdout, for example:
## Installed Plugins

kbd                  - Keyboard Shortcuts  (author: Brett Terpstra)
    Renders {% kbd %} tags as <kbd> elements.
    homepage: https://github.com/ApexMarkdown/apex-kbd-plugin

---

## Available Plugins

emoji-span           - Emoji span wrapper  (author: Brett Terpstra)
    Wrap :emoji: markers in a span for styling.
    homepage: https://github.com/ApexMarkdown/apex-emoji-plugin

Installing a plugin

The --install-plugin command accepts three types of arguments:

  1. Plugin ID from the directory (recommended for curated plugins):

    apex --install-plugin kbd

    This will:

    1. Fetch apex-plugins.json from the central directory.
    2. Find the entry whose id matches kbd.
    3. Read the repo field and clone the repository.
    4. Install into $XDG_CONFIG_HOME/apex/plugins/kbd or ~/.config/apex/plugins/kbd.
  2. Full Git URL (for plugins not in the directory):

    apex --install-plugin https://github.com/ttscoff/apex-plugin-kbd.git

    Also works with SSH URLs:

    apex --install-plugin git@github.com:ttscoff/apex-plugin-kbd.git
  3. GitHub shorthand (user/repo format):

    apex --install-plugin ttscoff/apex-plugin-kbd

    This is automatically expanded to https://github.com/ttscoff/apex-plugin-kbd.git.

Security confirmation for direct installs

When installing from a direct Git URL or GitHub shorthand (i.e., anything outside the curated directory), Apex will prompt:

Apex plugins execute unverified code. Only install plugins from trusted sources.
Continue? (y/n)
  • Answer y or Y to proceed with the installation.
  • Any other response (including n, N, or just pressing Enter) aborts the install.

Directory ID installs (like --install-plugin kbd from the curated list) do not show this prompt, as those plugins have been reviewed and included in the central directory.

Installation process

For all installation methods, Apex will:

  1. Create (if necessary) the user plugin directory:

    • $XDG_CONFIG_HOME/apex/plugins if $XDG_CONFIG_HOME is set, or
    • ~/.config/apex/plugins otherwise.
  2. For direct URL/shorthand installs:

    • Clone the repository into a temporary directory.
    • Read plugin.yml (or plugin.yaml) from the cloned repo to determine the plugin id.
    • Move the cloned directory to the final location: ~/.config/apex/plugins/<id>/.
  3. For directory ID installs:

    • Clone directly into the final location: ~/.config/apex/plugins/<id>/.
  4. If the target directory already exists, Apex will refuse to overwrite it and print an error. You can remove or rename the existing directory and rerun --install-plugin to reinstall.

  5. After successful cloning, look for a post_install hook in plugin.yml and run it if present.

Security note: Apex plugins execute unverified code. Only install plugins from trusted sources. When installing from a direct Git URL or GitHub repo name, you will be prompted to confirm before the plugin is cloned.

Once installed, the plugin is just like any other user-global plugin:

  • It will be discovered from $XDG_CONFIG_HOME/apex/plugins or ~/.config/apex/plugins.
  • You still need to enable plugins via --plugins or metadata (plugins: true).

If you are authoring a plugin for inclusion in the directory:

  • Make sure your plugin.yml has at least id, phase, and either handler.command or pattern/replacement.
  • Add title, author, description, homepage, and repo to plugin.yml so directory tools and installers have complete metadata.
  • Ensure the plugin.yml and any scripts live at the root of the Git repository referenced by repo.

Uninstalling a plugin

To remove a locally installed plugin, use:

apex --uninstall-plugin kbd

The --uninstall-plugin command:

  • Verifies that the plugin directory exists under the user plugin path ($XDG_CONFIG_HOME/apex/plugins or ~/.config/apex/plugins).
  • Prompts for confirmation before deleting the plugin directory.
  • Removes only the plugin’s directory; support data under .../apex/support/<plugin-id>/ is left intact.

This command only works for plugins installed in the user plugin directory. Project-local plugins (in .apex/plugins/) must be removed manually by deleting the plugin directory.

Share Your Plugin

To request that your plugin be added to the central directory:

  1. Fork the ApexMarkdown/apex-plugins repository on GitHub to your own account or organization.
  2. Clone your fork locally and create a new branch for your plugin entry (for example, add-kbd-plugin).
  3. Open the apex-plugins.json file in the root of your fork and add a new object to the plugins array with the fields:
    • id (matching the id in your plugin’s plugin.yml),
    • title,
    • description,
    • author,
    • homepage,
    • repo (canonical Git URL for the plugin repo).
  4. Commit your changes and push the branch to your fork on GitHub.
  5. From your fork, open a pull request against the main branch of ApexMarkdown/apex-plugins, briefly describing your plugin and confirming that the id and repo match your published plugin repository.
  6. Once the pull request is reviewed and merged, your plugin will show up in apex --list-plugins and be installable via apex --install-plugin <id>.

Example: kbd liquid tag plugin

This example shows how to support a liquid-style {% kbd ... %} syntax, turning key combos into <kbd> markup.

Directory layout

Project-local example:

.apex/
  plugins/
    kbd/
      plugin.yml
      kbd_plugin.rb

Global example:

~/.config/apex/plugins/
  kbd/
    plugin.yml
    kbd_plugin.rb

plugin.yml

---
id: kbd
title: Keyboard Shortcuts
author: Brett Terpstra
description: Render {% kbd ... %} key combos to HTML <kbd> elements
homepage: https://github.com/ApexMarkdown/apex-kbd-plugin
repo: https://github.com/ApexMarkdown/apex-kbd-plugin.git
phase: pre_parse
priority: 100
timeout_ms: 0
handler:
  command: "ruby kbd_plugin.rb"
---

kbd_plugin.rb (simplified shape)

The full script lives in the examples directory of the Apex repo as a reference. Conceptually, it:

  • Reads JSON from stdin.
  • Extracts text.
  • Replaces each {% kbd ... %} occurrence with properly formatted <kbd> HTML.
  • Prints the full transformed text to stdout.

A very abridged sketch:

#!/usr/bin/env ruby
require "json"

payload = JSON.parse($stdin.read)
text = payload["text"] || ""

# ... helper methods for normalizing modifier names, etc. ...

def render_kbd(markup)
  # convert markup like "^~@r" to HTML
  # returning something like:
  #   <kbd>⌃</kbd>+<kbd>⌥</kbd>+<kbd>⌘</kbd>+<kbd>R</kbd>
end

result = text.gsub(/\{%\s*kbd\s+([^%]+)%\}/) do
  render_kbd(Regexp.last_match(1))
end

print result

You can customize this script as you like; the only requirement is that it obey the stdin JSON / stdout contract.


Example: :emoji: span plugin (declarative)

This plugin turns :emoji: tokens in the final HTML into <span class="emoji">:emoji:</span>.

Directory layout

~/.config/apex/plugins/
  emoji/
    plugin.yml

plugin.yml

---
id: emoji-span
title: Emoji span wrapper
author: Brett Terpstra
description: Wrap :emoji: markers in a span for styling
homepage: https://github.com/ApexMarkdown/apex-emoji-plugin
repo: https://github.com/ApexMarkdown/apex-emoji-plugin.git
phase: post_render
pattern: "(:[a-zA-Z0-9_+-]+:)"
replacement: "<span class=\"emoji\">$1</span>"
flags: "i"
priority: 200
timeout_ms: 0
---

Because this is a declarative plugin, no external command is run. Apex compiles the regex and runs the replacements internally.


Putting it all together

A typical workflow:

  1. Create a plugin directory

    • Project-local: .apex/plugins/my-plugin/
    • Or global: ~/.config/apex/plugins/my-plugin/
  2. Add plugin.yml

    • Choose phase (pre_parse or post_render).
    • Choose plugin type:
      • External: add handler.command.
      • Declarative: add pattern, replacement, and optional flags.
    • Optionally, add metadata fields (title, author, description, homepage, repo) so your plugin shows up nicely in directories and can be auto-installed.
    • For bundles, use bundle: to define multiple related child plugins in one manifest.
  3. Add code (if external)

    • Put your script alongside plugin.yml.
    • Implement the JSON stdin → text stdout contract.
  4. Enable plugins

    • Either:
      • Add plugins: true (or enable-plugins: true) to your document metadata, or
      • Run with --plugins on the command line.
  5. Test

    • Run Apex on a sample document that exercises your plugin syntax.
    • If something goes wrong, log to a file under $APEX_SUPPORT_DIR/<id>/ rather than printing debugging output to stdout.

Once you’re comfortable, you can share the plugin.yml plus any scripts with others; they just need to drop the directory into their .apex/plugins/ or ~/.config/apex/plugins/ and enable plugins as needed.

Quick Links

Clone this wiki locally