Agent skill
R
Execute these commands after EVERY implementation (see AGENT_AUTOMATION module for full workflow).
Install this agent skill to your Project
npx add-skill https://github.com/hivellm/rulebook/tree/main/templates/skills/languages/r
SKILL.md
R Project Rules
Agent Automation Commands
CRITICAL: Execute these commands after EVERY implementation (see AGENT_AUTOMATION module for full workflow).
# Complete quality check sequence:
Rscript -e 'styler::style_pkg(dry="on")' # Format check
Rscript -e 'lintr::lint_package()' # Linting
R CMD check . # Full check
Rscript -e 'devtools::test()' # All tests
# Security audit:
Rscript -e 'pak::pkg_deps_explain()' # Dependency check
R Configuration
CRITICAL: Use R 4.2+ with modern package development tools.
- Version: R 4.2+
- Recommended: R 4.3+
- Style Guide: tidyverse style guide
- Testing: testthat 3.x
- Documentation: roxygen2
DESCRIPTION File Requirements
Package: yourpackage
Type: Package
Title: Your Package Title
Version: 0.1.0
Author: Your Name
Maintainer: Your Name <you@example.com>
Description: A comprehensive description of what your package does.
License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
Depends:
R (>= 4.2)
Imports:
dplyr (>= 1.1.0),
ggplot2 (>= 3.4.0)
Suggests:
testthat (>= 3.0.0),
knitr,
rmarkdown
VignetteBuilder: knitr
Code Quality Standards
Mandatory Quality Checks
CRITICAL: After implementing ANY feature, you MUST run these commands in order.
IMPORTANT: These commands MUST match your GitHub Actions workflows to prevent CI/CD failures!
# Pre-Commit Checklist (MUST match .github/workflows/*.yml)
# 1. Format check (matches workflow - use dry="on"!)
Rscript -e "styler::style_pkg(dry = 'on')"
# 2. Lint (MUST pass with no warnings - matches workflow)
Rscript -e "lintr::lint_package()"
# 3. Run all tests (MUST pass 100% - matches workflow)
Rscript -e "devtools::test()"
# 4. Check package (matches workflow - strict CRAN checks)
R CMD build .
R CMD check *.tar.gz --as-cran --no-manual
# 5. Check coverage (MUST meet threshold)
Rscript -e "covr::package_coverage()"
# 6. Build documentation (matches workflow)
Rscript -e "devtools::document()"
# If ANY fails: ❌ DO NOT COMMIT - Fix first!
If ANY of these fail, you MUST fix the issues before committing.
Why This Matters:
- Running different commands locally than in CI causes "works on my machine" failures
- CI/CD workflows will fail if commands don't match
- Example: Using
styler::style_pkg()locally butdry='on'in CI = failure - Example: Missing
--as-cranflag = CRAN submission fails - Example: Skipping lintr locally = CI catches style violations
Formatting
- Use
stylerfor consistent code style - Based on tidyverse style guide
- Configuration in
.lintrfile
Example .lintr:
linters: linters_with_defaults(
line_length_linter(100),
object_name_linter = NULL,
cyclocomp_linter(25)
)
exclusions: list(
"tests/testthat.R"
)
Documentation
- Use roxygen2 for function documentation
- All exported functions must be documented
- Include examples in documentation
Example roxygen2 documentation:
#' Process Data
#'
#' This function processes input data and returns cleaned results.
#'
#' @param data A data.frame with input data
#' @param threshold Numeric threshold value (default: 0.5)
#' @param verbose Logical; if TRUE, print progress messages
#'
#' @return A data.frame with processed data
#' @export
#'
#' @examples
#' data <- data.frame(x = 1:10, y = rnorm(10))
#' result <- process_data(data, threshold = 0.7)
process_data <- function(data, threshold = 0.5, verbose = FALSE) {
if (!is.data.frame(data)) {
stop("data must be a data.frame")
}
if (verbose) {
message("Processing data...")
}
# Implementation
return(data)
}
Testing
- Framework: testthat 3.x
- Location:
/tests/testthatdirectory - Coverage: Must meet threshold (80%+)
- File naming:
test-*.Rfor test files
Example test structure:
# tests/testthat/test-process.R
test_that("process_data handles valid input", {
data <- data.frame(x = 1:5, y = 1:5)
result <- process_data(data)
expect_s3_class(result, "data.frame")
expect_equal(nrow(result), 5)
})
test_that("process_data errors on invalid input", {
expect_error(
process_data("not a dataframe"),
"data must be a data.frame"
)
})
test_that("process_data respects threshold parameter", {
data <- data.frame(x = 1:10, y = rnorm(10))
result <- process_data(data, threshold = 0.7)
expect_true(all(result$y >= 0.7 | result$y <= -0.7))
})
Package Development
Package Structure
yourpackage/
├── DESCRIPTION # Package metadata
├── NAMESPACE # Auto-generated by roxygen2
├── LICENSE # License file
├── README.md # Package overview
├── NEWS.md # Changelog
├── R/
│ ├── package.R # Package-level documentation
│ ├── data.R # Data documentation
│ └── functions.R # Function implementations
├── man/ # Auto-generated documentation
├── tests/
│ ├── testthat.R
│ └── testthat/
│ ├── test-functions.R
│ └── test-data.R
├── data/ # Package data
├── inst/ # Installed files
└── vignettes/ # Long-form documentation
Development Workflow
# Load package for development
devtools::load_all()
# Run tests
devtools::test()
# Check package
devtools::check()
# Generate documentation
devtools::document()
# Build package
devtools::build()
# Install locally
devtools::install()
Dependencies
CRAN vs GitHub Packages
# From CRAN
install.packages("dplyr")
# From GitHub
devtools::install_github("tidyverse/dplyr")
# Specify in DESCRIPTION:
Imports:
dplyr (>= 1.1.0)
Remotes:
github::tidyverse/dplyr@main
Best Practices
DO's ✅
- USE tidyverse principles
- DOCUMENT all exported functions
- TEST all functionality
- CHECK inputs with
stopifnot()or custom validation - RETURN consistent types
- NAMESPACE use explicit
::for external functions - VECTORIZE operations when possible
DON'Ts ❌
- NEVER use
TorF(useTRUEandFALSE) - NEVER use
attach()or<<-in packages - NEVER modify global options
- NEVER use
library()inside functions - NEVER leave
browser()orprint()debug code - NEVER assume working directory
Example code style:
# ✅ GOOD: Proper function structure
process_data <- function(data, threshold = 0.5) {
# Input validation
stopifnot(
is.data.frame(data),
is.numeric(threshold),
threshold >= 0, threshold <= 1
)
# Use explicit namespace
result <- dplyr::filter(data, value > threshold)
return(result)
}
# ❌ BAD: Poor practices
process_data <- function(data, threshold = 0.5) {
library(dplyr) # DON'T load packages in functions!
attach(data) # NEVER use attach()!
result <- filter(data, value > threshold) # Unclear where filter comes from
result <<- result # DON'T use global assignment!
print(result) # Don't print inside functions
}
CI/CD Requirements
Must include GitHub Actions workflows:
-
Testing (
r-test.yml):- Test on ubuntu-latest, windows-latest, macos-latest
- R versions: release, devel
- Use R CMD check --as-cran
- Upload coverage to Codecov
-
Linting (
r-lint.yml):- Run lintr checks
- Check style with styler
- Verify roxygen2 documentation
-
CRAN Check (
r-cran.yml):- R CMD check with --as-cran
- Verify no NOTEs, WARNINGs, or ERRORs
Publishing to CRAN
Pre-Submission Checklist
- ✅ All tests passing
- ✅ R CMD check --as-cran passes with 0 NOTEs
- ✅ Package documentation complete
- ✅ NEWS.md updated
- ✅ README.md current
- ✅ Examples run successfully
- ✅ Vignettes build correctly
- ✅ Valid LICENSE file
- ✅ No submission policy violations
Submission Process
# 1. Final check
devtools::check(cran = TRUE)
# 2. Build package
devtools::build()
# 3. Submit to CRAN
devtools::submit_cran()
# 4. Respond to CRAN feedback promptly
Didn't find tool you were looking for?