Skip to content
    ↑↓ select↵ openesc close
    English中文
    Sewer56

    Md Expand

    v0.2.0Tools & Commands
    opencode-plugin-md-expand

    OpenCode plugin and CLI for expanding Markdown prompt templates.

    GitHub stars

    2

    Monthly installs

    15

    7 in 7 days

    Composite scoreSCORE

    27.5

    Multi-signal model

    Last commit

    2 months ago

    2026-06-07

    Install and configure

    opencode.json

    Writes to this project's opencode.json — applies to this repository only.

    opencode.json

    {
      "$schema": "https://opencode.ai/config.json",
      "plugin": ["opencode-plugin-md-expand@0.2.0"]
    }

    opencode loads npm dependencies through its embedded runtime on startup and caches them locally — no manual global install needed.

    OpenCode plugin that expands {{...}} templates in .md agent, command, mode, skill files, and user messages before the LLM sees them.

    Expand every {{...}} in your markdown files.

    File includes, environment variables, scoped arguments, and inline conditionals: all resolved before the LLM reads.

    At a glance

    Include shared rules files in any prompt:

    You must follow these rules:
    {{ file="./rules/general.md" }}
    {{ file="./rules/style.md" }}
    

    Inject environment variables at runtime:

    Base API URL: {{env:API_URL}}
    {{ if=env:CI }}Skip interactive prompts.{{ endif }}
    

    Switch instructions by mode (a custom arg):

    {{ file="./review.md" mode=cached }}
    

    Inside review.md:

    {{ if=mode==cached }}
    Reuse previous analysis results.
    {{ else }}
    Run a fresh analysis.
    {{ endif }}
    

    Pass args into a reusable template:

    {{ file="./template.md" title="Plan Review" domain=correctness }}
    

    Inside template.md:

    # {{arg:title}}
    
    Domain: {{arg:domain}}
    

    Reference scripts and files by absolute path:

    After changes, run `{{path:.cargo/verify.sh}}` before returning.
    

    {{path:...}} resolves relative to current working directory. {{gitpath:...}} resolves relative to the repository root.

    (Path tokens emit absolute paths without reading the file - see Path resolution in Template grammar.)

    Install in OpenCode

    Add to opencode.json (or .opencode/opencode.json):

    {
      "plugin": ["opencode-plugin-md-expand@^0.1.0"],
    }
    

    OpenCode auto-installs npm packages into its cache directory on first load - no manual npm install needed.

    With options:

    {
      "plugin": [
        [
          "opencode-plugin-md-expand@^0.1.0",
          {
            "debug": true,
          },
        ],
      ],
    }
    

    Configuration

    The plugin auto-derives fallback directories from standard OpenCode paths:

    1. <project>/.opencode
    2. <cwd>/.opencode
    3. $XDG_CONFIG_HOME/opencode (e.g. ~/.config/opencode)

    If your config lives in a non-standard location, override with configDirs:

    {
      "plugin": [
        [
          "opencode-plugin-md-expand@^0.1.0",
          {
            "configDirs": ["./my-custom-config"],
          },
        ],
      ],
    }
    

    configDirs replaces the three auto-derived defaults entirely. If you only want to add directories while keeping the defaults (project root, current working directory, $XDG_CONFIG_HOME), use extraConfigDirs instead:

    {
      "plugin": [
        [
          "opencode-plugin-md-expand@^0.1.0",
          {
            "extraConfigDirs": ["{env:HOME}/.config/opencode/extra-md"],
          },
        ],
      ],
    }
    

    OpenCode applies {env:VAR} substitution to the raw JSON before the plugin processes it, so {env:HOME} expands to your home directory at config-load time.

    The CLI and wrapper scripts also respect OPENCODE_CONFIG_DIR for runtime overrides.

    Options

    Option Description
    configDirs string[] · default ["<project>/.opencode", "<cwd>/.opencode", "$XDG_CONFIG_HOME/opencode"]. Ordered fallback dirs for relative {{ file="./..." }} includes. Replaces defaults entirely. See Configuration.
    extraConfigDirs string[] · default []. Additional dirs appended to auto-derived defaults. Ignored when configDirs is set. See Configuration.
    maxDepth number · default 10. Maximum recursive file include depth. At limit, file templates stay literal; env/arg/if still expand.
    debug boolean · env-based. Write debug logs. Also enabled by OPENCODE_PLUGIN_MD_EXPAND_DEBUG=1.
    logDir string · default <configDirs[0]>/plugins/.logs/opencode-plugin-md-expand. Debug log directory.
    initialArgs Record<string, string> · default {}. Key-value pairs injected as {{arg:*}} variables for every expansion. Useful for global defaults like mode or domain.
    cache boolean · default false. Cache raw and recursively-expanded file content across plugin transform calls. Also enabled by OPENCODE_PLUGIN_MD_EXPAND_CACHE=1; leave off while editing config files.

    Template grammar

    File includes

    {{ file="./rules/general.md" }}
    {{ file="~/.secrets/project-context" }}
    {{ file="./template.md" domain=correctness mode="cached review" }}
    

    Path rules:

    • ~/... resolves to $HOME
    • ./... and ../... resolve relative to <project>/ or <cwd>/
    • other relative paths resolve relative to <project>/ or <cwd>/
    • if configDirs is set, missing ./... / ../... paths fall back to those directories in order

    Args

    {{ file="./template.md" title="Plan Review" }}
    

    In template.md:

    # {{arg:title}}
    

    Arg rules:

    • undefined {{arg:key}} expands to an empty string
    • args are scoped to one file include
    • nested file includes do not inherit parent args unless the parent {{ file=... }} directive includes them as key=value pairs
    • arg values are literal for env/file tokens, but {{arg:...}} references can be nested within arg values and are resolved recursively

    Path resolution

    Run {{path:.cargo/verify.sh}} before committing.
    Source: {{gitpath:src/main.ts}}
    

    Path tokens:

    • {{path:./file.sh}} -- relative to current working directory
    • {{path:../sibling/file.txt}} -- ../ relative to current working directory
    • {{path:~/file.txt}} -- ~/ expands to $HOME
    • {{gitpath:src/main.ts}} -- relative to git root
    • {{gitpath:...}} -- fallback to base directory outside git repo

    {{path:...}} and {{gitpath:...}} are tokens that emit an absolute path string without reading the file. They are resolved during the same expansion pass as {{env:...}} and {{arg:...}}.

    Rules:

    • The path must be a literal string; env/arg interpolation inside the path value is not supported.
    • {{gitpath:...}} discovers the git root via git rev-parse --show-toplevel, with a module-level cache so repeated lookups in the same expansion are free.

    Conditionals

    {{ if=mode==cached }}
    Use cached review scope.
    {{ else }}
    Use cacheless review scope.
    {{ endif }}
    
    {{ if=mode!=cached }}
    Use cacheless review scope.
    {{ endif }}
    
    {{ if=env:CI }}
    CI-only instructions.
    {{ endif }}
    
    {{ if=env:CI!=true }}
    Local-only instructions.
    {{ endif }}
    

    Condition operators:

    • == - exact equality (if=arg==value includes when arg equals value)
    • != - inequality (if=arg!=value includes when arg differs from value; if=arg!= includes when arg is empty or absent)

    CLI

    Install globally or locally:

    npm install -g opencode-plugin-md-expand
    opencode-plugin-md-expand --help
    

    Or add to devDependencies and use via npx:

    {
      "devDependencies": {
        "opencode-plugin-md-expand": "^0.1.0",
      },
      "scripts": {
        "validate": "opencode-plugin-md-expand validate --config-dir config",
      },
    }
    

    To run from source (without publishing), use bun src/cli/cli.ts instead.

    Validate templates

    Useful in git hooks and CI:

    opencode-plugin-md-expand validate --config-dir config config/agent config/command config/rules
    

    Validation fails on:

    • missing file includes
    • empty file include paths
    • file include cycles (circular references where A includes B which includes A)
    • unexpanded {{ file=... }} tokens
    • unexpanded {{ if=... }} / {{ endif }} markers
    • unexpanded {{arg:...}} or {{env:...}} tokens
    • unexpanded {{path:...}} or {{gitpath:...}} tokens

    Git hook example:

    #!/usr/bin/env sh
    set -eu
    repo_root="$(git rev-parse --show-toplevel)"
    cd "$repo_root"
    opencode-plugin-md-expand validate --config-dir config config/agent config/command config/rules
    

    Render one template

    Useful for prompt debugging:

    opencode-plugin-md-expand render --config-dir config agent/_plan/finalize.md
    opencode-plugin-md-expand render --config-dir config rules/groups/style/self-wording.md -o /tmp/rendered.md
    

    Pass top-level args manually:

    opencode-plugin-md-expand render --config-dir config --arg mode=cached agent/example.md
    

    Enable debug:

    opencode-plugin-md-expand render --config-dir config --debug agent/example.md
    cat config/plugins/.logs/opencode-plugin-md-expand/debug.log
    

    Development

    Requires Bun.

    bun install            # install dependencies
    bun run typecheck      # check TypeScript types (no runtime)
    bun test               # run tests
    bun run build          # compile TypeScript to dist/
    bun run check          # run all checks (typecheck + test + format + build)
    bun run format         # auto-format all files
    bun run format:check   # check formatting (CI enforces this)
    

    Editor setup: .vscode/settings.json enables format-on-save with oxc.

    Local path

    For development or private forks, reference the plugin directory directly:

    {
      "plugin": ["plugins/opencode-plugin-md-expand"],
    }
    

    No wrapper .ts file needed: OpenCode detects local plugin directories and loads them directly from index.ts or package.json.

    Publishing

    GitHub Actions publishes the package to npm when you push a v* tag.

    Release workflow:

    npm version patch|minor|major # bumps package.json + creates matching git tag
    git push --follow-tags        # pushes commit + tag → triggers publish.yml
    

    The CI workflow runs a full check (typecheck, test, format:check, build) before publishing with --provenance, creating a cryptographically signed link between the published version and its originating git commit.

    Manual fallback:

    npm login
    npm publish --provenance --access public
    

    Benchmark prompt transforms

    The benchmark harness uses Tinybench and copied fixtures from a real OpenCode config:

    bun run bench
    

    It measures the experimental.chat.system.transform hook for simple imports, deep/complex imports, and no-op static markdown. Override run length when needed:

    MD_EXPAND_BENCH_TIME_MS=5000 MD_EXPAND_BENCH_MIN_ITERATIONS=512 bun run bench
    

    Package shape

    OpenCode npm plugin entrypoint is the default export:

    export default {
      id: "opencode-plugin-md-expand",
      server: MdExpandPlugin,
    };
    

    License

    MIT