diff --git a/.gitignore b/.gitignore index 1a6844d..2e641fb 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ *.so *.dll misc/ +*.Rproj diff --git a/AGENTS.md b/AGENTS.md index 5fb2325..7ef0742 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ ## What This Is -saber is a zero-dependency R package for code analysis and project context. It parses R source into symbol indices, traces callers across projects, discovers package metadata, generates project briefings, and inspects installed packages. +saber is a zero-dependency R package for context engineering in R. It assembles agent context from memory and instruction files, traces function call blast radius across projects, generates project briefings, parses R source into structured symbol indices, discovers dependency graphs, and introspects installed packages. ## Working Rules diff --git a/DESCRIPTION b/DESCRIPTION index b01990b..a25526f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,14 +1,18 @@ Package: saber Type: Package -Title: Code Analysis and Project Context for R +Title: Context Engineering for R Version: 0.7.0 -Authors@R: person("Troy", "Hernandez", role = c("aut", "cre"), - email = "troy@cornball.ai", - comment = c(ORCID = "0009-0005-4248-604X")) -Description: Parses R source files into Abstract Syntax Tree (AST) symbol indices, traces function - callers across projects, discovers project dependency graphs, generates - project briefings, and provides package introspection tools. Designed - for AI coding agents that need structured code understanding. +Authors@R: c( + person("Troy", "Hernandez", role = c("aut", "cre"), + email = "troy@cornball.ai", + comment = c(ORCID = "0009-0005-4248-604X")), + person("cornball.ai", role = "cph")) +Description: Context-engineering primitives for AI coding agents working in R. + Assembles agent context from memory and instruction files (AGENTS.md, + CLAUDE.md), traces function call blast radius across projects, generates + project briefings, parses source into structured AST symbol indices, + discovers dependency graphs, and introspects installed packages. + Zero dependencies. License: Apache License (>= 2) URL: https://github.com/cornball-ai/saber BugReports: https://github.com/cornball-ai/saber/issues diff --git a/R/agent_context.R b/R/agent_context.R index 90b4392..d6ee039 100644 --- a/R/agent_context.R +++ b/R/agent_context.R @@ -1,5 +1,6 @@ -#' @title Agent context loading -#' @description Load standard context files for AI coding agents. +#' @title Agent context assembly +#' @description Assemble context from memory, instructions, and identity files +#' for AI coding agents. #' Load context files for an AI coding agent #' diff --git a/R/fn_graph.R b/R/fn_graph.R index 6cc7d90..851231b 100644 --- a/R/fn_graph.R +++ b/R/fn_graph.R @@ -1,5 +1,6 @@ -#' @title Function call graph -#' @description Render a package's internal function call graph. +#' @title Code intelligence — function call graph +#' @description Render a project's internal function call graph as interactive +#' SVG. #' Render a function call graph for an R project #' diff --git a/R/graph_svg.R b/R/graph_svg.R index 884168a..76eab97 100644 --- a/R/graph_svg.R +++ b/R/graph_svg.R @@ -1,5 +1,5 @@ #' @title Force-directed graph rendering -#' @description Render a graph as static SVG using a base R +#' @description Render a graph as static, interactive SVG using a base R #' Fruchterman-Reingold force simulation. #' Render a graph as static SVG diff --git a/R/pkg.R b/R/pkg.R index 29f5cb9..bd9e59d 100644 --- a/R/pkg.R +++ b/R/pkg.R @@ -1,5 +1,6 @@ #' @title Package introspection -#' @description Query installed R packages for exports, internals, and help. +#' @description Query installed R packages for exported functions, internal +#' functions, and help documentation. #' List exported functions of a package #' diff --git a/R/pkg_graph.R b/R/pkg_graph.R index 54e9e62..975c25e 100644 --- a/R/pkg_graph.R +++ b/R/pkg_graph.R @@ -1,5 +1,6 @@ -#' @title Package dependency graph -#' @description Render the dependency graph across a set of R packages. +#' @title Project discovery — package dependency graph +#' @description Render the dependency graph across a set of R packages as +#' interactive SVG. #' Render a package-level dependency graph #' diff --git a/R/projects.R b/R/projects.R index 5f54e86..2502b3e 100644 --- a/R/projects.R +++ b/R/projects.R @@ -1,5 +1,6 @@ #' @title Project discovery -#' @description Discover R package projects and their dependency relationships. +#' @description Discover R package projects and map their dependency +#' relationships. #' Discover R package projects #' diff --git a/R/symbols.R b/R/symbols.R index 7470c93..8994dad 100644 --- a/R/symbols.R +++ b/R/symbols.R @@ -1,5 +1,6 @@ -#' @title AST symbol index -#' @description Parse R source files to extract function definitions and calls. +#' @title Code intelligence — AST symbol index +#' @description Parse R source files into structured function definitions and +#' call relationships. #' @importFrom utils getParseData #' Build a symbol index for a project diff --git a/README.md b/README.md index 08881d7..4fe5d13 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # saber -Code analysis and project context for R. +**Context engineering for R.** -saber ("to know" in Spanish, pronounced [sah-BEHR](https://www.youtube.com/watch?v=m3WBocsw9lw)) parses R source into structured symbol indices, traces function callers across projects, discovers dependency graphs, generates project briefings, and cracks open installed packages for introspection. Built for AI coding agents that need to understand R code without guessing. +saber ("to know" in Spanish, pronounced [sah-BEHR](https://www.youtube.com/watch?v=m3WBocsw9lw)) assembles agent context, traces blast radius across projects, and introspects packages so AI coding agents don't have to guess. ## Install @@ -14,22 +14,70 @@ remotes::install_github("cornball-ai/saber") ## What it does -**9 exported functions.** +**13 exported functions.** + +### Agent context | Function | What it does | |---|---| -| `symbols()` | Parse R source files into function defs and calls via `getParseData()` | +| `agent_context()` | Assemble memory, identity, and instruction files for an agent | +| `briefing()` | Generate a project briefing (metadata, dependents, git log) | + +### Code intelligence + +| Function | What it does | +|---|---| +| `symbols()` | Parse R source into function defs and calls via `getParseData()` | | `blast_radius()` | Find every caller of a function, across projects | -| `find_downstream()` | Find all projects that depend on a given package | +| `fn_graph()` | Render a project's internal function call graph as SVG | +| `pkg_graph()` | Render a package dependency graph as SVG | +| `graph_svg()` | Force-directed graph renderer (used by `fn_graph` and `pkg_graph`) | + +### Project discovery + +| Function | What it does | +|---|---| | `projects()` | Discover R package projects and their metadata | -| `briefing()` | Generate a project context briefing (metadata, dependents, memory, git log) | +| `find_downstream()` | Find all projects that depend on a given package | +| `default_exclude()` | Default directories to skip when scanning | + +### Package introspection + +| Function | What it does | +|---|---| | `pkg_exports()` | List exported functions with argument signatures | | `pkg_internals()` | List internal (non-exported) functions | | `pkg_help()` | Pull help documentation as markdown | -| `default_exclude()` | Default directories to skip when scanning | ## Examples +Assemble agent context from project and workspace files: + +```r +# Claude Code agent in current project +saber::agent_context(agent = "claude") + +# Codex agent with workspace identity +saber::agent_context(agent = "codex", workspace_dir = "~/.codex/workspace") +``` + +Generate a project briefing: + +```r +saber::briefing("saber") +#> # Briefing: saber +#> _Generated 2026-03-25 00:30_ +#> +#> ## Package +#> - **Name**: saber +#> - **Title**: Context Engineering for R +#> - **Version**: 0.7.0 +#> +#> ## Recent commits +#> - 7983478 Add r-ci GitHub Actions workflow +#> - ... +``` + Index all function definitions and calls in a project: ```r @@ -52,46 +100,38 @@ Discover projects and their dependencies: ```r saber::projects() #> package title version path depends imports -#> saber Code Analysis for R 0.2.0 /home/troy/saber ... +#> saber Context Engineering 0.7.0 /home/troy/saber ... saber::find_downstream("jsonlite") #> [1] "chatterbox" "cornfab" "diffuseR" "llamaR" "llm.api" #> [6] "safetensors" "stt.api" "torch" "tts.api" "tuber" "whisper" ``` -Generate a project briefing for an AI agent: +Inspect any installed package: ```r -saber::briefing("saber") -#> # Briefing: saber -#> _Generated 2026-03-25 00:30_ -#> -#> ## Package -#> - **Name**: saber -#> - **Title**: Code Analysis and Project Context for R -#> - **Version**: 0.2.0 -#> -#> ## Recent commits -#> - 7983478 Add r-ci GitHub Actions workflow -#> - ... +saber::pkg_exports("saber") +saber::pkg_help("symbols", "saber") ``` -Inspect any installed package: +Render a call graph: ```r -saber::pkg_exports("saber") -saber::pkg_help("symbols", "saber") +svg <- saber::fn_graph("~/myproject") +writeLines(svg, "~/callgraph.svg") ``` ## How it works -`symbols()` runs `getParseData()` on every `R/*.R` file in a project, extracts function definitions and call sites, and caches the results as RDS in the user cache directory. Cache invalidates on file content changes (MD5). +`agent_context()` loads standard context files for AI coding agents: project instructions (AGENTS.md / CLAUDE.md), Claude Code memory files, global instructions, and agent identity files (SOUL.md). It skips files the agent already autoloads to avoid duplication. + +`briefing()` assembles project context from DESCRIPTION metadata, downstream dependents, and recent git commits. It writes the markdown to the user cache directory so both the agent and user see the same context. -`blast_radius()` builds on top of `symbols()`. It finds internal callers, then scans `~/` for any project whose DESCRIPTION declares a dependency on the target package. Traces the call graph across all of them. +`symbols()` runs `getParseData()` on every `R/*.R` file in a project, extracts function definitions and call sites, and caches the results as RDS. Cache invalidates on file content changes (MD5). -`projects()` scans for directories containing DESCRIPTION files and reads their metadata. `find_downstream()` does the same scan but filters to projects that depend on a specific package. +`blast_radius()` builds on `symbols()`. It finds internal callers, then scans `~/` for any project whose DESCRIPTION declares a dependency on the target package. Traces the call graph across all of them. With `include = c("r", "examples", "vignettes")` it also flags references in roxygen `@examples` blocks and vignette code chunks. -`briefing()` assembles project context from DESCRIPTION metadata, downstream dependents, Claude Code memory files, and recent git commits. It prints the briefing to stdout, returns the same text invisibly, and writes the markdown to the user cache directory so both the agent and user see the same context. An `agent` parameter controls memory inclusion: `agent = "claude"` skips Claude Code memory (since Claude Code autoloads it), while other values include it. +`fn_graph()` and `pkg_graph()` render force-directed SVG graphs via a base R Fruchterman-Reingold simulation. No JavaScript — tooltips and links work via native SVG features. ## Codex integration @@ -159,6 +199,7 @@ project briefing. Set `AGENTS_GLOBAL_MD` if you want a different path. Every new Codex session starts with the project's metadata, downstream dependents, Claude Code memory (if available), recent git commits, and optional global preferences already in context. ## Claude Code integration + Add the following to your `~/.claude/CLAUDE.md` to teach Claude Code how to use saber: ```markdown diff --git a/man/agent_context.Rd b/man/agent_context.Rd index 9a449d6..7f980f6 100644 --- a/man/agent_context.Rd +++ b/man/agent_context.Rd @@ -1,7 +1,7 @@ % tinyrox says don't edit this manually, but it can't stop you! \name{agent_context} \alias{agent_context} -\title{Agent context loading} +\title{Agent context assembly} \usage{ agent_context(agent = NULL, project_dir = getwd(), workspace_dir = NULL, memory_base = file.path(path.expand("~"), ".claude", "projects"), @@ -43,7 +43,8 @@ Character string of assembled context, or empty string if no context applies. } \description{ -Load standard context files for AI coding agents. +Assemble context from memory, instructions, and identity files + for AI coding agents. Load context files for an AI coding agent Returns assembled context (memory, project/global instructions, agent diff --git a/man/fn_graph.Rd b/man/fn_graph.Rd index e293c39..553af5e 100644 --- a/man/fn_graph.Rd +++ b/man/fn_graph.Rd @@ -1,7 +1,7 @@ % tinyrox says don't edit this manually, but it can't stop you! \name{fn_graph} \alias{fn_graph} -\title{Function call graph} +\title{Code intelligence — function call graph} \usage{ fn_graph(project_dir, include_external = FALSE, ...) } @@ -18,7 +18,8 @@ functions called from other packages. Default \code{FALSE}.} Character vector of SVG lines. Write with \code{writeLines()}. } \description{ -Render a package's internal function call graph. +Render a project's internal function call graph as interactive + SVG. Render a function call graph for an R project Pulls the AST symbol index via \code{\link{symbols}} and renders diff --git a/man/graph_svg.Rd b/man/graph_svg.Rd index e83d4f4..0da927e 100644 --- a/man/graph_svg.Rd +++ b/man/graph_svg.Rd @@ -31,7 +31,7 @@ Character vector, one SVG element per line. Write with \code{writeLines()}. } \description{ -Render a graph as static SVG using a base R +Render a graph as static, interactive SVG using a base R Fruchterman-Reingold force simulation. Render a graph as static SVG diff --git a/man/pkg_exports.Rd b/man/pkg_exports.Rd index 8f235d6..91919f1 100644 --- a/man/pkg_exports.Rd +++ b/man/pkg_exports.Rd @@ -14,7 +14,8 @@ pkg_exports(package, pattern = NULL) A data.frame with columns: name, args. } \description{ -Query installed R packages for exports, internals, and help. +Query installed R packages for exported functions, internal + functions, and help documentation. List exported functions of a package Returns a data.frame of exported functions with their argument signatures. diff --git a/man/pkg_graph.Rd b/man/pkg_graph.Rd index 1adb006..8ece164 100644 --- a/man/pkg_graph.Rd +++ b/man/pkg_graph.Rd @@ -1,7 +1,7 @@ % tinyrox says don't edit this manually, but it can't stop you! \name{pkg_graph} \alias{pkg_graph} -\title{Package dependency graph} +\title{Project discovery — package dependency graph} \usage{ pkg_graph(scan_dir = path.expand("~"), packages = NULL, include_suggests = FALSE, ...) @@ -22,7 +22,8 @@ packages in each project's \code{Suggests} field. Default Character vector of SVG lines. Write with \code{writeLines()}. } \description{ -Render the dependency graph across a set of R packages. +Render the dependency graph across a set of R packages as + interactive SVG. Render a package-level dependency graph Discovers R packages under \code{scan_dir} via \code{\link{projects}}, diff --git a/man/projects.Rd b/man/projects.Rd index 4d21087..b8e2b91 100644 --- a/man/projects.Rd +++ b/man/projects.Rd @@ -15,7 +15,8 @@ A data.frame with columns: package, title, version, path, depends, imports. } \description{ -Discover R package projects and their dependency relationships. +Discover R package projects and map their dependency + relationships. Discover R package projects Scans a directory for subdirectories containing a DESCRIPTION file and diff --git a/man/symbols.Rd b/man/symbols.Rd index ea91b3c..f793c4a 100644 --- a/man/symbols.Rd +++ b/man/symbols.Rd @@ -1,7 +1,7 @@ % tinyrox says don't edit this manually, but it can't stop you! \name{symbols} \alias{symbols} -\title{AST symbol index} +\title{Code intelligence — AST symbol index} \usage{ symbols(project_dir, cache_dir = file.path(tools::R_user_dir("saber", "cache"), "symbols")) @@ -19,7 +19,8 @@ A list with components: } } \description{ -Parse R source files to extract function definitions and calls. +Parse R source files into structured function definitions and + call relationships. } \examples{ # Create a minimal project with R source files