diff --git a/DESCRIPTION b/DESCRIPTION index fbcf9ae..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")), @@ -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/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/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. diff --git a/R/directory.R b/R/directory.R index 0215355..7138f07 100644 --- a/R/directory.R +++ b/R/directory.R @@ -6,14 +6,13 @@ #' `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 #' 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. @@ -51,15 +50,28 @@ #' 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) #' -#' @examplesIf FALSE #' # print out the current prompt directory -#' directory_get() +#' directory_path() #' #' # list out prompts currently in the directory #' directory_list() @@ -74,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) @@ -94,6 +102,8 @@ directory_load <- function(dir = directory_path()) { .helper_add(chore = chore, prompt = prompt, interface = interface) } + + invisible() } #' @rdname directory @@ -131,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/doc-helper-roxygen.R b/R/doc-helper-roxygen.R index df7f5c9..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/hadley/elmer/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/R/helper-add-remove.R b/R/helper-add-remove.R index 6bb9a41..3daf98d 100644 --- a/R/helper-add-remove.R +++ b/R/helper-add-remove.R @@ -1,30 +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 -#' @export +# 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, @@ -39,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/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..79e3eb1 100644 --- a/R/init-helper.R +++ b/R/init-helper.R @@ -9,15 +9,20 @@ #' #' @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 #' `.Rprofile` to configure chores with ellmer every time you start a new R session. #' -#' @examplesIf FALSE +#' @returns +#' A Helper object, which is a subclass of an ellmer chat. +#' +#' @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 +33,7 @@ #' options( #' .chores_chat = ellmer::chat_openai(model = "gpt-4o-mini") #' ) +#' } #' @export .init_helper <- function( chore = NULL, @@ -37,7 +43,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..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" @@ -32,7 +37,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 +72,7 @@ #' "58c9b4da/summarize-prefix.md" #' ) #' ) -#' +#' } #' @name prompt #' @rdname prompt 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/README.Rmd b/README.Rmd index 3610395..f919874 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") ``` @@ -79,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 7560745..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) @@ -26,6 +24,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") ``` @@ -98,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/_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/cran-comments.md b/cran-comments.md index 858617d..eea119a 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,5 +1,17 @@ -## 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. +> If there are references describing the methods in your package... + +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. 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: diff --git a/man/directory.Rd b/man/directory.Rd index 26919d4..ba31c6c 100644 --- a/man/directory.Rd +++ b/man/directory.Rd @@ -19,20 +19,29 @@ 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 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, @@ -73,9 +82,12 @@ the prompt directory, provide a \code{dir} argument to \code{directory_load()}. } \examples{ -\dontshow{if (FALSE) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +# choose a path for the prompt directory +tmp_dir <- withr::local_tempdir() +directory_set(tmp_dir) + # print out the current prompt directory -directory_get() +directory_path() # list out prompts currently in the directory directory_list() @@ -90,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") -\dontshow{\}) # examplesIf} } \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_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..9c87ba0 100644 --- a/man/dot-init_helper.Rd +++ b/man/dot-init_helper.Rd @@ -9,13 +9,16 @@ \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, 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{ @@ -25,9 +28,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 +42,5 @@ with \code{\link[=prompt_new]{prompt_new()}}. options( .chores_chat = ellmer::chat_openai(model = "gpt-4o-mini") ) -\dontshow{\}) # examplesIf} +} } 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. -} 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/man/roxygen_helper.Rd b/man/roxygen_helper.Rd index c3e4814..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/hadley/elmer/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: 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/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()`. 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")) 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") ``` 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