Skip to contents

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:

tmp <- tempfile()
init_rule('my-rule', tmp)
#> Created rule "my-rule" at
#> 'C:/Users/chris/AppData/Local/Temp/RtmpScozzt/file39c8416e70da/my-rule.md'.

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")`