Using nlmixr2llm with different LLM clients and IDEs
Source:vignettes/nlmixr2llm.Rmd
nlmixr2llm.Rmdnlmixr2llm ships LLM-facing documentation for the
nlmixr2 pharmacometric modeling ecosystem: rxode2,
nlmixr2, nonmem2rx, monolix2rx,
and babelmixr2. It bundles a single combined
nlmixr2verse agent (the orchestration layer spanning all
five packages) plus one skill per package for on-demand depth. The same
content can be used three ways:
-
As a system prompt with any LLM client library (the
ellmerR package, the Anthropic SDK, the OpenAI SDK, etc.). -
As tool-native files installed into Claude Code,
OpenAI Codex CLI, Positron Assistant, or any tool that reads
AGENTS.md. - As a Claude Code plugin for users who prefer the plugin marketplace path and don’t want to touch R.
This vignette walks through each path.
Discovering what’s available
list_packages()
#> [1] "babelmixr2" "monolix2rx" "nlmixr2" "nonmem2rx" "rxode2"
list_agents()
#> [1] "nlmixr2verse"
list_skills()
#> [1] "babelmixr2" "monolix2rx" "nlmixr2" "nonmem2rx" "rxode2"list_agents() returns the single combined
nlmixr2verse agent; list_packages() and
list_skills() enumerate the five packages whose skills
provide per-package depth. You can read any single document
directly:
agent_text <- get_agent() # the combined nlmixr2verse agent
substr(agent_text, 1, 200)
#> [1] "---\nname: nlmixr2verse\ndescription: Specialist for the whole nlmixr2 pharmacometric modeling ecosystem in R. Use for any task involving rxode2 (author/simulate ODE-based PK/PD models), nlmixr2 (fit po"
skill_text <- get_skill("rxode2")
substr(skill_text, 1, 200)
#> [1] "---\nname: rxode2\ndescription: Use this skill when the user is creating, editing, or running ODE-based pharmacometric models with the R package rxode2. Triggers include writing PK/PD models with `ini({"1. Use as a system prompt with any LLM client
system_prompt() returns a single character string with
the agent and skill content concatenated and YAML frontmatter stripped.
Pass it as the system prompt to whichever client you use.
prompt <- system_prompt(packages = c("rxode2", "nlmixr2"))
nchar(prompt)
#> [1] 46084By default system_prompt() includes both agents and
skills for all packages. Subset with packages = ... or
include = c("agents", "skills").
With ellmer (Anthropic, OpenAI, Gemini, Ollama, …)
ellmer is the recommended path for R users — it
abstracts over multiple LLM providers and is the same client Positron
Assistant is built on.
library(ellmer)
chat <- chat_anthropic(
system_prompt = system_prompt(packages = c("rxode2", "nlmixr2")),
model = "claude-sonnet-4-6"
)
chat$chat(
"Write a one-compartment PK model with first-order absorption ",
"in rxode2, then simulate a single 100 mg dose for 24 hours."
)The same prompt works with any ellmer backend
(chat_openai(), chat_google_gemini(),
chat_ollama(), …).
With the Anthropic SDK directly (e.g., via httr2)
req <- httr2::request("https://api.anthropic.com/v1/messages") |>
httr2::req_headers(
"x-api-key" = Sys.getenv("ANTHROPIC_API_KEY"),
"anthropic-version" = "2023-06-01"
) |>
httr2::req_body_json(list(
model = "claude-sonnet-4-6",
max_tokens = 1024,
system = system_prompt(packages = "rxode2"),
messages = list(list(role = "user", content = "..."))
))With the OpenAI SDK directly
Same pattern — pass system_prompt() as the
system (or instructions) field.
2. Install for specific coding environments
Each installer writes the bundled content into the location its
target tool reads from. None of these clobber existing files unless you
ask: pass overwrite = TRUE (Claude Code, Positron) or
mode = "write"/"append" (Codex, AGENTS.md) to
control behavior.
Claude Code (Anthropic CLI / IDE extensions)
Claude Code looks for agents at
~/.claude/agents/<name>.md and skills at
~/.claude/skills/<name>/SKILL.md (user scope), or
under ./.claude/ at the project root (project scope).
# User-wide — every Claude Code session sees these
install_claude_code(scope = "user")
# Project-local — only when Claude Code runs in this project
install_claude_code(scope = "project", path = ".")
# Selective install
install_claude_code(scope = "user", packages = c("rxode2", "nlmixr2"))
# Replace existing files
install_claude_code(scope = "user", overwrite = TRUE)OpenAI Codex CLI
Codex reads ~/.codex/AGENTS.md globally and
AGENTS.md files at and above the current working directory
in a project. The installer concatenates the requested content into a
single AGENTS.md and writes it to either location.
# Project AGENTS.md at the repo root
install_codex(scope = "project", path = ".",
packages = c("rxode2", "nlmixr2"))
# Global ~/.codex/AGENTS.md (append rather than overwrite)
install_codex(scope = "user", mode = "append")Codex enforces a default 32 KiB cap on combined
AGENTS.md content (project_doc_max_bytes in
~/.codex/config.toml). The full corpus (~62 KiB) is larger
than that. The combined nlmixr2verse agent is ~29 KiB and
is always included whole when agents are requested, so
packages = ... only subsets the skills. For Codex you’ll
typically want to subset:
# Just the combined agent (~29 KiB, high-signal), not the long skill files
install_codex(scope = "project", include = "agents")
# Or just the skills for the packages you're actually using
install_codex(scope = "project", include = "skills",
packages = c("rxode2", "nlmixr2"))install_codex() warns when the written file exceeds 32
KiB so you know to subset or raise Codex’s cap.
Positron Assistant
Positron Assistant auto-discovers instruction files in the workspace root. The package supports two styles:
style = "agents_md" (default) writes a
single agents.md at the workspace root. Positron picks it
up, and the same file is the cross-tool AGENTS.md convention used by
Codex, Cursor, Aider, Zed, and others — so one file covers many tools at
once.
install_positron(workspace = ".", style = "agents_md")Note —
AGENTS.mdvsagents.mdon case-insensitive filesystems. On macOS and Windows,agents.md(this Positron style) andAGENTS.md(install_codex(scope = "project")/install_agents_md()) are the same file. Installing both into one project means the second call overwrites the first. With default arguments the instruction body is identical so this is harmless; if you give each a differentpackages/include, only the last call’s selection survives. On case-sensitive filesystems (most Linux) they are distinct files.nlmixr2llm_status()collapses them into a single reported entry when they resolve to the same file.
style = "instructions" writes one
.github/instructions/<package>.instructions.md per
selected package (skill content), plus a single
nlmixr2verse.instructions.md (the combined ecosystem
agent), each with applyTo: "**/*.R" in its frontmatter.
Positron attaches the relevant content when the model is editing R
files. This is the more selective option: the LLM only sees this
guidance when actually working on R code.
install_positron(
workspace = ".",
style = "instructions",
packages = c("rxode2", "nlmixr2")
)Positron does not currently document a user-level (cross-workspace)
instructions location. To get user-wide coverage, call
install_positron() from a project template or a setup
hook.
Cursor, Aider, GitHub Copilot, Zed, Warp, …
Any tool that follows the agents.md spec reads a
project-root AGENTS.md. Use the generic installer:
install_agents_md(path = ".", packages = c("rxode2", "nlmixr2"))This is a thin wrapper around
install_codex(scope = "project") since the file format is
the same.
3. Install as a Claude Code plugin (no R required)
The plugin manifest in .claude-plugin/ at the repo root
points at the same inst/agents/ and
inst/skills/ content the R package ships, so the GitHub
repo also functions as a Claude Code plugin marketplace. Users who don’t
want to install the R package can do:
/plugin marketplace add john-harrold/nlmixr2llm
/plugin install nlmixr2llm@nlmixr2llm
The plugin and the R-package installer write the same content into the same location, so either path works.
Choosing a path
| You want to … | Use |
|---|---|
| Call an LLM from R code (e.g., in a script or app) |
system_prompt() + ellmer
|
| Always have nlmixr2 guidance in Claude Code | install_claude_code(scope = "user") |
| Add nlmixr2 guidance to one Codex project | install_codex(scope = "project") |
| Add nlmixr2 guidance to one Positron workspace | install_positron() |
| Cover Cursor / Aider / Zed / Copilot / Codex in one file | install_agents_md() |
| Use Claude Code without touching R | /plugin install nlmixr2llm@nlmixr2llm |
Keeping content in sync
Upgrading the nlmixr2llm R package updates the content
bundled in your R library, but the installers write
independent copies into each tool’s location — those
copies don’t change until you re-run the installer. After an upgrade,
re-run it for whichever environment you use. Installers don’t overwrite
by default, so pass overwrite = TRUE (or
mode = "write") to refresh existing files.
To make that safe to do blindly, install_claude_code()
and install_positron() compare each existing file against
the current bundled content and tell you which are stale. With
overwrite = FALSE you’ll see, per file,
up to date or
out of date (overwrite = TRUE to refresh), and the summary
line reports how many are out of date — so a no-op re-install still
tells you whether an update is waiting.
install_claude_code(scope = "user", overwrite = TRUE)
install_codex(scope = "user", mode = "write")
install_positron(workspace = ".", overwrite = TRUE)Pruning files the package no longer ships
install_claude_code() and
install_positron(style = "instructions") record what they
wrote in a manifest (.nlmixr2llm-manifest) in the install
location. On re-install they prune files this package
installed in an earlier version but no longer ships — for example, if an
agent is renamed or a package is dropped, the stale file is removed
rather than left behind as a duplicate. Pruning is on by default
(prune = TRUE) and only ever touches files nlmixr2llm
itself created; your own agents and skills are never removed. Selecting
a subset with packages = ... does not prune the skills of
packages you leave out — only content the current version no longer
ships at all is removed.
# Full sync: refresh existing files and drop any the package no longer ships
install_claude_code(scope = "user", overwrite = TRUE, prune = TRUE)
# Keep stale files in place if you've customized them
install_claude_code(scope = "user", overwrite = TRUE, prune = FALSE)The single-file installers (install_codex(),
install_agents_md(), and
install_positron(style = "agents_md")) always write one
fixed-name file, so there is nothing to orphan and no manifest is
used.
Checking what’s current without reinstalling
nlmixr2llm_status() compares the content this version of
the package bundles against the copies installed into
every target — Claude Code (user and project scope),
Codex / AGENTS.md, and both Positron styles — and reports
which files are current, outdated, or
not installed, along with the refresh command for each
stale target.
Claude Code files are discrete, so they’re compared by content; the
concatenated single-file targets (Codex AGENTS.md, Positron
agents.md, and the Positron *.instructions.md
files) carry an embedded version stamp and are compared by the package
version recorded at install time. Only targets that actually have
installed content appear in the summary.
The nlmixr2verse agent runs this check itself, once per
session, and proactively tells you when the installed content lags the
package — so after you upgrade nlmixr2llm, the agent will
let you know there’s a newer version to install rather than silently
running stale guidance.