pkgmd

Markdown reference documentation for any R package — no HTML, no build step, no browser.

pkgmd reads the Rd documentation of an R package and renders it as plain Markdown: one file per topic, a navigable index, and a single bundled file ready to hand to an LLM as context. Think of it as a pkgdown alternative for teams who live in the GitHub/Gitea browser UI (or in Claude Code) rather than a hosted HTML site.

pkgmd::build_reference("dplyr", output_dir = "docs/reference")

Two functions cover the whole API: build_reference() writes the files above to disk, and get_documentation() returns that same reference as a single string, in memory — handy for handing a package’s reference straight to an LLM agent as context.

Why

Quick start

# An installed package:
pkgmd::build_reference("dplyr", output_dir = "docs/reference")

# A package you're actively developing, not installed anywhere:
pkgmd::build_reference(".", output_dir = "docs/reference")
pkgmd::build_reference("../myotherpkg", output_dir = "docs/reference")

package is a single, smart parameter: pass an installed package’s name, or a path to a local package source directory (detected by the presence of a DESCRIPTION file). No separate flag needed to switch modes.

This produces:

docs/reference/
├── README.md            # index: every topic, one row each, with links
├── mutate.md
├── filter.md
├── ...
└── _full_reference.md   # every topic in one file, for LLM context

pkgmd dogfoods itself: md-docs/ in this repo is pkgmd’s own reference, built from its own source by build_reference(".") — a live example of the output above.

In-memory, for agents

docs <- pkgmd::get_documentation("dplyr")

Same content as _full_reference.md, returned as a string instead of written to disk — no output_dir to pick, nothing left behind on the filesystem. Defaults to the public API only (include_private = FALSE), the opposite default from build_reference(), since the usual caller here is an LLM agent that wants a package’s reference, not its internals.

Features

Output that just works in GitHub/Gitea

Faithful to what the Rd source actually says

Beyond the basics (title, description, usage, arguments, value, examples, see also), pkgmd renders the parts of Rd that simpler tools tend to drop on the floor:

Handles internal/private functions, on purpose

Documented-but-unexported helpers (@keywords internal, no @export) are included by default and clearly marked:

# `internal_helper()` — Does the fiddly bit _(private)_

Set include_private = FALSE to generate a public-API-only reference instead. Export status is resolved correctly regardless of package size or NAMESPACE complexity (getNamespaceExports() for installed packages), and datasets are never mistaken for private helpers just because data objects aren’t in NAMESPACE’s export() list either.

pkgmd::build_reference("dplyr", output_dir = "docs/reference", include_private = FALSE)

Two sources, one code path

Both paths converge on the same internal representation, so every feature above works identically no matter where the documentation came from.

Safe to run again and again

Example

Running pkgmd against a minimal mutate()-like function produces:

# `mutate()` — Add or modify columns

**Package:** demo · **Version:** 0.1.0

Add or modify columns

## Usage

```r
mutate(.data, ...)
```

## Arguments

| Argument | Description |
|----------|-------------|
| `.data` | A data frame. |
| `...` | Name-value pairs of expressions. |

## Value

A data frame with modified/added columns.

---
*Generated by [pkgmd](../../pkgmd/) · [Back to index](README.md)*

Run it against base or dplyr and you get the same thing, at scale — pkgmd is exercised against base (446 topics) and dplyr (115 topics) in its own test suite, alongside 260+ unit tests covering the renderer directly.

Installation

install.packages("pkgmd")

Development version, from GitHub:

# pak
pak::pkg_install("peterczerner/pkgmd")

# or devtools
devtools::install_github("peterczerner/pkgmd")

Requires R ≥ 4.1.0 (uses the native pipe internally). No Pandoc, no rmarkdown, no browser.

Development

devtools::load_all()
devtools::test()
devtools::document()

roxygen2 is a Suggests dependency, not Imports — it’s only loaded as a fallback for development packages that haven’t rendered man/ yet. The main code paths (installed packages, or dev packages with man/ already present) never need it.

License

MIT © Peter Czerner