DataStep Studio for R Install
Preview 0.10.3: the first public release. Tell us what breaks.

R that says what it did. In VS Code.

A log that names the line of every problem and gives each data frame a NOTE. Stop a run where the value goes wrong and look inside. One live R session, for you and, if you want, your AI agent.

For VS Code and Positron · R 4.4 or later · For statistical and clinical programming · Sends nothing anywhere

The film, 70 seconds, silent: the run, the NOTE that gives the bug away, the stop where the value goes wrong, the one-character fix, the tests, and the same steps as the commands an agent uses. Every log line is from a real run of the manual's example study.

One live R session

You and your agent. In the same R.

The agent runs R only through the tools you click, as the dsr command or an MCP server. Its runs fill your R Console, its stops wait in your editor, and it reads the same log and the same verdicts you do.

VS Code: the Agent panel on the left lists the agent's dsr commands with their verdicts; the program in the middle; the R Console below shows the NOTEs, an ADAM FAIL with its first cause, then TESTS PASS

The Agent panel (Claude Code with the user's own login) programs ADSL of the CDISC pilot: lint, run, compare with its copy before the fix, dsr adam check, the tests. Recorded with the first build, 0.10.2.

  • Your agent, your choiceClaude Code, Codex, Cursor or Copilot through the MCP server (46 typed tools), any agent with a shell through dsr, or the Agent panel: Claude Code or a local model with Ollama.
  • Results as data--json on every tool: the verdict, the line, the next step. Exit codes 0, 1, 2, 3. dsr guide teaches the way of working on one page.
  • OptionalEverything on this page works without an agent and without an account.

Set up an agent · The dsr commands

Run · Cmd/Ctrl+Shift+Enter

Every message at its line. A NOTE for every data frame.

The program runs step by step in the session. The log shows each warning and error at its line, and each data frame a step makes: its rows and columns, the new columns, and how many values they miss.

An ADSL program in VS Code; the R Console below lists a NOTE per step: adsl has 254 rows and 39 columns, new AGEGR1 with 4 missing, and RESULT: CLEAN

ADSL of the CDISC pilot study, programmed with admiral: a NOTE per step, then the verdict. Recorded with 0.10.2.

    3  NOTE: dm has 12 rows and 7 columns.
    4  NOTE: dm has 12 rows and 8 columns (was 12 x 7). New: BMI.
    5  NOTE: dm has 12 rows and 9 columns (was 12 x 8). New: BMIGRP (1 missing).
    6  NOTE: dm has 12 rows and 10 columns (was 12 x 9). New: AGEGRP.
    7  NOTE: n_subjects = 12
RESULT: CLEAN  (0.11 s)

Clean is not the same as right.

No error, no warning, yet one subject got no BMI group. The NOTE says it on the line that made it. The verdict is CLEAN, CHECK or ERROR with its first cause.

  • The Suspicious LogText that became NA, a join that added rows, a type that changed: each at its line, with why it matters and the fix.
  • Problems at their lineA run's errors and warnings go to the Problems panel; F8 goes from one to the next.
  • Run a partThe selection, to the cursor, from the cursor. The objects stay between runs.

Debug · F9 F5

Stop where the value goes wrong. See every frame.

A breakpoint in the margin, with a condition if you like. The run stops there, also inside a function, and waits: the call stack, the variables of each frame, the Debug Console in the frame. Then next, step in, step out, continue.

The R debugger stopped at a breakpoint in fisher_p in R/insights.R: the call stack, the variables and the debug toolbar

Stopped in fisher_p at a margin breakpoint. Taken by the extension's UI check, which drives VS Code like a user.

An error keeps its frames open.

No need to run it again with a print: look at the objects of the frame where it failed, run R in it. Here one subject's weight is missing, so the BMI is NA. And pry() in your code stops a real run where you wrote it, also in a loop: pry(when = USUBJID == "S-110").

The Pry panel after an error: missing value where TRUE/FALSE needed in bmi_group at R/bmi.R line 4, the objects of the frame: bmi NA, height 154.94, weight NA; the stack; the R Console with the error and its first cause

A listing program of the CDISC pilot fails in a helper; the Pry panel opens the helper's frame. Recorded with 0.10.2.

$ dsr run 04_debug.R --stop-at 'R/derive.R:11 if any(bmi == 30)'
=>   11    grp[bmi >= 25 & bmi < 30] <- "25-<30"
RESULT: PRY | AT: R/derive.R:11: stopped at the breakpoint
$ dsr pry next
RESULT: PRY | AT: R/derive.R:12: stopped (next)
$ dsr pry eval 'grp[bmi == 30]'
       [1] NA

The same stop from a terminal.

An agent, or you, stops the run with --stop-at and goes on with dsr pry next. The editor's status bar shows the waiting stop, and the debugger can attach to it. Line 12 says > 30; the rule is >= 30.

Which tool when?

Four tools. One question each.

The debugger

Where does this value go wrong?

Breakpoints in the margin, step by step, the variables of each frame.

F9, then F5
dsr run --stop-at

pry

What is the data here, in the real run?

A stop written in the code, also in a loop and for one subject.

pry(when = ...)
dsr pry next

Trace

Why does this function return this?

Every line of one call, what it changed, each if with its values. No stop.

Trace Function Call
dsr trace --call

Pipe debugger

Which step loses the records?

Each step of a pipeline with its rows before and after, and the records it dropped.

Debug the Pipeline
dsr pipe file:line

Trace · dsr trace --call

Which line made this value?

Trace runs one call and lists every line that ran, the variables each line changed, and each if with the values it compared. Start it from a form of the function's arguments.

The call form of fisher_p with its arguments, and the trace of the call: each line with what it changed

The call form and the trace it opened. Taken by the UI check.

$ dsr trace --call 'bmi_group(c(24.9, 30, 31))' R/derive.R
R/derive.R:9             grp <- rep(NA_character_, length(bmi))
                          grp = [3] "NA", "NA", "NA"
R/derive.R:10            grp[bmi < 25] <- "<25"
                          grp = [3] "<25", "NA", "NA"
R/derive.R:11            grp[bmi >= 25 & bmi < 30] <- "25-<30"
R/derive.R:12            grp[bmi > 30] <- ">=30"
                          grp = [3] "<25", "NA", ">=30"
Value: [3] "<25", "NA", ">=30"
RESULT: CLEAN  (0.05 s)

Pipe debugger · dsr pipe file.R:line

Which step lost the records?

The pipeline at the cursor runs step by step. Each step shows its rows before and after, the columns it added or dropped, and the records it dropped, matched by USUBJID and the sequence number. One click shows them.

The Pipeline view of adsl: step 1 filter(ARMCD != Scrnfail) goes from 306 to 254 rows and dropped 52 rows; beside it the data viewer lists the 52 dropped subjects

ADSL of the CDISC pilot: the screen failures the filter dropped, 52 subjects, in the data viewer. Recorded with 0.10.2.

$ dsr pipe 06_pipe.R:4
   0  ae                                                            17 rows
   1  merge(dm[c("USUBJID", "ARM", "TRTSDT")], by = "USUBJID")      17 -> 17     +ARM +TRTSDT
   2  transform(ASTDY = as.numeric(as.Date(AESTDTC) - as.Date(TRTS  17 -> 17     +ASTDY
   3  subset(ASTDY > 1)                                             17 -> 9
        dropped 8 rows, e.g. S-101 / 3; S-102 / 1
   4  subset(AESEV != "MILD")                                       9 -> 6
RESULT: CLEAN  (data after step k: @pipe[k]; rows step k dropped: @pipe[k]:dropped)

Data viewer · dsr describe | freq | means | dups | compare

Your data, one click away. Every filter is R you can paste.

Click a data frame: pages of rows straight from the session, so a large ADaM data set stays quick. Click a value to keep it, type >=75 under a column, press Enter on a cell. R code shows the view as dplyr and base R.

The data viewer on ADAE of the CDISC pilot: four filters as chips, 223 of 1191 rows, and the WHERE they make

ADAE of the CDISC pilot: four filters as chips, 223 of 1191 rows. Taken by the UI check, which compares each count with R.

Finding a column by its label: planned treatment
c finds a column by its name or its label.
Raw values: the codes of a factor
v shows raw values: a factor's codes, a labelled value's number.
  • PROC CONTENTS, FREQ, MEANS, COMPAREdsr describe, freq (also counting subjects), means, dups and compare, on a data frame or a file (.rds, .xpt, .sas7bdat, .csv).
  • One subject, every data setdsr subject 01-701-1015: the subject's records in each data set and the events in time order.
  • At a stop tooThe data tools read a frame of a stop (@pry:adsl) and a pipeline step (@pipe[3]).

Lint · autocomplete · format

Find the mistake before you run.

On save, lint marks a function your installed package does not have, an argument name the function does not take, and a name with a typo, each with "did you mean".

A table program with four marks; the Problems panel: derive_vars_dt has no argument new_vars_prefx (did you mean new_vars_prefix?), derive_vars_dtx does not exist in admiral 1.4.1, a call needs packages that are not installed, a deprecated function

Four mistakes an agent made in a draft, found before the program ran: the editor and its Problems panel (the empty lines between them cut out). Recorded with 0.10.2.

Suggestions after dplyr::filter(AG: AGE with its label Age, AGEGR1, AGEGR1N, AGEU

Autocomplete that knows your data.

In a call or a pipe on a data frame, the suggestions are its columns with their labels, from the live session. Functions with their arguments, hover, signature help, go to definition, and clinical snippets.

before
summ<-function(x){
    n<-sum(!is.na(x)) # count
  c(n=n,mean=mean(x,na.rm=TRUE))}
after
summ <- function(x) {
  n <- sum(!is.na(x)) # count
  c(n = n, mean = mean(x, na.rm = TRUE))
}
proof: only the layout changed: the 35 tokens and 1 comments are the same

Tidy code, with proof that only the layout changed.

Format Document lays out R with styler in your project's style. It writes the file only when the tokens of the code and the comments are the same before and after.

Batch · tests · clinical checks

Done when the checks say so.

A program that ran in your session may still lean on it. A batch run proves it runs alone; the tests run in a clean R process; for clinical work the numbers are compared with their reference.

$ dsr batch 01_run.R 10_table.R
  CLEAN  01_run.R  log: 01_run.log  (0.3 s)
  CLEAN  10_table.R  log: 10_table.log  (0.3 s)
BATCH: CLEAN
$ dsr numbers output/t14_3_01.txt data/pilot/sources/csr.txt --table 14-3.01
output/t14_3_01.txt against data/pilot/sources/csr.txt (Table 14-3.01): 17 of 17 reference rows matched, 75 numbers compared
NUMBERS: MATCH
### 10:12:37 adam check ADSL: 1 problems -> ADAM FAIL | FIRST CAUSE: values:
BMIBLGR1 differs in 1 records, e.g. 01-702-1082: , reference <25
### 10:16:10 test: 37 tests (240 expectations, 6 files), 0 failed, 0 skipped ->
TESTS PASS

The batch run is from the manual's example study; the table check from the CDISC pilot study; the last two lines are the R Console's record of the agent's ADSL work shown above (the ADSL check found one record that differs from the double-programmed reference).

  • dsr numbersA table against a CSR page or a double-programmed table, row by row and number by number at the reference's decimals.
  • dsr adam check, spec, cdiscAn ADaM data set against its specification and, cell by cell, its reference; conformance before a submission.
  • dsr app check, gateA Shiny or teal dashboard opened headless, every page; each number of its contract from the data and on the screen.

The acceptance test: agents do the work

Three tasks in a study project with the pharmaverse data, each given to an agent with only the guide, and graded in a new R process. The trial of 2026-11-12:

TaskModelGradedsr calls
Find and fix a planted wrong derivation in ADAEHaikuPASS29
A table program from its shell, its numbers proved against a CSR pageSonnetPASS30
Refactor a function and prove nothing changedSonnetPASS52, then 26 after the fixes the trial showed

Install

From nothing to your first run in five minutes.

  1. Install

    In VS Code: Extensions, search DataStep Studio for R, Install. Or in a terminal:

    code --install-extension datastep-labs.datastep-studio-r
  2. Open your project

    A folder with R code. The DataStep R icon appears in the activity bar, and the walkthrough Get started on the Welcome page.

  3. Start the session

    The play button in the Session view. If R lacks a package, it says which and the command that installs it; dsr doctor in a terminal checks everything.

  4. Run

    Open an R file and press Cmd/Ctrl+Shift+Enter. The R Console shows the log.

Needs VS Code 1.85 or later (or Positron), R 4.4 or later with Rscript on the PATH, Node.js 18 or later for the dsr command, and the R packages httpuv, later, promises, evaluate, jsonlite, testthat and callr (styler, lintr, yaml and haven add single tools). Positron, Cursor and VSCodium use another extension store: install the .vsix file there (Extensions: Install from VSIX). Step by step: Get started.

Preview 0.10.3

A preview. What it is, and what it is not yet.

  • Built and tested on macOS (Apple silicon) in VS Code and Positron. Windows and Linux: not tested yet.
  • Tested with R 4.4.1. Older R versions: not tested.
  • The editor's checks ran in VS Code and in Positron; the real-click UI check in VS Code only. A folder with an .Rproj file and the R extension for VS Code side by side: not tested.
  • The clinical checks are tools for your own QC. They are not a validated system.

Something breaks or reads wrong? Please say so in the Q & A of the Marketplace page.

Privacy

Your code and your data stay yours.

The extension collects nothing: no telemetry, no usage data. The R session listens on 127.0.0.1 only, with a random token; the editor, dsr and the MCP server talk only to it.

Something leaves your computer only when you choose it: with the Agent panel and Claude Code, what the agent reads goes to Anthropic under your account (Ollama stays local); New Dashboard Project: Set up downloads R packages and the public CDISC pilot data. What talks to what, and how it was checked.

Questions

Do I need an AI agent?

No. Every feature works without one and without an account. An agent uses the same tools.

Does it work in Positron?

Yes: the editor's checks (the session, the debugger with Run to Cursor, pry, format, autocomplete, lint, the data viewer) passed in Positron 2026.11. Positron's own R console runs next to it as a separate R.

Does it work with renv?

Yes. The session starts in the project folder, so the project's .Rprofile and renv library are used.

Does it change my programs?

Only when you ask: Format lays out the file you format, and only with its proof. Running and debugging do not change your files. The session keeps its own files in .triallens/ of the project.

Is it only for clinical trials?

No. The log, the debuggers, the data viewer, lint and format work on any R code. The clinical checks are there when you need them.

How does it relate to DataStep Studio for SAS?

It is its R sibling: the same way of working (a log that points to the line, a data viewer, debuggers, pry), made for how R works. DataStep Studio for SAS.