Skip to contents

putior 0.2.0

Breaking Changes

  • put(), put_auto(), put_generate(), and put_merge() now default to recursive = TRUE (previously FALSE). Directory scans now include subdirectories by default. Pass recursive = FALSE for the old behavior. (#34)

  • put_diagram() output now starts with a frontmatter block that keeps Mermaid 11’s drawing on Mermaid 12 renderers that set a theme: GitHub has drawn Mermaid 12’s “neo” look, with labels wrapped at 120px, since it moved to Mermaid 12.1.0 in October 2026. The block sets the dagre layout, the classic look, a 200px wrapping width and no minimum node width; it sets no theme, so the page’s light or dark theme still applies. Mermaid 9.3.0 to 11.17.2 draw the same with or without it. The new argument mermaid_config takes "classic" (the default), "none" for the previous output, or a named list written as the configuration. The title, if any, shares the frontmatter. The MCP put_diagram tool has the same argument. Mermaid reads the frontmatter only at the very start of the diagram code, and versions before 9.3.0 not at all: text in front of it, such as an %%{init}%% line, makes the diagram fail to parse, so use mermaid_config instead, or "none" for such hosts. (#54)

  • put_diagram() output now also carries accessibility metadata for screen readers, right after the flowchart line: accTitle with the title, if one is given, and accDescr with a summary such as “Workflow with 4 steps and 3 data files.” (.internal variables are not counted as files). Mermaid puts them into the SVG’s <title> and <desc>; the frontmatter title alone does not (Mermaid 10.5.0 and later). The title is escaped so that Mermaid shows it as written: like labels, plus < and %%{, which Mermaid’s sanitizer and directive handling would change. Mermaid reads these lines from 9.1.0 on; for an older renderer use accessibility = FALSE together with mermaid_config = "none". The MCP put_diagram tool has the same argument. ?put_diagram and GUIDE.md now list the Mermaid versions that putior supports and that its CI tests. A decision node with the id accDescr is now written as node_accDescr: accDescr{ started an accDescr block, and the node lost its shape and label. (#50)

  • With show_artifacts = TRUE, put_diagram() now draws each data hop once, through the file’s node (script --> file --> script). Before, it also drew a direct edge between the two scripts, so every hop appeared twice and the diagram had about 1.5 times the edges it needed, which brings Mermaid’s 500-edge limit closer. A script that another script reads (such as utils.R) has no file node and keeps its direct edge. artifact_edges = "both" draws the direct edges as before; the MCP put_diagram tool has the same argument. (#55)

New Features

Everything in this section is new since putior 0.1.0. Where an entry says what changed or what happened before, it describes development versions of 0.2.0, not putior 0.1.0.

MCP Server Integration

  • Added putior_mcp_server() to expose putior functions as MCP tools for AI assistants
  • Added putior_mcp_tools() to get/filter tool definitions for custom MCP integrations
  • 16 tools available: put, put_diagram, put_auto, put_generate, put_merge, get_comment_prefix, get_supported_extensions, list_supported_languages, get_detection_patterns, get_diagram_themes, putior_guide, putior_help, set_putior_log_level, is_valid_put_annotation, split_file_list, ext_to_language
  • Supports Claude Code, Claude Desktop, and other MCP-compatible clients
  • Tools return JSON (workflow data as one object per node) or plain text. They print nothing, because with the stdio transport stdout is the protocol channel, and never write files or the clipboard, so every tool except set_putior_log_level is read-only. The put_diagram tool scans a path and returns the Mermaid code. (#65)
  • The put_diagram tool takes a source argument: annotations (the default) scans with put(), auto draws the workflow that put_auto() detects, and merge combines both like put_merge(), with merge_strategy. When a scan finds nothing and R warned (the directory is empty, no file matches pattern, a file cannot be read), the scan tools now fail with R’s warnings instead of returning [] or empty text: the warnings never reached the client. When nothing warned, put_diagram says what is missing for its source: no annotations (and it suggests source = "auto"), or no file that put_auto() can analyze. (#73)
  • An argument that a client sends as null gets its default. Before, most arguments of the scan and diagram tools and of list_supported_languages then stopped the call with “argument is of length zero”.
  • R warnings now reach the client. Over stdio they never did: they were written to stderr when the server stopped, so a < in a title or label, a diagram over Mermaid’s limits or an invalid annotation went unnoticed. A tool that warned returns the warnings (at most 20, each cut to 1,000 characters) as a second text item after its unchanged result. For put_diagram they are Mermaid comment lines, so the diagram still renders when a client joins both items, and a %%{ in a quoted label cannot act as a directive. An error lists the warnings raised before it. The MCP server needs mcptools 1.0.0 or later, which passes such a list on; older versions turned it into "<object>" text.
  • putior_mcp_server() serves only its 16 tools. mcptools’ tools for choosing a running R session (list_r_sessions, select_r_session) were on as well, so clients saw 18 tools, and a call could run in a user’s live R session instead of the server’s own process.
  • Over HTTP (type = "http"), the tools scan only the folder given as root (default: the working directory), with the ACP server’s rules, and replies show the root as <root>; before, a client could scan any folder the server’s R process could read. put_diagram refuses enable_clicks over HTTP, as its links name files by their path on the server. A token makes mcptools require the header Plumber-Shared-Secret: <token>; a server on an address other than a loopback one refuses to start without one. Without a token, the server answers only requests addressed to a loopback name (against DNS rebinding). mcptools’ own settings (trusted hosts, shared secret) stay in force. Before serving, the server tests that mcptools refuses requests without the token or from an untrusted Host, and does not start if it does not. Over stdio nothing changes. ?putior_mcp_server describes the security model. (#111)

ACP Server Integration

  • Added putior_acp_server() to expose putior as an ACP agent for inter-agent communication via REST API
  • Added putior_acp_manifest() for agent discovery
  • Supports natural language requests for all core operations
  • Requests may scan only the folder given as root (default: the working directory) and what lies below it; a path outside it, also through a symbolic link, is rejected, and a scan skips files that links inside it lead outside to (a folder with more than 200 such links is refused). Before, a request could name any absolute path the server’s R process could read. Network and device paths are rejected before the disk is touched, and messages show the root as <root>. A token makes the server require Authorization: Bearer <token> on /runs; a server on an address other than a loopback one refuses to start without one. Without a token, the server answers only requests addressed to a loopback name (against DNS rebinding). POST /runs needs Content-Type: application/json, and a Content-Length over 1 MB is refused; these checks, the Host and the token come before the request is handled. Run IDs are 128 random bits from openssl, also without the uuid package, where they were a timestamp and a counter that another client could guess. ?putior_acp_server describes the security model. (#111)
  • A path with .. or control characters ends the run with an error; it used to be replaced by . with a warning in the server log, so the run scanned the server’s working directory. An unquoted ../ path is now recognized, so it is rejected too instead of being ignored.
  • Unquoted paths in a request work: scan ./R/ read the path as ./R/, with the space in front, and reported that it does not exist.
  • Requests name paths in more forms: Windows paths (D:/data, C:\Users\me), ~/ paths, relative paths such as R/ that exist, and paths in backticks or curly quotes; punctuation after a path (./R?) is dropped. A path-like word wins over a quoted word that is not one (for ./R/ using the 'dark' theme, titled "ETL" for ./R/), a theme or direction name is never the path, and an apostrophe (“Let’s”) is no quote. A lone /, \, ~ or drive in prose is not taken for the root of a drive or the home directory. A scan with no path in the request reads the server’s root folder and says so; before, an unrecognized path silently gave the results of another folder. A .. after a drive (D:..\x) is rejected like any other. (#109)
  • The server works over HTTP. Every POST /runs failed with “500 Internal Server Error”: plumber2’s JSON parser turns the request’s input into a data frame, which the request handling did not expect. The tests had called the handlers directly; a test now sends real requests to a running server. Responses now give scalars as JSON scalars ("name": "putior", not ["putior"]), a missing session id as null, and GET /runs/<run_id> returns a stored run. (#113)
  • Warnings that R raises during a run (invalid annotations, a < in a label, …) come as a second text/plain part of the output; the server only logged them.
  • A scan whose annotations name no input (or no label) is listed; the run stopped with “missing value where TRUE/FALSE needed”.

Multi-Language Comment Syntax (30+ Languages)

Auto-Detection Patterns (18 Languages, 902 Patterns)

  • Full auto-detection for: R, Python, SQL, Shell, Julia, JavaScript, TypeScript, Go, Rust, Java, C, C++, MATLAB, Ruby, Lua, WGSL, Dockerfile, Makefile
  • Pattern inheritance: TypeScript extends JavaScript, C++ extends C
  • Single authoritative language registry (R/language_registry.R)

LLM Detection Patterns

  • 54 patterns for modern AI/ML libraries across R and Python
  • R: ellmer, tidyllm, httr2 (API requests)
  • Python: ollama, openai, anthropic, langchain, transformers, litellm, vllm, google generative AI, groq

Auto-Annotation Feature

  • Added put_auto() for automatic workflow detection from code analysis
  • Added put_generate() for generating PUT annotation comments (roxygen2-style)
    • Now supports output = "raw" for programmatic access
  • Added put_merge() for combining manual and auto-detected annotations
    • Three merge strategies: “manual_priority”, “supplement”, “union”

Colorblind-Safe Themes

  • Added 4 viridis family themes for accessibility:
    • viridis: Purple-blue-green-yellow (general accessibility)
    • magma: Purple-red-yellow (high contrast, print-friendly)
    • plasma: Purple-pink-orange-yellow (presentations)
    • cividis: Blue-gray-yellow (deuteranopia/protanopia optimized)
  • All themes perceptually uniform and tested for color vision deficiencies

Custom Theme API

  • Added put_theme() for creating custom color palettes (#39)
  • New palette parameter on put_diagram() accepts putior_theme objects
  • Override individual node types (input, process, output, decision, artifact, start, end)
  • Base theme inheritance — customize only what you need. An override keeps every key it does not set from the base theme’s style for that node type, including the text color and the border width (3px for start and end nodes, 1px for artifacts); earlier development versions dropped the unset keys and forced a 2px border (#61). If an override changes how light fill is, set color too, so the label stays readable

Block Comment Annotations

  • Added PUT annotation support inside /* */ and /** */ block comments (#37)
  • Use * put as line prefix inside block bodies for all //-prefix languages
  • Supports single-line block comments and JSDoc-style patterns

File Exclusion

WGSL Shader Language Support

  • Registered .wgsl with // comment prefix (#31)
  • 17 auto-detection patterns for GPU bindings, textures, samplers, and naga-oil imports

Dockerfile Support

  • Handle extensionless Dockerfile via .FILENAME_MAP (#35)
  • 13 detection patterns for FROM, COPY, ADD, EXPOSE, VOLUME, CMD, RUN

Makefile Support

  • Handle Makefile and GNUmakefile via .FILENAME_MAP (#36)
  • 10 detection patterns for include, wildcard, target rules, install, shell commands

Metadata Display

  • Added show_source_info parameter to put_diagram() for displaying source file information in diagram nodes
  • Added source_info_style parameter with “inline” and “subgraph” display options
  • Added enable_clicks parameter to put_diagram() for making nodes clickable
  • Added click_protocol parameter: “vscode” or “file”. The development version also offered “rstudio”, which was removed before release: RStudio has no URL scheme for opening files, so rstudio:// links did nothing. click_protocol = "rstudio" now warns and writes file:// links. (#101)
  • Click tooltips name the line (“Open a.R at line 3”) only for vscode:// links, which open that line; file:// links have no line number.
  • Click directives include line numbers when available

Structured Logging

  • Optional logger package integration for debugging annotation parsing
  • Log levels: DEBUG, INFO, WARN (default), ERROR
  • Added set_putior_log_level() for configuration
  • Per-call override via log_level parameter on core functions

S3 Workflow Class

  • Workflow data frames now have putior_workflow S3 class
  • Added print.putior_workflow() for concise workflow summaries
  • Added summary.putior_workflow() for structured summary information
  • putior_workflow inherits from data.frame, so code that uses the result as a data frame keeps working

Interactive Sandbox

  • New run_sandbox() opens an interactive Shiny app for trying PUT annotations: paste or type annotated code, simulate several files, and see the workflow diagram update as you edit (needs the shiny package)
  • Added copy-to-clipboard button for quick Mermaid code export
  • Added optional shinyAce syntax highlighting (graceful degradation)
  • The sandbox loads Mermaid 12.2.0 from jsdelivr, pinned with a Subresource Integrity hash (#48). It draws diagrams in Mermaid 11’s classic style (dagre layout, classic look, 200px label wrapping, no minimum node width), which putior’s themes are made for and which GitLab, Quarto and VS Code show. GitHub moved to Mermaid 12 in October 2026 and draws putior diagrams in the neo look with labels wrapped at 120px (#54). A diagram with a syntax error shows Mermaid’s error message. Mermaid 12 needs a current browser (ES2024; Safari 17.4 or later)
  • File markers (# ===== File: name.R =====) are read one line at a time, with any spaces or tabs around them, at least three = and an optional closing run, so an unclosed marker no longer takes the next file with it and a name containing = is recognized. When two simulated files have the same name (ignoring case), both are kept, the later one in a copy_2/ folder, and the status line says so; so does text before the first marker, which is ignored. File names are made safe to write on Windows (trailing dots dropped, characters such as : or ? replaced), and a file that still cannot be written is left out with a note instead of emptying the whole diagram (#53)
  • If Mermaid cannot be loaded from jsdelivr (no network, a blocked CDN), the preview says so instead of staying blank, and the copy button and the Mermaid Code tab still work. A diagram that changes while the preview is hidden is drawn as soon as the preview is visible again, whether its tab is shown or a collapsed pane is opened (#64)

Documentation

  • Documented all valid node_type values across vignettes and reference (#40)
  • 8 comprehensive vignettes including quick-start, annotation guide, features tour, API reference, showcase, troubleshooting, quick-reference, and AI integration
  • Professional cheat sheet (inst/cheatsheet/putior-cheatsheet.qmd)
  • 20 example files in inst/examples/
  • putior_guide() and putior_help() for in-session reference
  • The 0.1.0 vignette getting-started is replaced by quick-start and annotation-guide

Improvements

  • put_diagram() checks direction: it takes "TD", "TB", "LR", "BT" or "RL" (or an unambiguous abbreviation, such as "L"), and other values give an error. 0.1.0 wrote any value into the diagram, which Mermaid then could not parse.
  • put_diagram() warns when a diagram is over Mermaid’s default limits of 500 edges (maxEdges) or 50,000 characters (maxTextSize). Renderers such as GitHub, GitLab and Quarto then show an error or an empty placeholder, and the diagram cannot raise either limit itself. The size is counted as mermaid.render() counts it (without the frontmatter and comments, with the newline that ends a code block), and the Mermaid check confirms both limits at the boundary. Hosts that use mermaid.run(), such as pkgdown sites, count up to a few characters less, so the warning there can come a little early, never late. The warning names the options; ?put_diagram and the troubleshooting vignette describe both limits. Large diagrams are also much faster to generate: a 500-step chain took about two minutes, because every input looked through every step’s outputs, and now takes about two seconds. (#56)
  • Standardized output parameter across put_diagram(), put_generate(), and putior_guide() – all now support “raw”

Bug Fixes

  • Fixed pipe characters (|) in node labels breaking Mermaid diagram parsing. Pipes are now escaped using the #124; entity. (#33)
  • put() now gives annotations without an id a fallback id (node_1, node_2, …) when the uuid package is not installed. Before, such annotations had no id: if none had one, put_diagram() failed with a misleading “missing required column(s): id” error, and if only some had one, it silently dropped the others. This broke the quick-start example and the sandbox templates for anyone without uuid. The sandbox now also shows diagram errors in its status line instead of a blank preview. (#58)
  • Annotation values may now contain the other kind of quote: label:"Don't panic" and label:'Say "hi"' keep their labels. Before, put() dropped any value with a quote character in it, without a warning, and the node showed its id instead; the troubleshooting vignette even recommended label:"Load 'raw' data". There are still no escapes, so a value cannot contain its own kind of quote, and backslashes in Windows paths stay as they are. A property that put() cannot read is now named in its validation warning (with validate = TRUE, the default) instead of being dropped silently, and is_valid_put_annotation() returns FALSE for such a line. An annotation that ends with a continuation \ (without a comma before it) on the last line of the file no longer loses its last property. put_generate() wraps a value that contains a double quote in single quotes, so that put() reads its suggestions back unchanged. (#91)
  • A comment may now follow the values of an annotation, as the put() help page, the vignettes and the cheatsheet have always shown (# put id:"a", label:"Load" # first step). Before, the comment made the last property unreadable, and it was dropped. Outside quotes, whitespace followed by #, --, // or % after a value starts the comment. In a multiline annotation, a comment may also come before the continuation \ of a line. (#91)
  • put_diagram() gives every node one Mermaid id and uses it in the node definition, edges, class lines and click directives. Before, edges to an artifact whose path has two punctuation characters in a row (such as ./raw data (v2).csv) used a different id than the artifact node, so Mermaid drew an extra unlabeled node and left the artifact unconnected. Annotation ids that differ only in punctuation (a-b and a_b) were merged into one node with a self-loop; they now stay two nodes (a_b, a_b_2) and put_diagram() warns. Two data files whose names differ only in punctuation get two artifact nodes. put_auto() and put_generate(), which derive ids from file names, now give files that would share an id (a-b.R and a_b.R, end.R and node_end.R, or load.R in two folders) distinct ids (load, load_2). put_generate(output = "file") now writes each annotation next to the file it belongs to; when a file in between gave no annotation, the next one went into the wrong .put file. (#46)
  • Node ids that are Mermaid keywords (end, graph, flowchart, subgraph, style, class, classDef, click, linkStyle, call, href, interpolate, and the link targets _self, _blank, _parent, _top) made the whole diagram fail to parse. They are now written as node_end and so on. id:"end" is a natural name for a pipeline’s last step. (#45)
  • With show_files = TRUE, edge labels are now quoted and escaped like node labels (-->|"raw data (v2).csv"|). A file name with parentheses or other special characters made the diagram fail to parse. (#45)
  • The title of put_diagram() is now written as a quoted YAML string. A title with : (such as "ETL: stage 1") made the diagram fail to parse, and text after # was dropped as a YAML comment. (#45)
  • A # in a label that Mermaid would read as a character entity is now escaped as #35;. Mermaid decodes #<name>; and #<digits>;, so a label such as Step #35; was shown as Step #. A plain # (C#, item #1) is left alone. ?put_diagram now describes how labels and ids are written. (#45)
  • Labels that contain Mermaid syntax are now written so that Mermaid shows them as written. Mermaid read a line with direction, whitespace and TB, BT, RL or LR (and TD from Mermaid 11.17.2 on), as in “Change direction LR to TB”, as a direction statement and dropped it: the node lost its label, and with source_info_style = "subgraph" a file named direction LR.R made the diagram fail to parse. That whitespace is now written as an entity (direction#32;LR). The same rule spans line breaks, so an edge to an id ending in direction (wind_direction) followed by an edge from an id starting with LR (LR_prep) lost both edges; such an edge now ends with ;. On a line where style or classDef comes before it, in the label or in the node id, a : followed by an escaped character, as in style:"bold" (written style:#quot;bold#quot;), is now written as #58;: Mermaid removed the last ; of such a line and showed style:"bold#quot. (#89)
  • A %%{ in a node, edge or subgraph label is now written with its first % as #37;, so that Mermaid shows it as written. Mermaid removes %%{...}%% directives from the whole diagram text before it parses it, so a label such as “Budget 50%%{x: draft” was cut short or made the diagram fail to parse, and with a later }%% the text in between was lost. put_diagram() now also warns when the title contains a <: the drawn title then shows HTML entities such as &lt;, &gt; and &amp; and drops text that looks like an HTML tag, and no escape avoids that. The accTitle line shows the title as written. (#98)
  • A label with U+2028 or U+2029 (line and paragraph separator) followed by %% made the diagram fail to parse: Mermaid removes comment lines with a JavaScript regex that treats these characters as line ends. They are now written as spaces, as line breaks are. The title, the accTitle line and click tooltips already replaced them, except in the C locale; they now do so there too. (#104)
  • put_diagram() now warns when a drawn label contains a < before a letter, /, ! or ?, such as n<threshold or <draft>: Mermaid draws labels as HTML and reads it as the start of a tag, so the text up to the next > disappears (a<b and c>d shows “ad”). The docs show &lt; for a < that should show (\lt in $$...$$ math). The tags br, b, i, em, strong, sub, sup, code and span still count as intended HTML. (#102)
  • Fixed documentation using deprecated name instead of id in examples
  • Replaced unsafe 1:nrow() loops with seq_len(nrow()) to handle empty data frames

Fixes to features that are new in 0.2.0

These entries fix features that were not in putior 0.1.0, or checks added while 0.2.0 was developed. They matter only to users of a development version.

  • Renamed putior_skills() to putior_guide(). The function, its MCP tool (putior_guide), and its ACP operation (guide) all use the new name. putior_help("skills") still works as an alias for backward compatibility. The inst file was renamed from SKILLS.md to GUIDE.md, and the vignette from skills.Rmd to ai-integration.Rmd.
  • put_diagram() and the MCP and ACP servers accept direction = "TB", Mermaid’s synonym for "TD". GUIDE.md named TB as the default, but "TB" was rejected with an error. (#79)
  • Click links (enable_clicks = TRUE) now use the path the scan recorded (file_path) instead of the file’s base name, which was resolved against the working directory at the time the diagram was made. Absolute Unix paths give vscode://file/home/... and file:///home/... instead of a doubled or quadrupled slash, UNC paths (//server/share/...) give vscode://file//server/... and file://server/..., and a relative path to a missing file is made absolute. Paths are percent-encoded, and tooltips escaped like labels: a quote in a file name made the diagram fail to parse, and a name such as direction LR.R made Mermaid drop the click line. The docs now say that Mermaid keeps click links only with securityLevel: "loose", so they do not work on GitHub, GitLab or in Quarto with default settings. The pkgdown site and the sandbox stay "strict", because "loose" would also let click callbacks run JavaScript. (#47)
  • Fixed show_source_info = TRUE using <small> HTML tags that are unsupported by GitHub’s Mermaid renderer. Source file names are now shown without HTML formatting tags. (#32)
  • Fixed logging, which wrote nothing with logger 0.3 or later: every log call failed inside logger and the error was hidden. The putior.log_level option, set by set_putior_log_level() or for one call by the log_level argument, is now the only threshold, and an invalid log_level is an error. With logger installed, WARN-level messages now appear on stderr next to the matching R warnings. (#70)
  • Fixed single-line block comment annotations (/* put ... */) silently losing their last property, which could drop an output and its edge. (#72)
  • put_merge() no longer merges a manual annotation and an auto-detected file into one node when their ids meet: auto-detected rows get a suffix when their id (also in its Mermaid form, where 1 is node_1) is already used, so an unannotated node_1.R next to an annotation with the fallback id node_1 stays separate. It also matches manual and auto-detected rows by file path, so an annotated x/load.R no longer hides an unannotated y/load.R, and with merge_strategy = "supplement" or "union" no longer takes the inputs and outputs detected in the other file. (#74)
  • With source_info_style = "subgraph", subgraph ids are now file_<name> and never equal a node id. Before, a file load.R with a node id:"load" failed to render (“would create a cycle”) on Mermaid 11, and on Mermaid 12, which GitHub uses, the node silently disappeared. Files with the same name in different folders now get one subgraph each instead of sharing one, and the subgraph label then includes as much of the folder path as it takes to tell them apart (x/load.R, a/x/load.R). (#45, #46)
  • Fixed ACP put_generate() called with invalid output = "raw" (now supported)
  • Fixed MCP theme enum missing viridis family themes
  • Fixed ACP theme extraction regex missing colorblind-safe themes

putior 0.1.0

CRAN release: 2025-06-19

  • Initial CRAN submission
  • Added put() function for extracting workflow annotations from source code files
  • Added put_diagram() function for creating Mermaid flowchart diagrams
  • Added is_valid_put_annotation() for validating annotation syntax
  • Support for multiple programming languages: R, Python, SQL, Shell, and Julia
  • Multiline annotation support with backslash continuation syntax for better code style compliance
  • Automatic UUID generation when id field is omitted from annotations
  • Automatic output defaulting to file name when output field is omitted
  • Renamed annotation field from name to id for better graph theory alignment
  • Five built-in themes for diagrams: light, dark, auto, minimal, and github
  • Automatic file flow tracking between workflow steps
  • Comprehensive vignette with examples
  • Full test coverage for all major functions