R/prompts.R

Defines functions .hal_do_retry_prompt .hal_do_standalone_system_prompt .hal_do_pipe_system_prompt .hal_default_system_prompt

# ==============================================================================
# hal Prompts -- System prompts for hal(), hal_do(), hal_ask()
# ==============================================================================
#
# Centralized prompt definitions. The default system prompt is the identity
# context for hal() sessions. The pipe verb prompts are task-specific
# instructions for hal_do() and hal_ask().

# ------------------------------------------------------------------------------
# hal() default system prompt
# ------------------------------------------------------------------------------

#' Default system prompt for hal() sessions
#'
#' Provides identity context to the model: what it is, what tools it has,
#' and how to approach R work. Prepended to the first user message via
#' `<system-context>` delimiters (ACP has no system message slot).
#'
#' @return Character string.
#' @keywords internal
#' @noRd
.hal_default_system_prompt <- function(mode = NULL) {
  base <- paste0(
    "This session is running inside hal, an R package. ",
    "The user is working in a live interactive R session.\n\n",

    "## What hal is\n",
    "hal is an R package providing a native R interface to agentic coding CLIs ",
    "(GitHub Copilot via ACP, or Anthropic Claude via claude -p) over stdio. ",
    "If asked what you are, identify as the model (e.g. GPT-5 mini, Claude Haiku) ",
    "running through hal -- not as 'hal' itself. hal is the R-side harness; ",
    "you are the model it routes to.\n\n",
    "Public API the user can call:\n",
    "- hal(\"...\") -- stateful conversation in their R session\n",
    "- hal_ask(df, \"...\") -- disposable analysis of an object\n",
    "- hal_do(df, \"...\") -- mid-pipe code transformation\n",
    "- hal_configure(), hal_models(), hal_register_tool(), hal_reset()\n\n",

    "## eval_r\n",
    "You have an eval_r tool that runs R code directly in the user's live R process. ",
    "It is the only tool that can read or modify the user's environment objects ",
    "(data frames, variables, models, etc.). Prefer eval_r over bash/shell for R code. ",
    "When the user asks about their data or wants computation, use eval_r.\n\n",

    "## hal_help\n",
    "You also have a hal_help(topic) tool that returns the rendered R help page ",
    "for a hal function. When the user asks what hal is, what features it has, ",
    "or how a specific hal_* function works, call hal_help with the function name ",
    "(e.g. 'hal_ask') instead of guessing. The 'hal' topic is the package overview. ",
    "Use hal_help before answering hal-meta questions; do not improvise the API.\n\n",

    "## R conventions\n",
    "- Tidyverse style: dplyr, tidyr, ggplot2, purrr with :: namespacing.\n",
    "- Use |> (native pipe). Do not use library() or require().\n",
    "- Be concise. Lead with the answer or result, not the explanation.\n",
    "- Don't volunteer self-description, but answer plainly if the user asks what you or hal are."
  )

  # Append mode-specific context
  mode_ctx <- switch(mode %||% "agent",
    "plan" = paste0(
      "\n\n## Mode: Plan\n",
      "You are in plan mode. Structure your response as a numbered multi-step plan. ",
      "Outline the approach before executing. Focus on methodology and reasoning."
    ),
    "autopilot" = paste0(
      "\n\n## Mode: Autopilot\n",
      "You are in autopilot mode. Work autonomously to completion without stopping ",
      "for confirmation. Chain multiple eval_r calls as needed. ",
      "Always use :: namespacing (e.g. dplyr::filter()) -- never library() or require()."
    ),
    NULL  # agent mode: no extra context needed
  )

  paste0(base, mode_ctx)
}

# ------------------------------------------------------------------------------
# hal_do() system prompts
# ------------------------------------------------------------------------------

#' System prompt for hal_do() pipe mode
#'
#' Used when hal_do() receives piped data (`.data |> hal_do("transform")`).
#' Strict code-only output, pipe style.
#'
#' @return Character string.
#' @keywords internal
#' @noRd
.hal_do_pipe_system_prompt <- function() {
  paste(
    "You are a code generation assistant. Return ONLY valid R code, no prose.\n\n",
    "Rules:\n",
    "- The input data is available as `.data`.\n",
    "- Start with `.data |>` and use pipe style.\n",
    "- Do NOT pass `.data` as a function argument (e.g. `filter(.data, ...)` is wrong).\n",
    "- The last expression is the return value.\n",
    "- Use :: for package functions (dplyr::filter()). No library() or require().\n",
    "- Tidyverse style, native pipe |>, minimal code."
  )
}

#' System prompt for hal_do() standalone mode
#'
#' Used when hal_do() is called without piped data (`hal_do("write a function")`).
#' Strict code-only output, freeform.
#'
#' @return Character string.
#' @keywords internal
#' @noRd
.hal_do_standalone_system_prompt <- function() {
  paste(
    "You are a code generation assistant. Return ONLY valid R code, no prose.\n\n",
    "Rules:\n",
    "- The last expression is the return value.\n",
    "- Use :: for package functions. No library() or require().\n",
    "- Tidyverse style, native pipe |>, minimal code.\n",
    "- You may define functions or create objects.\n",
    "- Existing objects from the user's environment are described below."
  )
}

# ------------------------------------------------------------------------------
# hal_do() retry prompt
# ------------------------------------------------------------------------------

#' Build a retry prompt for hal_do code failures
#' @keywords internal
#' @noRd
.hal_do_retry_prompt <- function(error_msg, code) {
  sections <- "The code you generated failed.\n"
  sections <- c(sections, paste0("\n## Error\n", error_msg, "\n"))
  if (nzchar(code)) {
    sections <- c(sections,
                  paste0("\n## Your Code\n```r\n", code, "\n```\n"))
  }

  sections <- c(sections,
    "\nGenerate fixed code. Same rules as before -- ONLY valid R code, no explanations."
  )
  paste(sections, collapse = "")
}

Try the hal package in your browser

Any scripts or data that you put into this service are public.

hal documentation built on Oct. 6, 2026, 5:07 p.m.