A skill teaches a workflow; a prompt template turns a repeated request into a slash command; an agent definition describes a specialist. A plugin distributes these resources and can add R tools or MCP servers. Start with Markdown when instructions are enough. Use the extension API when you need executable behavior.
List resources without running plugin code:
library(gptr)
gptr_skills(scope = "all")
gptr_agents(scope = "project")
gptr_plugins(installed = TRUE)gptr_skills(scope = c("all", "project", "user", "packages"))
reports each skill’s name, description, source, path, catalog token
estimate and visibility. The same scope choices apply to
gptr_agents(). A short skill catalog helps the model choose
relevant instructions; the body is loaded on demand. Preload a known
skill with skills = "name":
peter("Review the normalization steps", skills = "analysis-review", model = "provider/model")
peter$read("skill:analysis-review/SKILL.md")
peter$read("skill:analysis-review/references/checklist.md")These paths refer to files inside the skill directory. Keep long references and examples in separate files and tell the skill when to read them. A skill supplies instructions, not a data snapshot; objects named in its text must still be available to the tools that use them. See Execution model for the R environment and provider context, Language models for selecting a provider, and Tools and artifacts for reading files.
Put SKILL.md in
.gptr/skills/analysis-review/ for a project, or in
file.path(tools::R_user_dir("gptr", "config"), "skills", "analysis-review")
for your own sessions. A minimal file is:
---
name: analysis-review
description: Review an R analysis for units, missing values and reproducibility.
---
Check the input dimensions and missing values before interpreting results.
Report quantities with units. Read references/checklist.md for the detailed checks.
Ask before changing the analysis.Use a lowercase name of at most 64 characters with letters, digits
and hyphens, beginning with a letter or digit. Avoid a trailing hyphen
or consecutive hyphens. description is required and should
say when to use the skill. name defaults to the folder
name. disable-model-invocation: true hides a skill from
automatic catalog selection; an explicit skills = preload
still works. allowed-tools is retained as metadata and does
not grant or restrict permissions by itself.
Project discovery also recognizes .agents/skills/ and
.claude/skills/. User discovery includes the gptr
configuration directory and compatible .agents,
.claude, .codex and .pi/agent
skill directories. Attached packages and enabled plugins can supply
skills. For another location, set skills.paths; relative
paths belong to the project, while absolute or ~/ paths are
user resources:
Save a prompt template as .gptr/prompts/review.md, or
under the user’s gptr prompts/ directory. Its file name
becomes /review in the interactive console:
---
description: Review an analysis section
argument-hint: section focus
---
Review $1. Focus on ${2:-reproducibility}. The full request is: $ARGUMENTSFor example,
/review "normalization steps" "missing values" substitutes
the two quoted arguments. $1, $2,
$ARGUMENTS and $@ cover the common forms;
${1:-default} provides a fallback, ${@:2:2}
takes a slice, and Claude’s $ARGUMENTS[0] selects the first
argument. Substitution happens once and does not evaluate R or shell
code. Template model and allowed-tools fields
are retained as metadata; the generated command does not apply them. An
existing slash command wins over a template with the same name.
Use AGENTS.md for instructions that apply to a directory
rather than a selected workflow. From the project root to the current
directory, gptr takes the first existing file among
AGENTS.override.md, AGENTS.md and
CLAUDE.md, and also reads CLAUDE.local.md. It
includes user instructions from the gptr configuration directory and
.gptr/vignette.Rmd when present. These are instruction
data; executable extensions live in a different directory.
An agent file is Markdown with required name and
description frontmatter. Save this as
.gptr/agents/reviewer.md:
---
name: reviewer
description: Check an analysis and report issues without changing files.
model: inherit
tools: [r, read, grep, find, ls]
preset: minimal
max_turns: 8
---
Inspect the supplied objects and files. Give evidence for each issue and suggest a fix.Load a definition by name or file; pass it to agents =
to make the specialist available:
reviewer = gptr_agent("reviewer")
reviewer = gptr_agent(file = ".gptr/agents/reviewer.md")
peter("Have the reviewer check this analysis", agents = list(reviewer))Definitions can also set skills, backend,
permission mode and a structured returns
schema. gptr_agent() creates the same records in R. An
agent definition does not itself run a review; execution and object
transfer are covered in Teams and
background work. Compatible .claude/agents,
.codex/agents and .pi/agents directories are
discovered too.
An R package stores its resources under inst/gptr/; the
installed directory is gptr/. Add these fields to
DESCRIPTION:
Imports: gptr
Config/gptr/plugin: true
Config/gptr/api: >= 1.0, < 2
A directory plugin instead keeps plugin.json at its
root. This manifest works for an R package with an exported factory
gptrlab::gptr_plugin:
{
"name": "gptrlab",
"version": "0.1.0",
"description": "Analysis review instructions and tools",
"gptr": {"api": ">= 1.0, < 2"},
"skills": "skills",
"prompts": "prompts",
"agents": "agents",
"mcpServers": "mcp.json",
"extension": {
"entry": "gptrlab::gptr_plugin",
"activation": "lazy",
"provides": {"tool": ["gptrlab/add"]},
"declarations": {
"gptrlab/add": {"signature": "add(a, b)", "description": "Add two numbers"}
}
}
}The factory registers the promised records:
gptr_plugin = function(gptr) {
gptr$require(">= 1.0, < 2")
schema = list(type = "object", required = I(c("a", "b")),
properties = list(a = list(type = "number"), b = list(type = "number")))
gptr$register(gptr::gptr_tool("add", "Add two numbers", parameters = schema,
fun = function(a, b) a + b, exposure = "r", namespace = "gptrlab"))
}Export that function from the package. For a directory plugin, put a
factory as the last expression of each extensions/*.R file
instead of using a package entry point. Enable an installed plugin, or
an existing directory, for a call:
peter("Review the analysis", plugins = "gptrlab")
peter("Review the analysis", plugins = "./lab-plugin")
gptr_config(plugins = "gptrlab", .scope = "user")
gptr_check("gptrlab", error = TRUE)gptr_check() validates a package and can execute its
extension factory in a scratch registry; review its code before checking
or enabling it. Listing with gptr_plugins() only reads
metadata. R code activates lazily when the manifest declares
extension.provides, unless activation is
"eager". Without those declarations, code loads eagerly.
Resource discovery and code activation are separate: skills and
templates can be available before the package namespace loads.
Claude bundles with .claude-plugin/plugin.json can
contribute skills, commands, agents and MCP configuration. gptr reads
compatible installed Claude plugin inventories but does not install
plugins for you. Claude hooks and executable Claude plugin behavior are
not imported. Supported root substitutions include
${CLAUDE_PLUGIN_ROOT} and ${GPTR_PLUGIN_ROOT}
in plugin resources. See MCP for the connection
and authentication steps.
Project skills, templates, executable extensions, plugins and MCP
servers require project trust. Review a project before calling
gptr_trust(".", trust = TRUE). Untrusted agent definitions
have special restrictions on their model and tools and require
interactive confirmation when usable; unattended runs refuse them. Configuration explains the trust
boundary.
For the same resource kind and name, session definitions take
priority over trusted project resources, then user resources, plugins
and built-ins. Equal-rank collisions keep the first registration and
produce a diagnostic. Built-in permission safeguards remain active.
Inspect gptr_registry(diagnostics = TRUE) when a resource
is missing or shadowed; gptr_reload() rereads changed
resources for later use.
The smallest executable extension can be tried offline without creating a plugin:
library(gptr)
units = function(gptr) {
gptr$register(gptr_prompt_section("example_units", "Report quantities with units."))
}
fake = gptr_fake_provider(list("The distance is 3 m."))
s = peter("Report the distance", extensions = units, model = fake, envir = new.env())
s$text
#> [1] "The distance is 3 m."For recorded workflows that others can replay, see Script as history.