npx claudepluginhub choxos/rpkgagent --plugin r-package-developmentThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Unifies notifications from GitHub, Linear, desktop alerts, hooks, and chat into ECC-native workflow. Use for alert routing, deduplication, escalation, and inbox collapse.
Managing dependencies correctly is critical for R packages. This skill covers the different dependency types, how to declare and use them, and common patterns for conditional dependencies.
Most important rule: Listing a package in Imports: does NOT make its functions available!
# DESCRIPTION:
Imports:
dplyr
# This does NOT work:
my_function <- function(data) {
filter(data, value > 0) # ERROR: object 'filter' not found
}
# You must ALSO either:
# Option 1: Use explicit namespace (RECOMMENDED for most cases):
my_function <- function(data) {
dplyr::filter(data, value > 0)
}
# Option 2: Import to namespace via roxygen2:
#' @importFrom dplyr filter
my_function <- function(data) {
filter(data, value > 0)
}
Listing in DESCRIPTION ensures the package is installed. Importing to NAMESPACE makes functions available in your code.
Packages required for your package to work.
# DESCRIPTION:
Imports:
dplyr (>= 1.0.0),
rlang (>= 1.0.0),
tidyr
Guarantees:
pkg::fun()Use for:
Usage pattern:
# Default: Use pkg::fun()
my_function <- function(x) {
dplyr::mutate(x, new_col = value * 2)
}
# Or import specific functions:
#' @importFrom dplyr mutate select filter
my_function <- function(x) {
x %>%
filter(value > 0) %>%
mutate(doubled = value * 2)
}
Optional packages for enhanced functionality, tests, or documentation.
# DESCRIPTION:
Suggests:
ggplot2,
testthat (>= 3.0.0),
knitr,
rmarkdown,
covr
No guarantees:
pkg::fun() without checkingUse for:
Usage pattern:
# MUST check availability:
my_plot <- function(data) {
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("Package 'ggplot2' required but not installed.\n",
"Install with: install.packages('ggplot2')",
call. = FALSE)
}
ggplot2::ggplot(data, ggplot2::aes(x, y)) +
ggplot2::geom_point()
}
# Better: Use rlang::check_installed()
my_plot <- function(data) {
rlang::check_installed("ggplot2", reason = "to create plots")
ggplot2::ggplot(data, ggplot2::aes(x, y)) +
ggplot2::geom_point()
}
Makes another package's functions available in user's workspace (rarely recommended).
# DESCRIPTION:
Depends:
R (>= 4.1.0),
methods
Effects:
Modern usage:
R (>= version) - minimum R version (ALWAYS use this)methods - if defining S4 classesWhy avoid:
# If you use Depends: dplyr:
library(mypackage) # Also attaches dplyr
# Now user's environment has all dplyr functions:
filter # Available (might conflict with stats::filter)
Better approach:
# Use Imports + explicit namespace:
Imports: dplyr
# In code:
dplyr::filter(...)
For packages with C/C++ code using headers from other packages.
# DESCRIPTION:
LinkingTo:
Rcpp,
RcppArmadillo
Use for:
Often combined with Imports:
Imports:
Rcpp (>= 1.0.0)
LinkingTo:
Rcpp
Dependencies for development tools, not package functionality.
# DESCRIPTION:
Config/Needs/website:
pkgdown
Config/Needs/coverage:
covr
Config/Needs/development:
devtools,
usethis,
roxygen2
Use for:
Not installed by default:
# Install with:
pak::pak("mypackage", dependencies = TRUE) # Includes Config/Needs/*
# Add to Imports:
usethis::use_package("dplyr")
usethis::use_package("rlang", min_version = "1.0.0")
# Add to Suggests:
usethis::use_package("ggplot2", type = "Suggests")
# Add to Imports with @importFrom:
usethis::use_import_from("dplyr", c("filter", "mutate", "select"))
# Adds to DESCRIPTION AND creates roxygen2 skeleton
# Add minimum R version:
usethis::use_package("R", min_version = "4.1.0", type = "Depends")
# DESCRIPTION:
Imports:
dplyr (>= 1.1.0),
rlang (>= 1.0.0),
tidyr,
purrr
Suggests:
ggplot2 (>= 3.4.0),
testthat (>= 3.0.0)
Version specifications:
dplyr # Any version
dplyr (>= 1.0.0) # At least 1.0.0
dplyr (>= 1.0.0, < 2.0.0) # Rarely used, not recommended
# No NAMESPACE imports needed
# Just use pkg::fun() everywhere:
my_function <- function(data) {
data %>%
dplyr::filter(value > 0) %>%
dplyr::mutate(doubled = value * 2) %>%
dplyr::select(id, doubled)
}
Advantages:
Disadvantages:
# Import specific functions:
#' @importFrom dplyr filter mutate select
#' @importFrom rlang .data .env
my_function <- function(data) {
data %>%
filter(value > 0) %>%
mutate(doubled = .data$value * 2) %>%
select(id, doubled)
}
When to use:
Where to put @importFrom:
# Option 1: In function documentation:
#' My function
#' @importFrom dplyr filter mutate
my_function <- function() { ... }
# Option 2: In package-level doc (R/mypackage-package.R):
#' @importFrom dplyr filter mutate select arrange
#' @importFrom rlang .data .env %||%
"_PACKAGE"
# Option 3: Dedicated imports file (R/aaa-imports.R):
#' @importFrom dplyr filter mutate select
#' @importFrom rlang .data %||%
NULL
#' @import rlang
Only for:
Avoid for most packages:
Always import operators:
# WRONG - doesn't work:
data %>% dplyr::filter(x > 0) # Error: %>% not found
# RIGHT:
#' @importFrom magrittr %>%
data %>% dplyr::filter(x > 0)
# Or use base pipe (R >= 4.1):
data |> dplyr::filter(x > 0) # No import needed
Common operators to import:
#' @importFrom magrittr %>%
#' @importFrom rlang %||% !! !!!
#' @importFrom data.table := .N .SD
# Imports dependencies - use pkg::fun() or @importFrom:
#' @importFrom dplyr filter
my_function <- function(data) {
filter(data, value > 0) # OK: imported
}
# Or:
my_function <- function(data) {
dplyr::filter(data, value > 0) # OK: explicit namespace
}
# Suggests dependencies - MUST check first:
my_optional_feature <- function(data) {
rlang::check_installed("ggplot2", reason = "for plotting")
ggplot2::ggplot(data, ggplot2::aes(x, y)) +
ggplot2::geom_point()
}
# Imports - can use freely:
#' @examples
#' my_function(mtcars)
# Suggests - must check or wrap:
#' @examples
#' \dontrun{
#' # Requires ggplot2
#' my_plot(mtcars)
#' }
#'
#' @examplesIf requireNamespace("ggplot2", quietly = TRUE)
#' my_plot(mtcars)
# Imports - can use freely:
test_that("function works", {
result <- my_function(data)
expect_equal(result$value, expected)
})
# Suggests - MUST skip if not available:
test_that("plotting works", {
skip_if_not_installed("ggplot2")
plot <- my_plot(data)
expect_s3_class(plot, "gg")
})
test_that("integration with optional package", {
skip_if_not_installed("dplyr")
library(dplyr)
result <- data %>%
my_transform() %>%
summarize(mean = mean(value))
expect_equal(result$mean, 5)
})
# YAML header for Suggests dependency:
# ---
# title: "My Vignette"
# vignette: >
# %\VignetteIndexEntry{My Vignette}
# %\VignetteEngine{knitr::rmarkdown}
# ---
# First chunk - setup with conditional evaluation:
# ```{r setup, include=FALSE}
# knitr::opts_chunk$set(
# eval = requireNamespace("ggplot2", quietly = TRUE)
# )
# ```
# Now code chunks only run if ggplot2 available:
# ```{r}
# library(ggplot2)
# ggplot(data, aes(x, y)) + geom_point()
# ```
Alternative: Use separate vignettes:
# DESCRIPTION:
Suggests:
knitr,
rmarkdown,
ggplot2
# vignettes/basic-usage.Rmd - no optional deps
# vignettes/advanced-plotting.Rmd - requires ggplot2
# References to Suggests packages:
#' @description
#' This function provides plotting capabilities. Requires the
#' \pkg{ggplot2} package to be installed.
#'
#' @seealso [ggplot2::ggplot()] for more plotting options
# Always specify if you need specific features:
Imports:
dplyr (>= 1.1.0), # Uses .by argument
rlang (>= 1.0.0), # Uses check_installed()
tidyr (>= 1.3.0) # Uses separate_wider_*
# Don't specify if any version works:
Imports:
jsonlite, # Basic read/write - any version fine
httr # Standard requests - any version fine
# Check when feature was introduced:
# 1. Check package NEWS file
# 2. Check function documentation
# 3. Look at GitHub releases/tags
# Use version where feature appeared:
usethis::use_package("dplyr", min_version = "1.1.0")
Always specify minimum R version:
# DESCRIPTION:
Depends:
R (>= 4.1.0)
Why specify:
How to choose:
# Conservative (wide compatibility):
R (>= 4.0.0)
# Modern (allows new features):
R (>= 4.1.0) # Native pipe, lambda syntax
# Latest stable:
R (>= 4.3.0)
Functions work differently with/without optional package:
my_function <- function(data, use_fast = TRUE) {
if (use_fast && requireNamespace("data.table", quietly = TRUE)) {
# Fast path with data.table:
dt <- data.table::as.data.table(data)
result <- dt[, .(mean = mean(value)), by = group]
return(as.data.frame(result))
}
# Fallback to base R:
aggregate(value ~ group, data, FUN = mean)
}
# In .onLoad() (R/zzz.R):
.onLoad <- function(libname, pkgname) {
# Register S3 method only if package available:
if (requireNamespace("dplyr", quietly = TRUE)) {
s3_register("dplyr::dplyr_reconstruct", "myclass")
}
}
# Helper function (from vctrs):
s3_register <- function(generic, class, method = NULL) {
stopifnot(is.character(generic), length(generic) == 1)
stopifnot(is.character(class), length(class) == 1)
pieces <- strsplit(generic, "::")[[1]]
stopifnot(length(pieces) == 2)
package <- pieces[[1]]
generic <- pieces[[2]]
caller <- parent.frame()
get_method_env <- function() {
top <- topenv(caller)
if (isNamespace(top)) {
asNamespace(environmentName(top))
} else {
caller
}
}
get_method <- function(method, env) {
if (is.null(method)) {
get(paste0(generic, ".", class), envir = get_method_env())
} else {
method
}
}
method_fn <- get_method(method)
stopifnot(is.function(method_fn))
setHook(
packageEvent(package, "onLoad"),
function(...) {
ns <- asNamespace(package)
method_name <- paste0(generic, ".", class)
env <- get_method_env()
registerS3method(generic, class, method_fn, envir = ns)
}
)
if (!isNamespaceLoaded(package)) {
return(invisible())
}
envir <- asNamespace(package)
if (exists(generic, envir)) {
registerS3method(generic, class, method_fn, envir = envir)
}
invisible()
}
# Check if optional features available:
has_plotting <- function() {
requireNamespace("ggplot2", quietly = TRUE)
}
has_fast_processing <- function() {
requireNamespace("data.table", quietly = TRUE)
}
# Inform users:
package_capabilities <- function() {
list(
plotting = has_plotting(),
fast_processing = has_fast_processing()
)
}
#' @examples
#' package_capabilities()
#' # $plotting
#' # [1] TRUE
#' # $fast_processing
#' # [1] FALSE
# Gradually move from Imports to Suggests:
# Version 1.0.0:
# Imports: oldpkg
my_function <- function() {
oldpkg::old_way()
}
# Version 1.1.0:
# Imports: oldpkg
my_function <- function() {
lifecycle::deprecate_soft(
"1.1.0", "my_function()",
details = "oldpkg will become optional in next version"
)
oldpkg::old_way()
}
# Version 2.0.0:
# Suggests: oldpkg (moved from Imports)
my_function <- function(use_old = FALSE) {
if (use_old) {
lifecycle::deprecate_warn(
"2.0.0", "my_function(use_old)",
details = "Old method will be removed in next version"
)
rlang::check_installed("oldpkg")
return(oldpkg::old_way())
}
new_way()
}
# Version 3.0.0:
# (oldpkg removed entirely)
my_function <- function() {
new_way()
}
Problem: Package in Imports but functions not accessible.
# DESCRIPTION:
Imports: dplyr
# Code:
my_function <- function(data) {
filter(data, x > 0) # ERROR: object 'filter' not found
}
# Fix:
my_function <- function(data) {
dplyr::filter(data, x > 0)
}
Problem: Assumes suggested package is installed.
# DESCRIPTION:
Suggests: ggplot2
# WRONG:
my_plot <- function(data) {
ggplot2::ggplot(data, ggplot2::aes(x, y)) # Fails if not installed
}
# RIGHT:
my_plot <- function(data) {
rlang::check_installed("ggplot2")
ggplot2::ggplot(data, ggplot2::aes(x, y))
}
# AVOID:
Depends: dplyr
# PREFER:
Imports: dplyr
# And use dplyr::filter() or @importFrom
# RISKY:
Imports: dplyr
my_function <- function(data) {
dplyr::filter(data, x > 0, .by = group) # .by added in 1.1.0!
}
# SAFER:
Imports: dplyr (>= 1.1.0)
# AVOID:
#' @import dplyr
#' @import tidyr
#' @import stringr
# PREFER:
#' @importFrom dplyr filter mutate select
#' @importFrom tidyr pivot_longer
#' @importFrom stringr str_detect
# WRONG:
test_that("optional feature works", {
library(ggplot2) # Fails if not installed
# ...
})
# RIGHT:
test_that("optional feature works", {
skip_if_not_installed("ggplot2")
library(ggplot2)
# ...
})
# CLUNKY:
if (!requireNamespace("pkg", quietly = TRUE)) {
stop("Please install pkg")
}
# BETTER:
rlang::check_installed(
"pkg",
reason = "to use this feature"
)
# Provides helpful install message
# Package A depends on Package B
# Package B depends on Package A
# = CIRCULAR DEPENDENCY (not allowed!)
# Solution: Extract shared code to third package C
# Both A and B depend on C
# WRONG - puts dev tools in Suggests:
Suggests:
testthat,
devtools,
usethis,
pkgdown,
covr
# RIGHT - separates dev tools:
Suggests:
testthat
Config/Needs/website:
pkgdown
Config/Needs/development:
devtools,
usethis
Config/Needs/coverage:
covr
# DESCRIPTION:
Suggests: ggplot2
# WRONG - even with requireNamespace():
if (requireNamespace("ggplot2", quietly = TRUE)) {
ggplot(data) # ERROR: ggplot not found
}
# RIGHT:
if (requireNamespace("ggplot2", quietly = TRUE)) {
ggplot2::ggplot(data) # OK: explicit namespace
}
# 1. Add to DESCRIPTION:
usethis::use_package("dplyr") # Imports
usethis::use_package("ggplot2", "Suggests") # Suggests
# 2. Use in code:
# Imports - either:
dplyr::filter(...) # Explicit (recommended)
# Or:
#' @importFrom dplyr filter
filter(...) # Imported
# Suggests - always check:
rlang::check_installed("ggplot2")
ggplot2::ggplot(...)
# 3. In tests:
skip_if_not_installed("ggplot2")
# 4. In examples:
#' @examplesIf requireNamespace("ggplot2", quietly = TRUE)
# 5. Document minimum versions:
Imports: dplyr (>= 1.1.0)
Package: mypackage
Version: 0.1.0
Depends:
R (>= 4.1.0)
Imports:
dplyr (>= 1.1.0),
rlang (>= 1.0.0),
tidyr (>= 1.3.0)
Suggests:
ggplot2 (>= 3.4.0),
testthat (>= 3.0.0),
knitr,
rmarkdown
Config/Needs/website:
pkgdown
Config/Needs/development:
devtools,
usethis
# Package-level imports (R/mypackage-package.R):
#' @importFrom rlang .data .env %||% !! !!!
#' @importFrom magrittr %>%
#' @importFrom dplyr filter mutate select arrange
"_PACKAGE"
# Or use base pipe (R >= 4.1):
# No import needed for |>