wf manages the building blocks of AI coding agents: skills, agents, rules, and hooks. Each type lives in a conventional directory (e.g. .claude/skills/ for Claude Code skills) and can be installed from GitHub or a local path, then tracked and updated through a lock file.
| Type | Directory | Description |
|---|---|---|
| Skills | .claude/skills/ |
Reusable instruction sets that extend what an agent can do |
| Agents | .claude/agents/ |
Custom subagent definitions with their own system prompts |
| Rules | .claude/rules/ |
Coding guidelines applied automatically to every session |
| Hooks | .claude/hooks/ |
Lifecycle scripts that run on agent events (e.g. pre-tool-use) |
Installation
To install the stable version on CRAN:
install.package('wf')To install the development version:
# install.packages('pak')
pak::pak('christopherkenny/wf')Setup
Set WF_AGENT in your .Renviron so every wf function knows which agent you use:
usethis::edit_r_environ()Add a line like:
WF_AGENT='claude_code'Supported agents: 'claude_code' (alias: 'claude'), 'openclaw', 'codex', 'cursor', 'gemini_cli', 'github_copilot' (alias: 'copilot'), 'posit_ai' (alias: 'posit').
Each type has its own *_path() function for looking up the conventional directory:
library(wf)
skill_path('claude_code', 'project')
#> [1] ".claude/skills"
agent_path('claude_code', 'project')
#> [1] ".claude/agents"
rule_path('claude_code', 'project')
#> [1] ".claude/rules"
hook_path('claude_code', 'project')
#> [1] ".claude/hooks"All path functions also accept scope = 'global' for user-wide installation, and auto-detect the agent from WF_AGENT when called without arguments.
Skills
Install a skill from GitHub using its owner/repo shorthand or a full URL:
add_skill('some-user/some-skill')For repos that bundle multiple skills under a skills/ subdirectory, use the skill argument:
add_skill('some-user/skills', skill = 'proofread')List, check, and update installed skills:
list_skills()
#> name
#> .claude/skills/cran-extrachecks cran-extrachecks
#> .claude/skills/tidy-argument-checking types-check
#> .claude/skills/tidy-deprecate-function tidy-deprecate-function
#> description
#> .claude/skills/cran-extrachecks Prepare R packages for CRAN submission by checking for common ad-hoc requirements not caught by devtools::check(). Use when: (1) Preparing a package for first CRAN release, (2) Preparing a package update for CRAN resubmission, (3) Reviewing a package to ensure CRAN compliance, (4) Responding to CRAN reviewer feedback. Covers documentation requirements, DESCRIPTION field standards, URL validation, examples, and administrative requirements.\n
#> .claude/skills/tidy-argument-checking Validate function inputs in R using a standalone file of check_* functions. Use when writing exported R functions that need input validation, reviewing existing validation code, or when creating new input validation helpers.
#> .claude/skills/tidy-deprecate-function Guide for deprecating R functions/arguments. Use when a user asks to deprecate a function or parameter, including adding lifecycle warnings, updating documentation, adding NEWS entries, and updating tests.
#> source
#> .claude/skills/cran-extrachecks https://github.com/posit-dev/skills/tree/HEAD/r-lib/cran-extrachecks
#> .claude/skills/tidy-argument-checking <NA>
#> .claude/skills/tidy-deprecate-function <NA>
#> installed_at
#> .claude/skills/cran-extrachecks 2026-03-11T13:56:50Z
#> .claude/skills/tidy-argument-checking <NA>
#> .claude/skills/tidy-deprecate-function <NA>
check_skills()
#> name installed_sha
#> 1 cran-extrachecks bf0fc0d480209a2f5f7fdf32eabb9c7546e53ee5
#> latest_sha update_available
#> 1 bf0fc0d480209a2f5f7fdf32eabb9c7546e53ee5 FALSE
update_skills()
#> All skills are up to date.Search GitHub for community skills or scaffold a new one:
find_skill('quarto')
#> name description owner
#> 1 skills [WIP] Claude Skills for Political Scientists christopherkenny
#> url stars
#> 1 https://github.com/christopherkenny/skills 3
tmp <- tempfile()
init_skill('my-skill', tmp)
#> Created skill "my-skill" at
#> 'C:/Users/chris/AppData/Local/Temp/RtmpScozzt/file39c824a4155b/my-skill'.Agents
Custom subagents are single .md files with YAML frontmatter. Install one from GitHub:
add_agent('some-user/some-agent')For multi-agent repos, use the agent argument:
add_agent('some-user/agents', agent = 'code-reviewer')Create a local template to get started:
tmp <- tempfile()
init_agent('my-agent', tmp)
#> Created agent "my-agent" at
#> 'C:/Users/chris/AppData/Local/Temp/RtmpScozzt/file39c84f5d7173/my-agent.md'.Rules
Rules are Markdown files that set coding guidelines for every session. Install from GitHub:
add_rule('some-user/some-rule')Create a local template:
Hooks
Hooks are executable scripts (.sh, .R, etc.) that run on agent lifecycle events. Installing a hook copies the script to .claude/hooks/ and registers it in settings.json.
add_hook('some-user/some-hook', event = 'PreToolUse')To register a bare command without installing a script file:
register_hook('PreToolUse', 'echo hello')List registered hooks or create a new script template:
list_hooks()
#> [1] event matcher command file
#> <0 rows> (or 0-length row.names)
tmp <- tempfile()
init_hook('my-hook', tmp)
#> Created hook "my-hook" at
#> 'C:/Users/chris/AppData/Local/Temp/RtmpScozzt/file39c866845bcc/my-hook.sh'.
#> ℹ Register it with `register_hook(event, command =
#> "C:/Users/chris/AppData/Local/Temp/RtmpScozzt/file39c866845bcc/my-hook.sh")`