From ae6d819efa22711910f7de9546971035529aa7d0 Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Fri, 14 Feb 2025 16:07:38 -0600 Subject: [PATCH 01/13] add aspirational installation instructions --- README.Rmd | 6 ++++++ README.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/README.Rmd b/README.Rmd index 3610395..cc761cf 100644 --- a/README.Rmd +++ b/README.Rmd @@ -31,6 +31,12 @@ Getting started with chores takes three steps. **1)** Install the chores package like so: +``` r +install.packages("chores") +``` + +You can install the developmental version with: + ``` r pak::pak("simonpcouch/chores") ``` diff --git a/README.md b/README.md index 7560745..f212843 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,12 @@ Getting started with chores takes three steps. **1)** Install the chores package like so: +``` r +install.packages("chores") +``` + +You can install the developmental version with: + ``` r pak::pak("simonpcouch/chores") ``` From 747375d607a6b086aa491ae8a61141ee45e6783d Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Fri, 14 Feb 2025 16:09:11 -0600 Subject: [PATCH 02/13] proofread DESCRIPTION --- DESCRIPTION | 6 +++--- man/chores-package.Rd | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index fbcf9ae..7cd4d5f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -9,9 +9,9 @@ Authors@R: c( Description: Provides a collection of ergonomic large language model assistants designed to help you complete repetitive, hard-to-automate tasks quickly. After selecting some code, press the keyboard shortcut you've chosen to - trigger the package app, select an assistant, and watch your code be - rewritten. While the package ships with a number of helpers for R package - development, users can create custom helpers just by writing some + trigger the package app, select an assistant, and watch your chore be + carried out. While the package ships with a number of chore helpers for R + package development, users can create custom helpers just by writing some instructions in a markdown file. License: MIT + file LICENSE Config/testthat/edition: 3 diff --git a/man/chores-package.Rd b/man/chores-package.Rd index b455638..cfa813c 100644 --- a/man/chores-package.Rd +++ b/man/chores-package.Rd @@ -8,7 +8,7 @@ \description{ \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} -Provides a collection of ergonomic large language model assistants designed to help you complete repetitive, hard-to-automate tasks quickly. After selecting some code, press the keyboard shortcut you've chosen to trigger the package app, select an assistant, and watch your code be rewritten. While the package ships with a number of helpers for R package development, users can create custom helpers just by writing some instructions in a markdown file. +Provides a collection of ergonomic large language model assistants designed to help you complete repetitive, hard-to-automate tasks quickly. After selecting some code, press the keyboard shortcut you've chosen to trigger the package app, select an assistant, and watch your chore be carried out. While the package ships with a number of chore helpers for R package development, users can create custom helpers just by writing some instructions in a markdown file. } \seealso{ Useful links: From 9323f8799d471626a0ee6916c840eafa5d9f4373 Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Fri, 14 Feb 2025 16:16:45 -0600 Subject: [PATCH 03/13] check that all exported functions have `@return` and `@examples` Also, unexports internal fn `.helper_add()`. --- NAMESPACE | 1 - R/directory.R | 8 +++++--- R/helper-add-remove.R | 1 - R/init-addin.R | 4 ++++ R/init-helper.R | 9 ++++++--- R/prompt.R | 5 +++-- man/directory.Rd | 7 ++++--- man/dot-init_addin.Rd | 5 +++++ man/dot-init_helper.Rd | 7 ++++--- man/prompt.Rd | 4 ++-- tests/testthat/_snaps/helper-init.md | 2 +- 11 files changed, 34 insertions(+), 19 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index f40a6cb..e9e959a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,7 +1,6 @@ # Generated by roxygen2: do not edit by hand S3method(print,helper_response) -export(.helper_add) export(.init_addin) export(.init_helper) export(directory_list) diff --git a/R/directory.R b/R/directory.R index 0215355..1a760b2 100644 --- a/R/directory.R +++ b/R/directory.R @@ -57,13 +57,15 @@ #' for more on adding your own helper prompts, sharing them with others, and #' using prompts from others. #' -#' @examplesIf FALSE +#' @examples #' # print out the current prompt directory -#' directory_get() +#' directory_path() #' #' # list out prompts currently in the directory #' directory_list() #' +#' # sets options and creates files, so don't run automatically: +#' \dontrun{ #' # create a prompt in the prompt directory #' prompt_new("boop", "replace") #' @@ -77,7 +79,7 @@ #' # these are equivalent: #' directory_set("some/folder") #' options(.chores_dir = "some/folder") -#' +#' } #' @export directory_load <- function(dir = directory_path()) { prompt_base_names <- directory_base_names(dir) diff --git a/R/helper-add-remove.R b/R/helper-add-remove.R index 6bb9a41..2279a72 100644 --- a/R/helper-add-remove.R +++ b/R/helper-add-remove.R @@ -24,7 +24,6 @@ #' is registered (or unregistered) with the chores package. #' #' @name helper_add_remove -#' @export .helper_add <- function( chore, prompt = NULL, diff --git a/R/init-addin.R b/R/init-addin.R index d64d752..5692bfd 100644 --- a/R/init-addin.R +++ b/R/init-addin.R @@ -13,6 +13,10 @@ #' `NULL`, invisibly. Called for the side effect of launching the chores addin #' and interfacing with selected text. #' +#' @examples +#' if (interactive()) { +#' .init_addin() +#' } #' @export .init_addin <- function() { if (is.null(fetch_chores_chat())) { diff --git a/R/init-helper.R b/R/init-helper.R index 50f4972..90bf1d4 100644 --- a/R/init-helper.R +++ b/R/init-helper.R @@ -15,9 +15,11 @@ #' so e.g. set `options(.chores_chat = ellmer::chat_claude())` in your #' `.Rprofile` to configure chores with ellmer every time you start a new R session. #' -#' @examplesIf FALSE +#' @examples +#' # requires an API key and sets options +#' \dontrun{ #' # to create a chat with claude: -#' .init_helper() +#' .init_helper(.chores_chat = ellmer::chat_claude()) #' #' # or with OpenAI's 4o-mini: #' .init_helper(.chores_chat = ellmer::chat_openai(model = "gpt-4o-mini")) @@ -28,6 +30,7 @@ #' options( #' .chores_chat = ellmer::chat_openai(model = "gpt-4o-mini") #' ) +#' } #' @export .init_helper <- function( chore = NULL, @@ -37,7 +40,7 @@ if (!chore %in% list_helpers()) { cli::cli_abort(c( "No helpers for chore {.arg {chore}} registered.", - "i" = "See {.fn .helper_add}." + "i" = "See {.fn prompt_new}." )) } diff --git a/R/prompt.R b/R/prompt.R index 0dd7d77..b36e850 100644 --- a/R/prompt.R +++ b/R/prompt.R @@ -32,7 +32,8 @@ #' Each `prompt_*()` function returns the file path to the created, edited, or #' removed filepath, invisibly. #' -#' @examplesIf FALSE +#' @examples +#' if (interactive()) { #' # create a new helper for chore `"boop"` that replaces the selected text: #' prompt_new("boop") #' @@ -66,7 +67,7 @@ #' "58c9b4da/summarize-prefix.md" #' ) #' ) -#' +#' } #' @name prompt #' @rdname prompt diff --git a/man/directory.Rd b/man/directory.Rd index 26919d4..cd49e77 100644 --- a/man/directory.Rd +++ b/man/directory.Rd @@ -73,13 +73,14 @@ the prompt directory, provide a \code{dir} argument to \code{directory_load()}. } \examples{ -\dontshow{if (FALSE) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} # print out the current prompt directory -directory_get() +directory_path() # list out prompts currently in the directory directory_list() +# sets options and creates files, so don't run automatically: +\dontrun{ # create a prompt in the prompt directory prompt_new("boop", "replace") @@ -93,7 +94,7 @@ directory_load() # these are equivalent: directory_set("some/folder") options(.chores_dir = "some/folder") -\dontshow{\}) # examplesIf} +} } \seealso{ The "Custom helpers" vignette, at \code{vignette("custom", package = "chores")}, diff --git a/man/dot-init_addin.Rd b/man/dot-init_addin.Rd index 57a273e..52c84d8 100644 --- a/man/dot-init_addin.Rd +++ b/man/dot-init_addin.Rd @@ -19,3 +19,8 @@ and/or register the addin with a shortcut via \verb{Tools > Modify Keyboard Shortcuts > Search "Chores"}--we suggest \code{Ctrl+Alt+C} (or \code{Ctrl+Cmd+C} on macOS). } +\examples{ +if (interactive()) { + .init_addin() +} +} diff --git a/man/dot-init_helper.Rd b/man/dot-init_helper.Rd index ee8fd28..deaa6dd 100644 --- a/man/dot-init_helper.Rd +++ b/man/dot-init_helper.Rd @@ -25,9 +25,10 @@ with \code{\link[=prompt_new]{prompt_new()}}. } } \examples{ -\dontshow{if (FALSE) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +# requires an API key and sets options +\dontrun{ # to create a chat with claude: -.init_helper() +.init_helper(.chores_chat = ellmer::chat_claude()) # or with OpenAI's 4o-mini: .init_helper(.chores_chat = ellmer::chat_openai(model = "gpt-4o-mini")) @@ -38,5 +39,5 @@ with \code{\link[=prompt_new]{prompt_new()}}. options( .chores_chat = ellmer::chat_openai(model = "gpt-4o-mini") ) -\dontshow{\}) # examplesIf} +} } diff --git a/man/prompt.Rd b/man/prompt.Rd index ba7aad0..2820415 100644 --- a/man/prompt.Rd +++ b/man/prompt.Rd @@ -51,7 +51,7 @@ Load the prompts you create with these functions using \code{\link[=directory_lo (which is automatically called when the package loads). } \examples{ -\dontshow{if (FALSE) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +if (interactive()) { # create a new helper for chore `"boop"` that replaces the selected text: prompt_new("boop") @@ -85,7 +85,7 @@ prompt_new( "58c9b4da/summarize-prefix.md" ) ) -\dontshow{\}) # examplesIf} +} } \seealso{ The \link{directory} help-page for more on working with prompts in diff --git a/tests/testthat/_snaps/helper-init.md b/tests/testthat/_snaps/helper-init.md index e089ad4..a595ab2 100644 --- a/tests/testthat/_snaps/helper-init.md +++ b/tests/testthat/_snaps/helper-init.md @@ -45,5 +45,5 @@ Condition Error in `.init_helper()`: ! No helpers for chore `beepBopBoop` registered. - i See `.helper_add()`. + i See `prompt_new()`. From 02f663f18841332ba409e934118d69f385eb2a1d Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Fri, 14 Feb 2025 16:19:14 -0600 Subject: [PATCH 04/13] `urlchecker::url_check()` --- R/doc-helper-roxygen.R | 2 +- README.Rmd | 2 +- README.md | 4 +--- man/roxygen_helper.Rd | 2 +- vignettes/custom.Rmd | 4 ++-- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/R/doc-helper-roxygen.R b/R/doc-helper-roxygen.R index df7f5c9..84f5a3c 100644 --- a/R/doc-helper-roxygen.R +++ b/R/doc-helper-roxygen.R @@ -32,7 +32,7 @@ #' @section Gallery: #' #' This section includes a handful of examples -#' "[from the wild](https://github.com/hadley/elmer/tree/e497d627e7be01206df6f1420ca36235141dc22a/R)" +#' "[from the wild](https://github.com/tidyverse/ellmer/tree/e497d627e7be01206df6f1420ca36235141dc22a/R)" #' and are generated with the recommended model, Claude Sonnet 3.5. #' #' Documenting a function factory: diff --git a/README.Rmd b/README.Rmd index cc761cf..f919874 100644 --- a/README.Rmd +++ b/README.Rmd @@ -85,7 +85,7 @@ As-is, the package provides ergonomic LLM assistants for R package development: Users have also contributed a number of helpers for a wide range of tasks--see `vignette("gallery", package = "chores")` for a gallery of user-contributed helpers! -That said, all you need to create your own chore helper is a markdown file with some instructions on how you'd like it to work. See `prompt_new()` and `directory_load()` for more information, and [chorespable](https://github.com/simonpcouch/chorespable) for an example chores extension package. +That said, all you need to create your own chore helper is a markdown file with some instructions on how you'd like it to work. See `prompt_new()` and `directory_load()` for more information, and [palpable](https://github.com/simonpcouch/palpable) for an example chores extension package. ## How much do helpers cost? diff --git a/README.md b/README.md index f212843..395a975 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,6 @@ [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental) -[![CRAN -status](https://www.r-pkg.org/badges/version/chores)](https://CRAN.R-project.org/package=chores) [![R-CMD-check](https://github.com/simonpcouch/chores/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/simonpcouch/chores/actions/workflows/R-CMD-check.yaml) @@ -104,7 +102,7 @@ user-contributed helpers! That said, all you need to create your own chore helper is a markdown file with some instructions on how you’d like it to work. See `prompt_new()` and `directory_load()` for more information, and -[chorespable](https://github.com/simonpcouch/chorespable) for an example +[palpable](https://github.com/simonpcouch/palpable) for an example chores extension package. ## How much do helpers cost? diff --git a/man/roxygen_helper.Rd b/man/roxygen_helper.Rd index c3e4814..695c735 100644 --- a/man/roxygen_helper.Rd +++ b/man/roxygen_helper.Rd @@ -37,7 +37,7 @@ valid documentation entries, and it would cost around This section includes a handful of examples -"\href{https://github.com/hadley/elmer/tree/e497d627e7be01206df6f1420ca36235141dc22a/R}{from the wild}" +"\href{https://github.com/tidyverse/ellmer/tree/e497d627e7be01206df6f1420ca36235141dc22a/R}{from the wild}" and are generated with the recommended model, Claude Sonnet 3.5. Documenting a function factory: diff --git a/vignettes/custom.Rmd b/vignettes/custom.Rmd index e677fb0..35753c2 100644 --- a/vignettes/custom.Rmd +++ b/vignettes/custom.Rmd @@ -72,9 +72,9 @@ chores extension packages allow you to more flexibly share helper prompts with o * Place one markdown file per new chore helper in `inst/prompts/`. This folder will take the same format as the prompt directory described above. * Place a call to `chores::directory_load()` in the package's `.onLoad()`, referencing the extension package's `system.file("prompts", package = "yourpackage")`. This will automatically register your package's prompts with chores when the extension is loaded. -For an example chores extension, see [simonpcouch/chorespable](https://github.com/simonpcouch/chorespable). +For an example chores extension, see [simonpcouch/palpable](https://github.com/simonpcouch/palpable). -chores extension packages also allow you to document what your helpers are for and how they tend to behave in context; situate your documentation files at `?chore_helper`, replacing `chore` with your new chore helper's name. Then, with your package loaded, users can view a high-level description of the helper's behavior and a gallery of examples. See `?cli_helper` for an example helper help-page, with source code [here](https://github.com/simonpcouch/chores/blob/main/R/doc-chores-cli.R). +chores extension packages also allow you to document what your helpers are for and how they tend to behave in context; situate your documentation files at `?chore_helper`, replacing `chore` with your new chore helper's name. Then, with your package loaded, users can view a high-level description of the helper's behavior and a gallery of examples. See `?cli_helper` for an example helper help-page, with source code [here](https://github.com/simonpcouch/chores/blob/main/R/doc-helper-cli.R). ## Using others' custom helpers From 6bfd56397eef9270a4d7bb6c7c45084759fa50ac Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Fri, 14 Feb 2025 16:27:59 -0600 Subject: [PATCH 05/13] proofread --- _pkgdown.yml | 1 - vignettes/chores.Rmd | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/_pkgdown.yml b/_pkgdown.yml index 1763161..754a0ce 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -20,6 +20,5 @@ reference: - cli_helper - title: Developer Infrastructure contents: - - helper_add_remove - .init_helper - .init_addin diff --git a/vignettes/chores.Rmd b/vignettes/chores.Rmd index 84da9d7..e5d1467 100644 --- a/vignettes/chores.Rmd +++ b/vignettes/chores.Rmd @@ -22,7 +22,7 @@ The chores package ships with a number of pre-engineered "chore helpers." A chor The chores addin supports any model supported by [ellmer](https://ellmer.tidyverse.org/). When choosing a model for use with chores, you'll want to the use the most performant model possible that satisfies your privacy needs; chores automatically passes along your selected code to your chosen model, so it's especially important to consider data privacy when using LLMs with chores. -chores uses the `.chores_chat` option to configure which model powers the addin. `.chores_chat` should be set to an ellmer Chat object. For example, to use OpenAI's GPT-4o-mini, you might write `options(.chores_chat = ellmer::chat_claude())`. Paste that code in your `.Rprofile` via `usethis::edit_r_profile()` to always use the same model every time you start an R session. +chores uses the `.chores_chat` option to configure which model powers the addin. `.chores_chat` should be set to an ellmer Chat object. For example, to use Anthropic's Claude, you might write `options(.chores_chat = ellmer::chat_claude())`. Paste that code in your `.Rprofile` via `usethis::edit_r_profile()` to always use the same model every time you start an R session. If you're using ellmer inside a organization, you'll be limited to what your IT department allows, which is likely to be one provided by a big cloud provider, e.g. `chat_azure()`, `chat_bedrock()`, `chat_databricks()`, or `chat_snowflake()`. If you're using ellmer for your own exploration, you'll have a lot more freedom, so we have a few recommendations to help you get started: @@ -44,6 +44,7 @@ Just: * Watch your code be rewritten. ```{r} +#| echo: false #| fig-alt: "A screencast of an RStudio session. An .R file is open in the editor with a function definition. The user selects various subsets of the function and, after selecting from a dropdown menu, the helper assistant converts erroring code and drafts function documentation." knitr::include_graphics("https://raw.githubusercontent.com/simonpcouch/chores/refs/heads/main/inst/figs/addin.gif") ``` From 4692db349b948cc6b608342893cf6a761c19e6e7 Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Fri, 14 Feb 2025 16:32:55 -0600 Subject: [PATCH 06/13] unlist `helper_add_remove` topic from docs --- R/helper-add-remove.R | 54 ++++++++++++++++++++-------------------- R/prompt.R | 7 +++++- man/helper_add_remove.Rd | 38 ---------------------------- 3 files changed, 33 insertions(+), 66 deletions(-) delete mode 100644 man/helper_add_remove.Rd diff --git a/R/helper-add-remove.R b/R/helper-add-remove.R index 2279a72..3daf98d 100644 --- a/R/helper-add-remove.R +++ b/R/helper-add-remove.R @@ -1,29 +1,29 @@ -#' Registering helpers -#' -#' @description -#' Users can create custom helpers using the `.helper_add()` function; after passing -#' the function a chore and prompt, the helper will be available in the -#' [chores addin][.init_addin]. -#' -#' **Most users should not need to interact with these functions.** -#' [prompt_new()] and friends can be used to create prompts for new helpers, and -#' those helpers can be registered with chores using [directory_load()] and friends. -#' The helpers created by those functions will be persistent across sessions. -#' -#' @param chore A single string giving a descriptor of the helper's functionality. -#' Cand only contain letters and numbers. -#' @param prompt A single string giving the system prompt. In most cases, this -#' is a rather long string, containing several newlines. -#' @param interface One of `"replace"`, `"prefix"`, or `"suffix"`, describing -#' how the helper will interact with the selection. For example, the -#' [cli helper][cli_helper] `"replace"`s the selection, while the -#' [roxygen helper][roxygen_helper] `"prefixes"` the selected code with documentation. -#' -#' @returns -#' `NULL`, invisibly. Called for its side effect: a helper for chore `chore` -#' is registered (or unregistered) with the chores package. -#' -#' @name helper_add_remove +# Registering helpers +# +# @description +# Users can create custom helpers using the `.helper_add()` function; after passing +# the function a chore and prompt, the helper will be available in the +# [chores addin][.init_addin]. +# +# **Most users should not need to interact with these functions.** +# [prompt_new()] and friends can be used to create prompts for new helpers, and +# those helpers can be registered with chores using [directory_load()] and friends. +# The helpers created by those functions will be persistent across sessions. +# +# @param chore A single string giving a descriptor of the helper's functionality. +# Cand only contain letters and numbers. +# @param prompt A single string giving the system prompt. In most cases, this +# is a rather long string, containing several newlines. +# @param interface One of `"replace"`, `"prefix"`, or `"suffix"`, describing +# how the helper will interact with the selection. For example, the +# [cli helper][cli_helper] `"replace"`s the selection, while the +# [roxygen helper][roxygen_helper] `"prefixes"` the selected code with documentation. +# +# @returns +# `NULL`, invisibly. Called for its side effect: a helper for chore `chore` +# is registered (or unregistered) with the chores package. +# +# @name helper_add_remove .helper_add <- function( chore, prompt = NULL, @@ -38,7 +38,7 @@ invisible() } -#' @rdname helper_add_remove +# @rdname helper_add_remove .helper_remove <- function(chore) { check_string(chore) if (!chore %in% list_helpers()) { diff --git a/R/prompt.R b/R/prompt.R index b36e850..2aef10e 100644 --- a/R/prompt.R +++ b/R/prompt.R @@ -18,7 +18,12 @@ #' Load the prompts you create with these functions using [directory_load()] #' (which is automatically called when the package loads). #' -#' @inheritParams helper_add_remove +#' @param chore A single string giving a descriptor of the helper's functionality. +#' Cand only contain letters and numbers. +#' @param interface One of `"replace"`, `"prefix"`, or `"suffix"`, describing +#' how the helper will interact with the selection. For example, the +#' [cli helper][cli_helper] `"replace"`s the selection, while the +#' [roxygen helper][roxygen_helper] `"prefixes"` the selected code with documentation. #' @param contents Optional. Path to a markdown file with contents that will #' "pre-fill" the file. Anything file ending in `.md` or `.markdown` that can be #' read with `readLines()` is fair game; this could be a local file, a "raw" diff --git a/man/helper_add_remove.Rd b/man/helper_add_remove.Rd deleted file mode 100644 index 253ec41..0000000 --- a/man/helper_add_remove.Rd +++ /dev/null @@ -1,38 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/helper-add-remove.R -\name{helper_add_remove} -\alias{helper_add_remove} -\alias{.helper_add} -\alias{.helper_remove} -\title{Registering helpers} -\usage{ -.helper_add(chore, prompt = NULL, interface = c("replace", "prefix", "suffix")) - -.helper_remove(chore) -} -\arguments{ -\item{chore}{A single string giving a descriptor of the helper's functionality. -Cand only contain letters and numbers.} - -\item{prompt}{A single string giving the system prompt. In most cases, this -is a rather long string, containing several newlines.} - -\item{interface}{One of \code{"replace"}, \code{"prefix"}, or \code{"suffix"}, describing -how the helper will interact with the selection. For example, the -\link[=cli_helper]{cli helper} \code{"replace"}s the selection, while the -\link[=roxygen_helper]{roxygen helper} \code{"prefixes"} the selected code with documentation.} -} -\value{ -\code{NULL}, invisibly. Called for its side effect: a helper for chore \code{chore} -is registered (or unregistered) with the chores package. -} -\description{ -Users can create custom helpers using the \code{.helper_add()} function; after passing -the function a chore and prompt, the helper will be available in the -\link[=.init_addin]{chores addin}. - -\strong{Most users should not need to interact with these functions.} -\code{\link[=prompt_new]{prompt_new()}} and friends can be used to create prompts for new helpers, and -those helpers can be registered with chores using \code{\link[=directory_load]{directory_load()}} and friends. -The helpers created by those functions will be persistent across sessions. -} From f436d9cecee6aabf3457c6c627eeb795bf852716 Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Fri, 14 Feb 2025 16:36:09 -0600 Subject: [PATCH 07/13] remove references to unexported fn --- R/directory.R | 2 +- R/init-helper.R | 2 +- man/directory.Rd | 2 +- man/dot-init_helper.Rd | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R/directory.R b/R/directory.R index 1a760b2..ec97dc5 100644 --- a/R/directory.R +++ b/R/directory.R @@ -13,7 +13,7 @@ #' * `directory_list()` enumerates all of the different prompts that currently #' live in the directory (and provides clickable links to each). #' * `directory_load()` registers each of the prompts in the prompt -#' directory with the chores package (via [.helper_add()]). +#' directory with the chores package. #' #' [Functions prefixed with][prompt] `prompt*()` allow users to conveniently create, edit, #' and delete the prompts in chores' prompt directory. diff --git a/R/init-helper.R b/R/init-helper.R index 90bf1d4..63da6d3 100644 --- a/R/init-helper.R +++ b/R/init-helper.R @@ -9,7 +9,7 @@ #' #' @param chore The identifier for a helper prompt. By default one #' of `r glue::glue_collapse(paste0("[", glue::double_quote(default_chores), "]", "[", default_chores, "_helper", "]"), ", ", last = " or ")`, -#' though custom helpers can be added with [.helper_add()]. +#' though custom helpers can be added with [prompt_new()]. #' @param .chores_chat An ellmer Chat, e.g. #' `function() ellmer::chat_claude()`. Defaults to the option by the same name, #' so e.g. set `options(.chores_chat = ellmer::chat_claude())` in your diff --git a/man/directory.Rd b/man/directory.Rd index cd49e77..e92894d 100644 --- a/man/directory.Rd +++ b/man/directory.Rd @@ -32,7 +32,7 @@ the option \code{.chores_dir}). \item \code{directory_list()} enumerates all of the different prompts that currently live in the directory (and provides clickable links to each). \item \code{directory_load()} registers each of the prompts in the prompt -directory with the chores package (via \code{\link[=.helper_add]{.helper_add()}}). +directory with the chores package. } \link[=prompt]{Functions prefixed with} \verb{prompt*()} allow users to conveniently create, edit, diff --git a/man/dot-init_helper.Rd b/man/dot-init_helper.Rd index deaa6dd..287d8ce 100644 --- a/man/dot-init_helper.Rd +++ b/man/dot-init_helper.Rd @@ -9,7 +9,7 @@ \arguments{ \item{chore}{The identifier for a helper prompt. By default one of \link[=cli_helper]{"cli"}, \link[=testthat_helper]{"testthat"} or \link[=roxygen_helper]{"roxygen"}, -though custom helpers can be added with \code{\link[=.helper_add]{.helper_add()}}.} +though custom helpers can be added with \code{\link[=prompt_new]{prompt_new()}}.} \item{.chores_chat}{An ellmer Chat, e.g. \code{function() ellmer::chat_claude()}. Defaults to the option by the same name, From e6fb45d7cf02a111af4593e058b38315eea08c3d Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Fri, 14 Feb 2025 16:53:58 -0600 Subject: [PATCH 08/13] remove problematic URL --- R/doc-helper-roxygen.R | 3 +-- man/roxygen_helper.Rd | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/R/doc-helper-roxygen.R b/R/doc-helper-roxygen.R index 84f5a3c..9b79396 100644 --- a/R/doc-helper-roxygen.R +++ b/R/doc-helper-roxygen.R @@ -31,8 +31,7 @@ #' #' @section Gallery: #' -#' This section includes a handful of examples -#' "[from the wild](https://github.com/tidyverse/ellmer/tree/e497d627e7be01206df6f1420ca36235141dc22a/R)" +#' This section includes a handful of examples "from the wild" #' and are generated with the recommended model, Claude Sonnet 3.5. #' #' Documenting a function factory: diff --git a/man/roxygen_helper.Rd b/man/roxygen_helper.Rd index 695c735..5c5bece 100644 --- a/man/roxygen_helper.Rd +++ b/man/roxygen_helper.Rd @@ -36,8 +36,7 @@ valid documentation entries, and it would cost around \section{Gallery}{ -This section includes a handful of examples -"\href{https://github.com/tidyverse/ellmer/tree/e497d627e7be01206df6f1420ca36235141dc22a/R}{from the wild}" +This section includes a handful of examples "from the wild" and are generated with the recommended model, Claude Sonnet 3.5. Documenting a function factory: From 4858e78df8e82360b3863ecedd4a4cf841c68135 Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Tue, 18 Feb 2025 09:04:51 -0600 Subject: [PATCH 09/13] tailor CRAN comments to previous results --- cran-comments.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cran-comments.md b/cran-comments.md index 858617d..9384b7d 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,5 +1,9 @@ -## R CMD check results +**Notes for CRAN maintainers** -0 errors | 0 warnings | 1 note +There's 1 R CMD check NOTE, as this is a new release. -* This is a new release. +While there are not formal references for the methods implemented in this package, relevant software is mentioned in single quotes in the Description. + +The core functionality in this package requires both an API key and interactivity with the RStudio API, so many of the examples in the package's documentation are wrapped in `\dontrun{}` with notes on prerequisites. I've ensured that test coverage is as complete as possible, though many of those tests focus on unexported internals to allow for mocking inputs. + +Thank you all for your work. From 16c6c7ded900add24346f8bb9f772527580f1a9e Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Wed, 19 Feb 2025 15:43:30 -0600 Subject: [PATCH 10/13] use tempdir in tests --- tests/testthat/_snaps/directory.md | 20 ------------- tests/testthat/_snaps/prompt.md | 18 ------------ tests/testthat/test-directory.R | 17 ++++------- tests/testthat/test-prompt.R | 47 +++++++++--------------------- 4 files changed, 18 insertions(+), 84 deletions(-) diff --git a/tests/testthat/_snaps/directory.md b/tests/testthat/_snaps/directory.md index 6aa6a70..217886d 100644 --- a/tests/testthat/_snaps/directory.md +++ b/tests/testthat/_snaps/directory.md @@ -28,26 +28,6 @@ Prompt "bop-bad.md" has an unrecognized `interface` noted in its filename and will not be registered with chores. `interface` (following the hyphen) must be one of `replace`, `prefix`, or `suffix`. -# directory_list works - - Code - directory_list() - Message - - -- Prompts: - * 'test-prompt-dir/boop-replace.md' - * 'test-prompt-dir/wop-prefix.md' - ---- - - Code - directory_list() - Message - - -- Prompts: - * 'test-prompt-dir/boop-replace.md' - * 'test-prompt-dir/wop-prefix.md' - # directory_set works Code diff --git a/tests/testthat/_snaps/prompt.md b/tests/testthat/_snaps/prompt.md index 3443232..b1d4f66 100644 --- a/tests/testthat/_snaps/prompt.md +++ b/tests/testthat/_snaps/prompt.md @@ -1,21 +1,3 @@ -# prompt_new errors informatively with redundant chore - - Code - prompt_new("boop", "replace") - Condition - Error in `prompt_new()`: - ! There's already a helper for chore "boop". - i You can edit it with `prompt_edit("boop")` - ---- - - Code - prompt_new("boop", "prefix") - Condition - Error in `prompt_new()`: - ! There's already a helper for chore "boop". - i You can edit it with `prompt_edit("boop")` - # prompt_remove errors informatively with bad chore Code diff --git a/tests/testthat/test-directory.R b/tests/testthat/test-directory.R index 31f2c92..e1f4ec1 100644 --- a/tests/testthat/test-directory.R +++ b/tests/testthat/test-directory.R @@ -41,26 +41,19 @@ test_that("filter_interfaces messages informatively", { expect_equal(res, x[1]) }) -test_that("directory_list works", { - # contains two prompts, `boop-replace` and `wop-prefix` - withr::local_options(.chores_dir = "test-prompt-dir") - testthat::local_mocked_bindings(interactive = function(...) {TRUE}) - expect_snapshot(directory_list()) - - testthat::local_mocked_bindings(interactive = function(...) {TRUE}) - expect_snapshot(directory_list()) -}) test_that("directory_list works", { # contains two prompts, `boop-replace` and `wop-prefix` - withr::local_options(.chores_dir = "test-prompt-dir") - expect_equal(directory_path(), "test-prompt-dir") + tmp_dir <- withr::local_tempdir() + withr::local_options(.chores_dir = tmp_dir) + expect_equal(directory_path(), tmp_dir) }) test_that("directory_set works", { expect_snapshot(error = TRUE, directory_set(identity)) - withr::local_options(.chores_dir = "test-prompt-dir") + tmp_dir <- withr::local_tempdir() + withr::local_options(.chores_dir = tmp_dir) path <- directory_path() withr::defer(directory_set(path)) diff --git a/tests/testthat/test-prompt.R b/tests/testthat/test-prompt.R index 63d7888..cf8ca76 100644 --- a/tests/testthat/test-prompt.R +++ b/tests/testthat/test-prompt.R @@ -1,9 +1,10 @@ test_that("prompt_* functions work", { # contains two prompts, `boop-replace` and `wop-prefix` - withr::local_options(.chores_dir = "test-prompt-dir") + tmp_dir <- withr::local_tempdir() + withr::local_options(.chores_dir = tmp_dir) testthat::local_mocked_bindings(interactive = function(...) {FALSE}) - path <- "test-prompt-dir/floop-replace.md" + path <- file.path(tmp_dir, "floop-replace.md") if (file.exists(path)) {file.remove(path)} withr::defer({if (file.exists(path)) {file.remove(path)}}) @@ -24,36 +25,10 @@ test_that("prompt_* functions work", { expect_false(file.exists(.res)) }) -test_that("prompt_new errors informatively with redundant chore", { - # contains two prompts, `boop-replace` and `wop-prefix` - withr::local_options(.chores_dir = "test-prompt-dir") - testthat::local_mocked_bindings(interactive = function(...) {FALSE}) - - expect_snapshot(error = TRUE, prompt_new("boop", "replace")) - expect_snapshot(error = TRUE, prompt_new("boop", "prefix")) -}) - -test_that("prompt_new works when directory doesn't exist yet (#47)", { - subdir <- "test-prompt-dir/subdir" - withr::local_options(.chores_dir = subdir) - testthat::local_mocked_bindings(interactive = function(...) {FALSE}) - - if (dir.exists(subdir)) {unlink(subdir, recursive = TRUE)} - if ("floop" %in% list_helpers()) {.helper_remove("floop")} - - withr::defer({ - if (dir.exists(subdir)) {unlink(subdir, recursive = TRUE)} - if ("floop" %in% list_helpers()) {.helper_remove("floop")} - }) - - .res <- prompt_new("floop", "replace") - expect_equal(.res, paste0(subdir, "/floop-replace.md")) - expect_true(file.exists(.res)) -}) - test_that("prompt_remove errors informatively with bad chore", { # contains two prompts, `boop-replace` and `wop-prefix` - withr::local_options(.chores_dir = "test-prompt-dir") + tmp_dir <- withr::local_tempdir() + withr::local_options(.chores_dir = tmp_dir) testthat::local_mocked_bindings(interactive = function(...) {FALSE}) expect_snapshot(error = TRUE, prompt_remove("nonexistentchore")) @@ -62,7 +37,8 @@ test_that("prompt_remove errors informatively with bad chore", { test_that("new prompts can be pre-filled with contents", { skip_if_offline() # contains two prompts, `boop-replace` and `wop-prefix` - withr::local_options(.chores_dir = "test-prompt-dir") + tmp_dir <- withr::local_tempdir() + withr::local_options(.chores_dir = tmp_dir) testthat::local_mocked_bindings(interactive = function(...) {FALSE}) withr::defer(prompt_remove("summarizeAlt")) @@ -85,7 +61,8 @@ test_that("new prompts can be pre-filled with contents", { test_that("new prompts are pre-filled with a template by default (#57)", { skip_if_offline() # contains two prompts, `boop-replace` and `wop-prefix` - withr::local_options(.chores_dir = "test-prompt-dir") + tmp_dir <- withr::local_tempdir() + withr::local_options(.chores_dir = tmp_dir) testthat::local_mocked_bindings(interactive = function(...) {FALSE}) withr::defer(prompt_remove("template")) @@ -99,7 +76,8 @@ test_that("new prompts are pre-filled with a template by default (#57)", { test_that("new prompts error informatively with bad pre-fill contents", { skip_if_offline() # contains two prompts, `boop-replace` and `wop-prefix` - withr::local_options(.chores_dir = "test-prompt-dir") + tmp_dir <- withr::local_tempdir() + withr::local_options(.chores_dir = tmp_dir) testthat::local_mocked_bindings(interactive = function(...) {FALSE}) withr::defer(prompt_remove("summarizeAlt")) @@ -117,7 +95,8 @@ test_that("new prompts error informatively with bad pre-fill contents", { test_that("prompts can be added, removed, and added again without restart (#58)", { skip_if_offline() # contains two prompts, `boop-replace` and `wop-prefix` - withr::local_options(.chores_dir = "test-prompt-dir") + tmp_dir <- withr::local_tempdir() + withr::local_options(.chores_dir = tmp_dir) testthat::local_mocked_bindings(interactive = function(...) {FALSE}) withr::defer(prompt_remove("template")) From c67b8704285c21cbe2aef389e12e3bf438a452c7 Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Fri, 21 Feb 2025 08:01:51 -0600 Subject: [PATCH 11/13] update cran comments post-review --- cran-comments.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cran-comments.md b/cran-comments.md index 9384b7d..eea119a 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -2,8 +2,16 @@ There's 1 R CMD check NOTE, as this is a new release. -While there are not formal references for the methods implemented in this package, relevant software is mentioned in single quotes in the Description. +> If there are references describing the methods in your package... -The core functionality in this package requires both an API key and interactivity with the RStudio API, so many of the examples in the package's documentation are wrapped in `\dontrun{}` with notes on prerequisites. I've ensured that test coverage is as complete as possible, though many of those tests focus on unexported internals to allow for mocking inputs. +Unfortunately, there are not formal references for the methods implemented in this package. Relevant software is mentioned in single quotes in the Description. + +> Please add \value to .Rd files regarding exported methods and explain the functions results in the documentation. + +Added docs on return value for this missing functions, thanks! + +> Please ensure that your functions do not write by default or in your examples/vignettes/tests in the user's home filespace... In your examples/vignettes/tests you can write to tempdir(). + +Thanks, examples/tests/vignettes now use a tempdir. Thank you all for your work. From d60e7e9b3002c3ccb553f62b256e5cebe3e53477 Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Fri, 21 Feb 2025 08:02:40 -0600 Subject: [PATCH 12/13] add return values, don't create persistent directories by default --- R/directory.R | 42 ++++++++++++++++++++++++++++++------------ R/init-helper.R | 3 +++ R/zzz.R | 4 ++-- man/directory.Rd | 29 ++++++++++++++++++----------- man/dot-init_helper.Rd | 3 +++ 5 files changed, 56 insertions(+), 25 deletions(-) diff --git a/R/directory.R b/R/directory.R index ec97dc5..7138f07 100644 --- a/R/directory.R +++ b/R/directory.R @@ -6,8 +6,7 @@ #' `directory_*()` functions allow users to interface with the directory, #' making new "chores" available: #' -#' * `directory_path()` returns the path to the prompt directory, which -#' defaults to `~/.config/chores`. +#' * `directory_path()` returns the path to the prompt directory. #' * `directory_set()` changes the path to the prompt directory (by setting #' the option `.chores_dir`). #' * `directory_list()` enumerates all of the different prompts that currently @@ -51,21 +50,32 @@ #' loading the package, just set the `.chores_dir` option with #' `options(.chores_dir = some_dir)`. To load a directory of files that's not #' the prompt directory, provide a `dir` argument to `directory_load()`. +#' +#' @returns +#' * `directory_path()` returns the path to the prompt directory (which is +#' not created by default unless explicitly requested by the user). +#' * `directory_set()` return the path to the new prompt directory. +#' * `directory_list()` returns the file paths of all of the prompts that +#' currently live in the directory (and provides clickable links to each). +#' * `directory_load()` returns `NULL` invisibly. +#' #' @name directory #' -#' @seealso The "Custom helpers" vignette, at `vignette("custom", package = "chores")`, -#' for more on adding your own helper prompts, sharing them with others, and -#' using prompts from others. +#' @seealso The "Custom helpers" vignette, at +#' `vignette("custom", package = "chores")`,for more on adding your own +#' helper prompts, sharing them with others, and using prompts from others. #' #' @examples +#' # choose a path for the prompt directory +#' tmp_dir <- withr::local_tempdir() +#' directory_set(tmp_dir) +#' #' # print out the current prompt directory #' directory_path() #' #' # list out prompts currently in the directory #' directory_list() #' -#' # sets options and creates files, so don't run automatically: -#' \dontrun{ #' # create a prompt in the prompt directory #' prompt_new("boop", "replace") #' @@ -76,10 +86,6 @@ #' # (this will also happen automatically on reload) #' directory_load() #' -#' # these are equivalent: -#' directory_set("some/folder") -#' options(.chores_dir = "some/folder") -#' } #' @export directory_load <- function(dir = directory_path()) { prompt_base_names <- directory_base_names(dir) @@ -96,6 +102,8 @@ directory_load <- function(dir = directory_path()) { .helper_add(chore = chore, prompt = prompt, interface = interface) } + + invisible() } #' @rdname directory @@ -133,7 +141,17 @@ directory_list <- function() { #' @rdname directory #' @export directory_path <- function() { - getOption(".chores_dir", default = file.path("~", ".config", "chores")) + .chores_dir <- getOption(".chores_dir", default = NULL) + + if (is.null(.chores_dir)) { + cli::cli_warn(c( + "No {.pkg chores} prompt directory configured.", + 'Set one in your {.file .Rprofile} using e.g. + {.code directory_set(file.path("~", ".config", "chores"))}.' + )) + } + + .chores_dir } #' @rdname directory diff --git a/R/init-helper.R b/R/init-helper.R index 63da6d3..79e3eb1 100644 --- a/R/init-helper.R +++ b/R/init-helper.R @@ -15,6 +15,9 @@ #' so e.g. set `options(.chores_chat = ellmer::chat_claude())` in your #' `.Rprofile` to configure chores with ellmer every time you start a new R session. #' +#' @returns +#' A Helper object, which is a subclass of an ellmer chat. +#' #' @examples #' # requires an API key and sets options #' \dontrun{ diff --git a/R/zzz.R b/R/zzz.R index ad56b0d..58f2410 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -6,8 +6,8 @@ directory_load(system.file("prompts", package = "chores")) - chores_dir <- getOption(".chores_dir", default = file.path("~", ".config", "chores")) - if (dir.exists(chores_dir)) { + chores_dir <- getOption(".chores_dir", default = NULL) + if (!is.null(chores_dir) && dir.exists(chores_dir)) { directory_load(chores_dir) } } diff --git a/man/directory.Rd b/man/directory.Rd index e92894d..ba31c6c 100644 --- a/man/directory.Rd +++ b/man/directory.Rd @@ -19,14 +19,23 @@ directory_set(dir) \arguments{ \item{dir}{Path to a directory of markdown files--see \code{Details} for more.} } +\value{ +\itemize{ +\item \code{directory_path()} returns the path to the prompt directory (which is +not created by default unless explicitly requested by the user). +\item \code{directory_set()} return the path to the new prompt directory. +\item \code{directory_list()} returns the file paths of all of the prompts that +currently live in the directory (and provides clickable links to each). +\item \code{directory_load()} returns \code{NULL} invisibly. +} +} \description{ The chores package's prompt directory is a directory of markdown files that is automatically registered with the chores package on package load. \verb{directory_*()} functions allow users to interface with the directory, making new "chores" available: \itemize{ -\item \code{directory_path()} returns the path to the prompt directory, which -defaults to \verb{~/.config/chores}. +\item \code{directory_path()} returns the path to the prompt directory. \item \code{directory_set()} changes the path to the prompt directory (by setting the option \code{.chores_dir}). \item \code{directory_list()} enumerates all of the different prompts that currently @@ -73,14 +82,16 @@ the prompt directory, provide a \code{dir} argument to \code{directory_load()}. } \examples{ +# choose a path for the prompt directory +tmp_dir <- withr::local_tempdir() +directory_set(tmp_dir) + # print out the current prompt directory directory_path() # list out prompts currently in the directory directory_list() -# sets options and creates files, so don't run automatically: -\dontrun{ # create a prompt in the prompt directory prompt_new("boop", "replace") @@ -91,13 +102,9 @@ directory_list() # (this will also happen automatically on reload) directory_load() -# these are equivalent: -directory_set("some/folder") -options(.chores_dir = "some/folder") -} } \seealso{ -The "Custom helpers" vignette, at \code{vignette("custom", package = "chores")}, -for more on adding your own helper prompts, sharing them with others, and -using prompts from others. +The "Custom helpers" vignette, at +\code{vignette("custom", package = "chores")},for more on adding your own +helper prompts, sharing them with others, and using prompts from others. } diff --git a/man/dot-init_helper.Rd b/man/dot-init_helper.Rd index 287d8ce..9c87ba0 100644 --- a/man/dot-init_helper.Rd +++ b/man/dot-init_helper.Rd @@ -16,6 +16,9 @@ though custom helpers can be added with \code{\link[=prompt_new]{prompt_new()}}. so e.g. set \code{options(.chores_chat = ellmer::chat_claude())} in your \code{.Rprofile} to configure chores with ellmer every time you start a new R session.} } +\value{ +A Helper object, which is a subclass of an ellmer chat. +} \description{ \strong{Users typically should not need to call this function.} \itemize{ From b0eab2c8afb837d2f87675cbd12e2426c7a0488a Mon Sep 17 00:00:00 2001 From: simonpcouch Date: Fri, 21 Feb 2025 08:03:29 -0600 Subject: [PATCH 13/13] bump version --- DESCRIPTION | 2 +- NEWS.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 7cd4d5f..2d9a88c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: chores Title: A Collection of Large Language Model Assistants -Version: 0.0.4 +Version: 0.1.0 Authors@R: c( person("Simon", "Couch", , "simon.couch@posit.co", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-5676-5107")), diff --git a/NEWS.md b/NEWS.md index d744b99..b1599f0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# chores (development version) +# chores 0.1.0 * Initial CRAN submission.