Agent skill

R

Execute these commands after EVERY implementation (see AGENT_AUTOMATION module for full workflow).

Stars 10
Forks 1

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).

bash
# 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!

bash
# 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 but dry='on' in CI = failure
  • Example: Missing --as-cran flag = CRAN submission fails
  • Example: Skipping lintr locally = CI catches style violations

Formatting

  • Use styler for consistent code style
  • Based on tidyverse style guide
  • Configuration in .lintr file

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:

r
#' 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/testthat directory
  • Coverage: Must meet threshold (80%+)
  • File naming: test-*.R for test files

Example test structure:

r
# 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

r
# 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

r
# 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 T or F (use TRUE and FALSE)
  • NEVER use attach() or <<- in packages
  • NEVER modify global options
  • NEVER use library() inside functions
  • NEVER leave browser() or print() debug code
  • NEVER assume working directory

Example code style:

r
# ✅ 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:

  1. 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
  2. Linting (r-lint.yml):

    • Run lintr checks
    • Check style with styler
    • Verify roxygen2 documentation
  3. 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

r
# 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?

Be as detailed as possible for better results