github robotcodedev/robotcode v2.6.0

4 hours ago

Features

  • analyze: Add --code filter to code subcommand (4bfc339)

    --code restricts the result to the given diagnostic codes (e.g.
    KeywordNotFound), repeatable and comma-separated, matched
    case-insensitively like the modifiers. Unlike -mi "*" + re-include, it
    filters without changing the severity of what remains. It affects the
    output, summary and exit code, and combines with --severity using AND.

  • analyze: Add --severity filter to code subcommand (f565c16)

    --severity restricts the analysis result to the listed severities
    (repeatable and comma-separated; error/warn/info/hint). Filtered-out
    severities are dropped from the output, the summary counts and the exit
    code alike, so all three stay consistent. It's orthogonal to
    --exit-code-mask (which only masks the exit code) and complements the
    diagnostic modifiers (which remap rather than filter).

  • analyze: Add SARIF, GitHub and GitLab output formats (7103836)

    robotcode analyze code gains a local --output-format flag (overrides
    the global --format for this command) with these values:

    • concise (default): the human-readable text output
    • json / json-indent: structured result (diagnostics + summary)
    • sarif: SARIF 2.1.0 log for GitHub code scanning and other consumers
    • github: GitHub Actions workflow annotations (::error file=...)
    • gitlab: GitLab Code Quality report (JSON array)

    --output-file FILE writes the report to a file instead of stdout (for
    the json/json-indent/sarif/github/gitlab formats), which is handy for CI
    artifact upload. A missing target directory raises a clear UsageError and
    other I/O errors surface as a ClickException instead of a traceback.

    Mapping details shared across the machine formats:

    • LSP 0-based ranges become the 1-based positions SARIF/GitHub expect
    • severities collapse onto each format's levels (e.g. SARIF error/warning/
      note, GitHub error/warning/notice, GitLab major/minor/info)
    • artifact paths are POSIX and relative to the project root (what the CI
      integrations expect); --full-paths switches to absolute paths
    • SARIF and GitLab results carry a stable fingerprint (independent of line
      number) so alerts survive unrelated edits
    • GitHub property/message values are escaped per the @actions/toolkit rules
  • analyze: Add JSON output and --full-paths to code subcommand (fc76cf3)

    robotcode --format json analyze code (and --format json_indent) now
    emit a structured result instead of falling back to plain text:

    • diagnostics: a map of source path to LSP diagnostics, using the same
      shape as the discover/results commands so editor and CI integrations
      can reuse their parsers. Workspace-level diagnostics key on ".".
    • summary: file count and per-severity totals.

    The JSON always carries the full diagnostic message (tracebacks included);
    --show-tracebacks only affects the text output.

    New --full-paths/--no-full-paths flag (mirroring discover) switches
    between root-relative and absolute paths in both text and JSON output.
    JSON keys are always rendered POSIX-style for cross-platform stability.

  • analyze: Polish code subcommand output (1049b79)

    • Output is now sorted by (file, line, column) so warnings and errors
      appear in source order instead of mixed by analyzer pass.
    • Severity is rendered as a full word, e.g. [ERROR] instead of [E],
      matching the convention from Robocop, ruff, mypy and friends.
    • Robot Framework's appended Python tracebacks and PYTHONPATH dumps are
      no longer printed by default; the new --show-tracebacks flag opts
      back into the full message.
    • Multi-line diagnostic messages (e.g. MultipleKeywords listing the
      alternative names) are re-indented with a consistent prefix.
    • Related-information entries are prefixed with -> to set them apart
      visually, and they fall back to (see related location) when Robot
      Framework provides no secondary message.
  • cli: Detect GitHub Copilot agent sessions (dbc5603)

    Adds two markers to the AI-agent auto-detection so terminal commands
    invoked from Copilot get the same clean-output defaults (no color,
    no pager, REPL plain backend) as sessions from Claude Code, Cursor,
    Codex, and friends:

    • COPILOT_AGENT — set by VS Code Copilot Chat / agent mode in
      terminals launched via run_in_terminal
      (microsoft/vscode#311734).
    • COPILOT_AGENT_SESSION_ID — set on every shell command and MCP
      server the standalone GitHub Copilot CLI spawns (>= 0.0.429).
  • cli: Adapt automatically to AI agent sessions (8b34a33)

    When robotcode is invoked from inside an AI agent (Claude Code,
    Cursor, Copilot CLI, OpenCode, Codex, Gemini, …) it now drops
    coloured output, the pager, and the REPL's interactive prompt
    features — so the agent sees clean line-by-line text instead of
    ANSI escapes and popup ghosts.

    Explicit flags and the usual environment variables (NO_COLOR,
    FORCE_COLOR, ROBOTCODE_REPL_PLAIN, ROBOTCODE_REPL_BACKEND)
    still override the detection. ROBOTCODE_NO_AI_AGENT=1 opts out
    when you want the full experience anyway; ROBOTCODE_FORCE_AI_AGENT=1
    turns the agent-mode on for local repros.

  • cli: Auto-detect when to emit colors, honor NO_COLOR and FORCE_COLOR (4bbf944)

    ANSI escape codes no longer leak into piped output, files, log captures or
    CI artefacts — colors are auto-disabled when stdout/stderr isn't a TTY.
    The NO_COLOR and FORCE_COLOR environment variables are now respected.
    stdout and stderr are decided independently, so warnings keep their colour
    when stdout is piped but stderr is still on the terminal. Explicit --color
    / --no-color still override everything.

  • discover: Show a diagnostics summary, full list only on demand (4b96656)

    robotcode discover no longer prints Robot's parsing errors and warnings
    by default. When a suite has parse issues, a compact Diagnostics section
    with the error/warning counts now appears after the statistics, separate
    from the discovery statistics.

    Pass --diagnostics to list the full messages; they are printed before
    the discovered results, with file paths shown the same project-relative
    way as the rest of the output. The machine-readable JSON output keeps
    including the full diagnostics regardless, so editors are unaffected.

  • discover: robotcode discover TEXT output is now markdown (a7ef8bc)

    robotcode discover subcommands now emit markdown for human
    output. On a coloured TTY rich renders it to themed ANSI and
    pages if needed; in a pipe (or with --no-color) the raw markdown
    is emitted verbatim. JSON output is unchanged.

    • info renders as a field/value table.
    • files, suites, tests, tasks emit as bullet lists; tags shows
      one bullet per tag with nested tests/tasks when --tests / --tasks
      is on.
    • all keeps the workspace → suites → tests tree as a nested
      markdown list, each level indented two spaces.
    • Statistics blocks are markdown tables.

    The generic markdown helpers used by results — md_table,
    bold_status, path_paren, display_width, the search highlighter
    — move from results/_render.py to a shared
    cli/_markdown.py so both commands import them from one place.

    Same commit aligns the existing results renderer on the new
    label convention: bold is reserved for entities (test/suite
    /tag names, status); italic marks labels (Tags, Total,
    Extracted, Summary, metadata keys); (path:line) references
    are wrapped in inline-code spans so they read as code tokens.

  • docs: Add interactive hero image carousel with responsive lightbox (edd6d22)

  • language-server: Use the SemanticModel for the "Create Keyword" and "Assign Result to Variable" code actions (6a3d5a3)

    When the experimental SemanticAnalyzer is enabled, the "Create Keyword"
    quick fix (offered for unknown keywords) and the "Assign keyword result
    to variable" refactor read the keyword call's namespace, library entry
    and assignment state from the SemanticModel instead of walking the AST
    and re-resolving via ModelHelper. Output unchanged — covered by
    dedicated equivalence test files for both code actions.

  • language-server: Use the SemanticModel for the "Open Documentation" code action (0768c84)

    When the experimental SemanticAnalyzer is enabled, the "Open Documentation"
    code action reads the pre-resolved keyword data and SemanticTokens from
    the model instead of looking the keyword up again on every request.
    Output is unchanged — verified by the existing E2E suite under both paths
    and a new equivalence test file.

  • language-server: Use the SemanticModel for signature help (9415e86)

    When the experimental SemanticAnalyzer is enabled, signature help reads
    the pre-resolved keyword data from the SemanticModel instead of looking
    the keyword up again on every request. Output is unchanged — verified by
    running the existing regression suite under both paths and a dedicated
    equivalence test file.

  • repl: Suggest the setting-import aliases in tab-completion (6507af5)

    Typing at the prompt now suggests Library, Resource, and Variables next to the keywords, and completing their argument offers the same libraries, resources, and variable files as Import Library / Import Resource / Import Variables — so the Settings-style aliases are as discoverable as the keywords they stand for. Offered only at the interactive prompt, where they apply.

  • repl: Make the interactive shell and debugger more consistent (1a11c80)

    Several rough edges in the interactive shell and its command-line debugger are smoothed out, so it behaves consistently — both with the rest of Robot Framework and within itself:

    • Failures behave like a shell, not a debugger. A failing keyword prints its error and leaves you at the prompt; the debugger only steps in when asked for, and switching it off keeps your breakpoints so switching it back on picks up where you were. (robotcode robot-debug is unchanged for debugging a real run.)
    • Imports work like they do in a suite. Write Library, Resource, or Variables at the prompt just as under *** Settings ***; saved sessions keep that familiar layout.
    • The debugger speaks one language. Its commands and messages now follow one short, uniform style instead of a mix of phrasings and cryptic notes.
  • repl: Add an interactive command-line debugger (8e62733)

    Debug Robot Framework runs straight from the terminal — pause a run, see
    where you are, inspect and change state, run keywords in the live context,
    and step through execution, no editor required. It's the scriptable,
    terminal-native counterpart to the VS Code extension's graphical debugger.

    • Two ways in. robotcode robot-debug <paths> (alias run-debug) runs a
      real suite through the normal runner with the debugger attached — same
      output, options and arguments as robotcode robot, and the run keeps
      streaming as you step, continue or detach. robotcode repl (alias shell)
      now has the debugger built in too, so breakpoints fire while you try
      keywords at the prompt.
    • Pause where you want. Line and keyword breakpoints (--break login.robot:42, --break "Open Browser"), an embedded Breakpoint step in
      a .robot file, --stop-on-entry, and breaking on failures (uncaught
      failures by default, plus --break-on-all-exceptions, --break-on-failed-test
      and --break-on-failed-suite). Every stop shows why, which keyword, and where.
    • Step through it. .step/.next/.return/.continue/.until, plus
      .detach (stop debugging but let the run finish) and .abort (end it now).
    • Inspect and change state. Walk the call stack (.where/.up/.down/
      .frame), list variables by scope (.vars), evaluate in the selected frame
      (.print/.pprint/.whatis), auto-show expressions at every stop
      (.display), set variables (.set), and run any keyword right at the prompt
      in the paused context.
    • Manage breakpoints live. List them and add conditional, one-shot,
      logpoint or command-list breakpoints; set conditions, ignore N hits, delete,
      and disable/enable by number (.breakpoints/.condition/.ignore/.delete/
      .disable/.enable/.commands/.tbreak). Arm exception breakpoints on the
      fly with .catch.
    • Read source and docs in place. .list shows the source at the stop,
      .source <kw> a named keyword's definition; the full shell command set
      (.kw, .doc, .imports, .save, …) works at the debug prompt too. On the
      rich prompt these open in a scrollable full-screen viewer with search and
      links; on the plain prompt they print inline.
    • Built for humans and agents alike. Context-aware completion, syntax
      highlighting, persistent history and multi-line editing on the rich prompt;
      --plain (or --backend / ROBOTCODE_REPL_*) gives a bare prompt that reads
      commands from stdin, and the rich prompt falls back to plain automatically on
      piped input — so the debugger drives cleanly from scripts, CI or AI agents.
    • Long commands accept any unambiguous prefix; one-letter aliases mirror pdb.
  • repl: Browse and search keywords from .kw (a884736)

    .kw does more than show a single keyword now. With no argument it
    lists every keyword you have imported, grouped by the library or
    resource it comes from. With text that isn't an exact keyword name it
    lists the keywords whose name contains that text, so you can find a
    keyword without remembering its full name or leaving the REPL. Giving an
    exact keyword name still shows that keyword's full documentation as
    before.

    In the interactive documentation viewer the listed keyword names are
    links: Tab to one and press Enter to open its documentation, and press
    [ to go back to the list — the same navigation the cross-reference
    links inside a library page already use. On the plain backend the list
    comes back as text.

  • repl: Document imported resources and aliased/argument libraries in .doc and .kw (3d1665d)

    The .doc and .kw commands show documentation for the libraries,
    resources, and keywords you are working with in the REPL. They now cover
    everything the current session has imported and match how it was imported.

    .doc documents imported resource files, not just libraries — the
    resource introduction plus every keyword with its arguments and
    description. It also works for libraries that were imported with
    arguments, which previously could not be shown. Libraries and resources
    are addressed by the name they were imported under, so a library imported
    with an alias (AS, or the older WITH NAME) is found by that alias.

    .kw accepts the explicit Owner.Keyword form, e.g. .kw BuiltIn.Log,
    to pick a specific keyword when the same name is provided by more than one
    import; names resolve the same way they do in a suite. It now also shows
    full documentation for resource keywords, including the argument table.

    Running .doc or .kw on something you haven't imported tells you it
    isn't loaded instead of showing an empty page. The built-in help for both
    commands has been rewritten to read as user help rather than internal notes.

  • repl: Show a startup banner (73eb26a)

    When the REPL starts in an interactive terminal it now prints a
    Python-style banner with the RobotCode, Robot Framework, and Python
    versions, plus a hint about .help and .exit. The banner is skipped
    when stdin is piped/redirected or when running --files without
    --inspect.

  • repl: Documentation viewer and themed prompt out of the box (dda4258)

    .help, .kw, and .doc (or F1 for help) open a built-in
    documentation viewer — a tiny terminal browser for Robot Framework
    docs without leaving your REPL session.

    • Search with /, jump between matches with n/N.
    • Click a link, or use Tab/Shift-Tab to step between them. Anchor
      links jump to the right section, web links open in your browser.
    • Walk back and forward through your reading history with [ and ].
    • Scroll with the mouse wheel, or with j/k, PgUp/PgDn, g/G.
    • Pages use the same renderer the editor shows on hover: signature,
      argument table with types and defaults, tags, docstring, headings,
      lists, tables, code blocks.

    A single color theme covers the prompt, syntax highlighting, log
    lines (INFO, WARN, ERROR, …), and the documentation viewer.

    prompt_toolkit and rich are bundled with robotcode-repl, so the
    rich prompt experience is available out of the box. --plain (or
    --backend=plain) is available for AI-agent sessions and pipe
    workflows that need clean stdin/stdout.

  • repl: Same prompt experience on Linux, macOS, and Windows (f8b8b15)

    The interactive REPL ships with a single editor that behaves the
    same way on every platform. Install the [prompt-toolkit] extra
    to enable it:

    pip install 'robotcode-repl[prompt-toolkit]'
    

    You get a completion popup, syntax highlighting, signature toolbar,
    persistent history and Ctrl-R search. Without the extra the prompt
    degrades to a plain input() line read.

    The previously implemented readline-based path has been removed —
    its behaviour differed too much across platforms and had platform-
    specific bugs that weren't worth maintaining.

  • repl: --backend flag to force a specific input backend (372afca)

    Lets you exercise the readline (or plain) code path without
    uninstalling the prompt_toolkit extra:

    robotcode repl --backend=readline
    
  • repl: Dot-commands, ${_} last-result, signature hints, session export (75cbdfd)

    A grab-bag of power-user features for the interactive REPL:

    • Dot-commands intercepted before Robot's parser sees them — .help
      (with per-command drill-down via .help <cmd>), .imports, .vars,
      .kw <name>, .doc <library>, .history/.history clear/.history del <N>, .cwd, .clear, .save [-a] [-t name] <file>, .exit.
      Help text on each command documents its flags and shows examples.
    • ${_} mirrors the last keyword's non-None return value, like Python's
      interactive _. Works in the next argument with no ${tmp}= step.
    • F1 invokes .help from the prompt; Ctrl-R reverse-search now
      documented (was already wired via prompt_toolkit defaults).
    • Bottom row shows the active keyword's signature with the current
      argument highlighted when the cursor sits in an argument cell.
      Named-arg syntax (html=True) follows the spec position, not the
      positional cell index. Outside an argument cell the row is hidden so
      the prompt stays uncluttered.
    • Embedded-argument completion: Literal[...]-typed parameters on
      RF 7+ expose their allowed values as completion candidates after
      name=. Silently no-ops on RF 5/6.
    • .save writes the session to a runnable .robot file with imports
      hoisted into a *** Settings *** section.

    All features are prompt_toolkit-aware where it matters but degrade
    cleanly on the readline and plain backends.

  • repl: Session-context toolbar + completion/popup polish (7e0c881)

    Adds the bottom-toolbar feature from the original plan plus the
    last round of UX polish on the prompt_toolkit candidate popup
    that we shaved down through testing:

    • Bottom toolbar shows the running RF version and the cwd at
      all times — orientation info that's useful when juggling several
      venvs / project directories.
    • Esc closes the candidate popup and reverts any preview text
      that arrow-navigation had pinned, restoring the user's literal
      pre-popup buffer. Bound only when the popup is open
      (has_completions filter) so Esc keeps its Alt-chord prefix
      role everywhere else.
    • Enter on a highlighted candidate accepts it (popup closes,
      preview becomes permanent). For keyword completions Enter also
      cleans up the cell — deletes any leftover text after the cursor
      in the same cell (so a mid-cell completion replaces the whole
      keyword) and appends a cell separator if no argument follows.
    • Tab is smart: cycles through the popup when one is open,
      inserts a cell separator in argument context, opens the popup
      otherwise.
    • Multi-line history survives a session restart as one entry
      (newlines are escaped to \\n on disk so the file stays
      readline-compatible).
    • Mid-keyword preview renders cleanly — ${Lo} cursor before
      o, picking Log To Console now shows Log To Console, not
      Log To Consoleog (we trim the forward-cell tail from the
      state's original_document before prompt_toolkit applies the
      preview).
    • TEXT_FRAGMENT in compound variable lookups (${PREFIX_${X}})
      renders with the variable-name colour, not the argument colour,
      so the eye reads the whole concatenated name as one identifier.
    • EOL / EOS tokens from Robot's tokenizer are dropped from the
      rendered fragments — \\n inside a fragment would otherwise show
      as ^J at the end of every multi-line buffer line.
    • Trailing whitespace is padded out to the document's actual
      length so the cursor stays in sync with the buffer (was sticking
      at column 0 when the user kept typing spaces).
    • _completion.py is consolidated — candidates_for() is now
      a thin projection over candidates_for_rich(), ~200 lines of
      duplicated helpers removed (was an artefact of how the rich
      variant was added in the previous commit).
    • CELL_SEPARATOR + find_cell_end() added to
      _completion.py as named constants and a shared helper, used by
      the smart-Tab / Enter-cleanup paths.
  • repl: Doc hints next to completion popup candidates (c891215)

    The prompt-toolkit candidate popup now shows a short context line
    next to each candidate (prompt_toolkit's display_meta), so you
    know what a candidate is before picking it:

    • Keywords: first line of the keyword's docstring next to the
      name (Log a message with the given level next to Log, etc.)
    • Library / resource / variables imports: the discovery kind —
      MODULE_INTERNAL for built-ins, MODULE for third-party,
      RESOURCE for .resource files, FILE for filesystem hits
    • Variables (${…}/@{…}/&{…}): repr(value)[:40] of the
      current value in the live suite scope
    • Environment variables (%{…}): repr(os.environ[name])[:40]

    Backed by a new candidates_for_rich() API parallel to the
    existing candidates_for() — keeps the readline backend's path
    zero-cost (it can't render two-column popups anyway). Works on
    RF 5/6/7+ with the same attribute-name version-switch
    (short_docshortdoc) the rest of the package already uses.

  • repl: Robot-aware syntax highlighting at the prompt (a35b252)

    When the prompt-toolkit extra is installed, the prompt now paints
    Robot Framework syntax in colour. Keywords, arguments, assigns,
    comments, block constructs (FOR, IF, END, …), BDD prefixes
    (Given, When, Then, … plus localised variants from RF 6+) and
    variables all render distinctly.

    Variables decompose to the part level: sigil and braces, base name,
    type hints (${age: int}), default values (%{HOME=default}),
    subscripts (${dict}[key]), nested forms (${${inner}}), and
    inline-Python expressions (${{expr}}) all get their own colour.

    The highlighter uses Robot's own production tokenizer
    (robot.api.get_tokens) plus the robotcode semantic analyzer's
    variable decomposer — the same code path the RobotCode VS Code
    extension uses for semantic-token rendering. Colour assignments
    mirror the LSP semantic-token mapping, so the REPL prompt and the
    VS Code editor use a consistent palette.

    No additional dependency: Robot is already required by
    robotcode-repl, and the variable decomposer ships with
    robotcode-robot. Works on RF 5, 6, and 7+ (BDD-prefix
    localisation requires RF 6+; English defaults apply on RF 5).

  • repl: Multi-line buffer with block-aware auto-indent (8cfcc4c)

    When you open a Robot block (FOR, WHILE, IF, TRY, GROUP),
    the next line of input is auto-indented to the matching depth.
    END pops one level. Nested blocks stack — IF inside FOR lands
    at depth 2.

    Behaviour differs slightly between backends:

    • readline (always available): each continuation line is its own
      input() call. The ... prompt seeds the buffer with the right
      number of spaces via set_pre_input_hook. You type from there.
    • prompt-toolkit (with the optional extra): one multi-line
      buffer per top-level statement. Plain Enter is smart — submits
      when the block is balanced, otherwise inserts a newline +
      auto-indent so you keep typing inside. Alt-Enter (Esc
      Enter) and Ctrl-J always insert a newline + auto-indent,
      even when balanced. Cursor-Up/Down navigates inside the buffer.

    Shift-Enter is intentionally not bound — most terminals can't
    deliver it distinctly from plain Enter. Alt-Enter and Ctrl-J cover
    the use case portably.

  • repl: --plain flag to skip all prompt enhancements (bf9fc5b)

    robotcode repl --plain (or ROBOTCODE_REPL_PLAIN=1) bypasses the
    prompt-toolkit / readline cascade and falls back to a bare input()
    call — no history, no Tab-completion, no candidate popup, no
    auto-suggest, no syntax highlighting.

    Recommended for:

    • AI-agent invocations where ANSI escapes from the candidate popup
      would corrupt the stdout capture the agent reads back
    • Automation pipelines and CI that pipe input via heredoc
    • Wrapper scripts that need a deterministic, side-effect-free prompt

    --plain and --no-history are orthogonal — plain mode has no
    history file anyway, but combining the two is safe and does not
    raise.

  • repl: Live as-you-type completion with prompt_toolkit (b449e1b)

    pip install 'robotcode-repl[prompt-toolkit]' swaps the readline
    frontend for one driven by PromptSession:

    • Live candidate popup under the cursor as you type — no Tab
      needed (though Tab still works). Arrow-keys pick, Enter accepts.
      The verbose Import Library Foo / Import Library Bar row
      layout is replaced by just the labels.
    • Fish-style auto-suggest — last matching line you typed shows
      greyed-out behind the cursor, right-arrow accepts it.
    • Ctrl-R reverse search with a dedicated UI, bracket auto-match,
      multi-line cursor movement inside open blocks.

    The completer runs in a background thread (complete_in_thread=True)
    so the popup never blocks the UI, and Robot's expensive
    complete_*_import(None, …) filesystem / sys.path discovery is
    cached for the session — even with hundreds of importable modules,
    only the first keystroke pays the walk cost.

    Candidate sourcing reuses the same Robot-aware logic the readline
    backend uses, and the history file is shared on disk between the
    two backends — swapping extras (or having neither) doesn't lose
    arrow-up recall.

  • repl: Tab completion for keywords, variables, imports (72cdbc2)

    Tab now completes Robot-aware:

    • keyword names at the start of a cell, sourced from every loaded
      library and imported resource (case + whitespace + underscore
      insensitive — matches Robot's own resolution rules)
    • ${...} / @{...} / &{...} variables from the live suite scope,
      and %{...} environment variables from the process environment
    • Import Library arguments — plain identifiers (Coll<Tab>
      Collections), dotted module paths (robot.libraries.Coll<Tab>
      robot.libraries.Collections), and filesystem paths (./libs/My<Tab>
      ./libs/MyLib.py)
    • Import Resource arguments — .robot / .resource files only
    • Import Variables arguments — .py / .yaml / .yml / .json
      variable files plus discoverable variables modules

    Works across Robot Framework 5, 6, and 7+.

    On Pythons whose stdlib readline is backed by libedit (macOS system
    Python, some uv / python-build-standalone builds), install the
    gnureadline extra for full GNU readline behaviour:
    pip install robotcode-repl[gnureadline].

  • repl: --no-history flag for ephemeral sessions (1c28b57)

    robotcode repl --no-history skips loading and saving the persistent
    history file. Arrow-up still recalls lines from the current session;
    nothing leaks across sessions. Useful for AI-agent invocations, quick
    spike sessions, or working with secrets you don't want sitting in
    .robotcode_cache/repl_history. Also picked up from the
    ROBOTCODE_REPL_NO_HISTORY env var, which is handier when the REPL
    is launched by a wrapper script.

  • repl: Fish-style history dedup and configurable size (1c93402)

    Re-running a command that's already in the history no longer leaves
    the older copy behind — only the most recent invocation survives, so
    arrow-up cycles through unique commands (the same behaviour fish
    shell ships). Legacy history files with duplicates are cleaned up on
    first load.

    The buffer size is now configurable via the ROBOTCODE_REPL_HISTORY_SIZE
    environment variable; default stays at 1000 entries.

  • repl: Persistent line history across sessions (9c99f43)

    robotcode repl now remembers the lines you typed and recalls them
    with Pfeil-hoch / Ctrl-R, just like Python's own shell or bash. The
    history file lands in your project's .robotcode_cache/ when one is
    detected, otherwise in the per-user cache directory. ROBOTCODE_CACHE_DIR
    overrides both.

  • results: robotcode results TEXT output is now markdown (4b94d66)

    robotcode results subcommands now emit markdown for human
    output. On a coloured TTY rich renders it to themed ANSI and
    pages if needed; in a pipe (or with --no-color) the raw
    markdown is emitted verbatim — ideal for pasting into PRs,
    Slack, or feeding to an LLM. JSON output is unchanged.

    • summary, show, log, stats and diff open with a markdown
      heading and lay out content as tables, bullet lists,
      blockquotes for failure messages, and inline code for log
      output.
    • Each status shows both an icon and the bold word
      (✅ PASS, ❌ FAIL, ⏭ SKIP, ⏸ NOT RUN, ⚪ NOT SET). summary
      and stats tables are column-padded so the raw markdown stays
      aligned under cat/less.
    • log renders each test body as a nested list: keywords,
      control flow (FOR, WHILE, IF, TRY, VAR, GROUP), setups and
      teardowns. Argument values appear as inline-code spans;
      failure messages as blockquotes between a keyword and its
      children. Log output renders as - `[INFO] text`;
      multi-line messages get a fenced code block.
    • Artefacts attached to log messages render as markdown:
      images as ![name](url) so GitHub, Slack and VS Code show
      them inline; other files as [path](url) so OSC 8-aware
      terminals (rich, wezterm, iTerm2) make them clickable. Link
      targets prefer paths relative to cwd and fall back to
      file:// URLs when the artefact lives outside cwd;
      --full-paths forces file:// URLs everywhere.
  • results: Unify failure vocabulary across results subcommands (e3a0bd3)

    summary uses --failed/--no-failed for the failures listing (with
    failed as the matching JSON field), lining the wording up with
    --status fail everywhere else.

    show, log, and stats gain --failed, --passed, and
    --skipped as additive shortcuts for --status fail|pass|skip.
    They stack with each other and with --status, so
    results show --failed --skipped is --status fail --status skip
    without the typing.

  • robot: Enable bottom-up navigation in the SemanticModel (a87fc79)

    Every SemanticNode now carries a parent back-pointer, so LSP features
    that start from a token or statement can walk up to the enclosing block,
    section, or definition without re-querying the model by line. Comes with
    helpers enclosing_definition_block, enclosing_block_of_kind,
    enclosing_section, and path_from_root on SemanticModel.

    Parent is typed as SemanticNode (not SemanticBlock) so it can also
    point at a Statement — RunKeywordCallStatement.inner_calls are
    parented to their outer Run-Keyword statement.

  • robot: Pre-resolve more semantic data so LSP features can read it once (be36e61)

    Inlay Hints now read from the SemanticModel instead of doing a second
    analysis pass. The model gains a real block hierarchy (FOR/WHILE/IF/TRY/
    GROUP), pre-resolved init keyword docs on Library/Variables imports,
    and consolidated header-token construction so future LSP feature
    migrations stop re-walking the AST.

    The old AST-walk path stays as fallback while
    robotcode.experimental.semanticModel is off; both paths must produce
    identical hints (covered by parametrized equivalence tests across all
    supported RF versions).

  • robot: Implement cross-platform file locking for data cache (8ae4aec)

    Replace the Unix-only flock-based locking with a platform-dispatched
    implementation using LockFileEx/UnlockFileEx (Windows) and fcntl.flock
    (Unix/macOS). The Windows path previously had no-op early returns,
    meaning the exclusive lock used by cache prune provided no actual
    protection on Windows.

  • robot: Introduce SemanticModel for richer Robot Framework analysis (Part 1) (23a538c)

    Introduce the first part of an opt-in SemanticModel that provides deeper
    static analysis of Robot Framework files, available behind a feature flag.

    Enable via robot.toml:

    [tool.robotcode-analyze]
    semantic-model = true
    

    or in VS Code settings:

    "robotcode.experimental.semanticModel": true
    
    • Nested variable resolution: variables like ${DICT_${key}} are now
      statically resolved where possible, with diagnostics when resolution
      fails
    • Richer semantic highlighting: token classification driven by the
      semantic model instead of syntactic heuristics
    • RF 7.4 type hint support: KeywordName/KeywordArgument annotations are
      recognized for run-keyword detection
    • Log output to verify which analyzer is active
    • Robust deserialization of cached library data when new fields are added

    The SemanticModel is disabled by default. Further parts will add
    additional LSP feature migrations and extended analysis capabilities.

  • runner: Extend results & discover search and add log --keyword-info / --suite-info (9559316)

    --search / --search-regex reach more places now. On top of the
    existing test name / body / tags targets, they also hit:

    • every executed keyword's [Documentation], [Tags] and [Timeout]
      — answer questions like "which tests touched a keyword tagged slow"
      or "which tests called a keyword whose docstring mentions
      rate-limiting".
    • any ancestor suite's Documentation or Metadata — a hit on a
      suite-level field keeps every test underneath, so
      --search "API tests" picks up every test in a suite whose
      docstring reads "API tests" without enumerating names.

    Both rules apply to results and discover alike.

    results log gains two opt-in views (default off — the plain log
    stays compact):

    • --keyword-info prints each executed keyword's [Documentation],
      [Tags] and [Timeout] under its header, so the log shows what
      each keyword is, not just what it was called with.
    • --suite-info groups tests under a Suite: header that carries
      the suite's name, source, status, Documentation and Metadata
      — useful when scanning a multi-suite run to see at a glance which
      suite a test belongs to and what it's about.

    They compose: log --suite-info --keyword-info gives a structured
    view that mirrors the layout of the original .robot files.

  • runner: Add --search / --search-regex to discover commands (ce8c783)

    discover all / tests / tasks / suites / tags / files now
    support the same search flags as the results family. Filtering runs
    through a SearchModifier SuiteVisitor wired into the existing
    prerunmodifier slot, so the Collector only ever sees the surviving
    tree — statistics, ancestor suites and the tag aggregate fall out of
    that automatically.

    Search targets per test: name, full name, source path, documentation,
    template, timeout, tags (normalisation-aware), and the full body —
    keyword names, arguments, assigned variables, FOR/WHILE/IF conditions,
    VAR/RETURN values, EXCEPT patterns, GROUP names, plus setup/teardown.
    TEXT output highlights matches in name/source/tags/path; JSON output
    echoes the pattern in filtersApplied.

    To avoid duplication, the search infrastructure moves into a shared
    runner/cli/_search.py module: SearchMatcher (with a model-agnostic
    matches_body method that walks both robot.running and robot.result
    body trees via duck-typing), make_search_matcher, make_highlighter
    and the new SearchModifier. results.py and _render.py switch to
    the shared module and drop their local copies.

  • runner: Add the standard result filters to results diff (a10f6ce)

    results diff now accepts the same filter options as the other results
    subcommands: --status, -i/--include, -e/--exclude, -s/--suite,
    -t/--test, -bl/--by-longname, -ebl/--exclude-by-longname, plus
    --search / --search-regex. Each filter is applied identically to
    both baseline and current before the diff, so the comparison can be
    scoped to a single suite, tag pattern, name, or message snippet:

    robotcode results diff baseline.xml -s "MyProject.Login"
    robotcode results diff baseline.xml -i smoke
    robotcode results diff baseline.xml --search TimeoutError
    robotcode results diff baseline.xml -bl "MyProject.Login.Bad Password"
    

    DiffResult gains a filtersApplied field (additive, optional) so the
    JSON output records the filters in the same shape used by the other
    result models.

  • runner: Add -bl/--by-longname and -ebl/--exclude-by-longname to results filters (33697cb)

    robotcode results now accepts long-name filters alongside the existing
    --status, -i/--include, -e/--exclude, -s/--suite, -t/--test
    options. Pick a specific test or suite by its full long name (exact
    match), or exclude a branch of the tree the same way:

    robotcode results show -bl "MyProject.Login.Bad Password"
    robotcode results summary -ebl "MyProject.Slow Suite"
    robotcode results stats --by tag -bl "MyProject.Smoke"
    robotcode results log -bl "MyProject.Login" -bl "MyProject.Logout"
    

    Both options compose with the existing filters and --search /
    --search-regex, and surface in JSON filtersApplied as by-longname
    / exclude-by-longname for CI consumption. Available on summary,
    show, log, and stats.

    Along the way, trimmed redundant "repeat for OR" notes from option help
    texts — Click already shows the * marker on repeatable options.

  • runner: Split --search and --search-regex, add to summary and stats (89c2952)

    --search-regex is now a standalone option that accepts a pattern instead
    of a boolean flag — you no longer have to spell out both --search TEXT --search-regex. The two options are mutually exclusive:

    • --search TEXT — case-insensitive substring match (open browser,
      TimeoutError, …)
    • --search-regex PATTERN — Python regex, honoured as written. Use
      (?i)pattern for case-insensitive matching, just like in any regex
      engine.

    Both options now work on summary, show, log, and stats, so the
    same search vocabulary applies across the whole results family:

    robotcode results summary --search TimeoutError
    robotcode results stats --by tag --search Browser
    robotcode results show --search-regex "AssertionError.*"
    robotcode results log --search "Open Browser"
    

    Search traverses the raw output tree (test attributes plus keyword names,
    arguments, and log messages), so a single search reaches into the
    execution body without first materialising the full per-test log.

  • runner: Add results diff for comparing two runs (bbdcf33)

    results diff BASELINE [CURRENT] compares two output.xml / output.json
    files and reports tests whose status changed, plus tests that appeared or
    disappeared between the runs. Categories:

    • New failures — passed in baseline, fails in current
    • New passes — failed or skipped in baseline, passes in current
    • Status changes — any other status flip
    • Added — only in current
    • Removed — only in baseline

    If CURRENT is omitted, it falls back to the active profile's auto-
    discovered output file, so the common pattern is robotcode results diff /path/to/baseline.xml after the latest run. Use --only new-failures
    (repeatable) to narrow the view; combined with --format json this gives
    CI pipelines a clean signal for PR feedback without parsing output.xml
    themselves.

    Exit code is always 0 — the command is for inspection, not gating.

  • runner: Add --search / --search-regex to results show and log (1e90209)

    results log and results show now accept --search TEXT to narrow the
    output to tests that mention the given text — searched across test name,
    failure message, keyword names, keyword arguments, and log messages.
    Case-insensitive substring match by default; pass --search-regex to use
    a Python regular expression instead.

    In log's TEXT output, every match is highlighted with a yellow background
    so the relevant lines stand out at a glance — handy for tracking down
    which test triggered a particular TimeoutError without reopening
    log.html. JSON output records the search pattern in filtersApplied for
    auditability but stays free of markup.

    --search composes with the existing filters (--status, -i/-e/-s/-t),
    so combinations like "failures that mention AssertionError" or "smoke
    tests where Open Browser was called" come naturally.

  • runner: Add results stats subcommand (d1fa30f)

    results stats aggregates a finished run by --by tag, --by suite, or
    --by status — the same dimensions report.html exposes in its
    "Statistics by Tag" / "Statistics by Suite" panels. Repeat --by to render
    multiple sections in one call.

    Each section is a table with pass/fail/skip counts and total elapsed time
    per group, sorted by failures (descending) by default. Switch to --sort elapsed, --sort total, or --sort name, and limit rows with --top N.
    The standard --status, -i/--include, -e/--exclude, -s/--suite,
    -t/--test filters narrow the test set before aggregation, so combinations
    like "tag stats for the smoke suite, top 10 by failures" come out of the
    box.

    JSON output exposes a stable StatsResult / StatsSection / StatsGroup
    schema for CI consumption.

  • runner: Add --sort and --reverse to results show (e4d693c)

    results show can now sort tests by name, suite, status, elapsed,
    or start before display. Pair with --top N for triage views like "the
    slowest 10 tests" or "failures first". --reverse flips the order; without
    --sort, execution order is preserved (the existing behaviour).

    status orders FAIL → SKIP → PASS → NOT RUN (matching report.html's
    custom sort), and elapsed defaults to longest-first so the most expensive
    tests bubble to the top. A dim footer line Sorted by FIELD (desc) is
    emitted in TEXT mode when a sort is active; JSON output reflects the order
    in the tests array.

  • runner: Unify results log filters and add --max-depth (02bb47b)

    results log now uses the same filter options as results show and results summary — pick tests by --status, -i/--include, -e/--exclude,
    -s/--suite, or -t/--test. The old TEST_PATTERN positional and
    --failures-only flag are gone; use -t "*Login*" and --status fail
    instead.

    A new --max-depth N collapses deeply nested keyword calls. The keyword
    header still prints, and a dim … N hidden (--max-depth N) marker takes the
    place of the body so nothing disappears silently. Default 0 keeps the
    behaviour unchanged (full tree). The previous --keywords/--no-keywords
    toggle is dropped — without keyword grouping the output overlapped with
    results show.

    See docs/03_reference/cli.md for the refreshed
    option reference.

  • runner: Add results command to inspect Robot Framework runs (cf67abf)

    After running tests, robotcode results lets you explore the output
    without opening report.html or re-running the suite — handy for CI
    logs, agent-driven workflows, and quick failure inspection from the
    terminal.

    Three subcommands:

    • robotcode results summary — overall status, pass/fail/skip counts,
      run duration, and the total number of error and warning messages.
      --failures adds the list of failed tests above the counts.
    • robotcode results show — one line per test with its status, source
      link, and failure message. Filter by status, tags, suite or test
      name pattern.
    • robotcode results log — the full execution tree: every keyword
      call, control structure, and log message, just like Robot's
      report.html but in your terminal. --failures-only, --level WARN, --extract DIR (pulls out screenshots and embedded
      artefacts), and more.

    The output file is auto-discovered from your active robot.toml
    profile; override with -o/--output PATH. Test names carry a
    (path:line) suffix that VS Code's integrated terminal turns into a
    clickable goto-source link. Pass --format json (or toml) for a
    structured, agent-friendly payload.

    See docs/03_reference/cli.md and robotcode results --help for the
    full reference.

  • vscode: Add commands to register the chat plugins marketplace (0e2b02f)

    Two new Command Palette commands let you pull the RobotCode chat
    plugins marketplace into VS Code without editing settings by hand:

    • "RobotCode: Add Chat Plugins Marketplace"
    • "RobotCode: Remove Chat Plugins Marketplace"

    They add or remove robotcodedev/robotframework-agent-plugins in the
    user-level chat.plugins.marketplaces setting, so GitHub Copilot Chat
    can discover and install RobotCode plugins (and future ones from that
    marketplace). This is the marketplace route — separate from the plugin
    the extension already bundles via contributes.chatPlugins.

    If you install the robotcode plugin from the marketplace, disable the
    bundled one by turning off robotcode.ai.enableChatPlugins to avoid
    running two copies of the same plugin.

    Only the applicable command is shown: Add when the marketplace is not
    registered, Remove when it is. Removing also clears the entry when it
    was added as a GitHub URL rather than the owner/repo shorthand.

  • vscode: Bundle AI chat plugins (4018a4f)

    The extension now bundles chat plugins for GitHub Copilot Chat in VS Code.
    When enabled, the agent uses the project's robotcode CLI to answer Robot
    Framework questions and run tasks, honoring the project's robot.toml,
    profiles, and Python environment.

    For example:

    • "run the smoke tests with the ci profile" runs them through the
      selected profile and reports pass/fail counts
    • "why did Login Works fail?" inspects the existing run results instead
      of loading output.xml into the chat
    • "what tests and tags exist?" uses robotcode discover, which resolves
      the real set (paths, profiles, variables, pre-run modifiers) rather than
      grepping .robot files
    • "what arguments does this keyword take?" looks it up with libdoc
      against the installed libraries and local resources
    • "try this keyword" runs it in the REPL

    Enabled by default; toggle with robotcode.ai.enableChatPlugins.

    For other AI agents (Claude Code, Codex, and other Open-Plugin-compatible
    tools), the same plugins are published as a marketplace:
    https://github.com/robotcodedev/robotframework-agent-plugins

  • vscode: Disable chat language model tools by default (3c47b48)

    RobotCode now includes a robotcode CLI-based chat plugin for chat assistance. They use the same CLI workflows RobotCode users already know for setup, configuration, profiles, discovery, analysis, test runs, result inspection, and REPL usage.

    The existing VS Code Chat language model tools are now turned off by default. They provide chat access to RobotCode information such as keyword documentation, library documentation, document imports, and environment details.

    Users who still need these tools can enable robotcode.ai.enableLanguageModelTools.

    The setting remains visible in VS Code for now and notes that the language model tools are planned for deprecation and later removal.

  • vscode: Add global setting to disable What's New notification (fa03c08)

    Allow users to turn off the update notification that appears after
    RobotCode has been updated. The new robotcode.showWhatsNewOnUpdate
    setting can be found in the global VS Code settings under
    RobotCode > General.

  • Add inline documentation and examples to robot.toml.json (825f891)

    Users now get rich documentation directly in their editor when editing
    robot.toml configurations through JSON schema integration. The schema works
    seamlessly with the Even Better TOML VS Code extension
    (https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml),
    which is powered by Taplo.

    Enhancements to robot.toml.json:

    • Comprehensive inline documentation (markdownDescription) for every field
    • TOML code examples extracted from configuration docs
    • Direct links to RobotCode documentation via Taplo integration
    • Full compatibility with Even Better TOML and other Taplo-based editors

    Improvements to schema generator:

    • Complete type hints for all inner functions
    • Detailed docstrings for better code clarity
    • More robust TOML code block extraction regex

Bug Fixes

  • analysis: Respect diagnosticMode for workspace files (d2f8114)

    Diagnostics for closed workspace files could appear (or be suppressed)
    inconsistently in openFilesOnly mode, depending on which file the
    workspace diagnostics loop happened to iterate last.

    Also clarify the diagnosticMode description so it no longer implies
    that background analysis is restricted -- the setting only controls
    which files diagnostics are reported for.

  • analyze: Run unused diagnostics through the diagnostic modifier (f13b678)

    collect_unused_keywords / collect_unused_variables returned their
    diagnostics directly, bypassing the diagnostic modifier. As a result the
    KeywordNotUsed / VariableNotUsed diagnostics could not be ignored or
    restyled via -mi/-mX or # robotcode: comments, unlike every other
    diagnostic. Pass their result through modify_diagnostics, the same way
    collect_diagnostics does.

  • analyze: Tighten path filtering and refine code output (b0b8820)

    • Path arguments to robotcode analyze code are now matched segment-wise:
      passing tests/api no longer pulls in siblings like tests/api_v2.
    • Workspace-level diagnostics (e.g. variable/library import errors) are
      now prefixed with .: instead of being printed without any source
      marker; their related-information lines keep their line/column.
    • The summary line picks its color from the highest severity present
      (red/yellow/blue/cyan) instead of only highlighting errors.
    • Unused-keyword/variable collectors are only registered when
      --collect-unused is set, and empty document reports from the
      analysis pass are no longer emitted.
  • analyze: Ignore intentionally unused variables in CLI diagnostics (9cba5b0)

    Treat variable names starting with "_" as intentionally unused in CLI unused-variable diagnostics, matching the language server behavior.

  • bundled: Quote arguments in robotcode script to handle spaces correctly (4311747)

  • config: Allow "NONE" string for max-error-lines in robot.toml (6f2e5e1)

    You can now set max-error-lines = "NONE" to show the full error
    message, matching Robot Framework's --maxerrorlines NONE. Integers
    keep working as before; any other string is rejected with a clear
    error.

  • discover: Render arbitrarily deep suite trees in markdown output (3825939)

    robotcode discover all (and any other markdown output) now renders
    the full workspace tree no matter how deeply suites are nested.
    Previously, projects nested past ~8 levels had everything below the
    limit silently dropped — including the trailing ## Statistics
    block — because rich's markdown parser caps nesting at 20 and every
    list level consumes two of that budget.

    Markdown is now rendered through a parser configured for deep
    nesting, so the whole tree and its statistics always appear. Headings
    are left-aligned and tables render without the extra blank lines rich
    inserts by default.

    rich is now a direct dependency of the plugin package (the lowest
    package that renders markdown); the repl package picks it up
    transitively instead of declaring it separately.

  • docs: Fix sidebar links treated as external URLs (9b393ec)

    vitepress-sidebar generates base:"/" with links starting with "/",
    causing VitePress to produce protocol-relative URLs ("//path") that
    browsers interpret as external links. Remove the redundant base:"/"
    from the root sidebar group so absolute links resolve correctly.

  • language-server: Honor semantic model setting changes (782d83d)

  • packages: Pin internal robotcode dependencies to exact version (7e4e9f2)

    Updating a single robotcode package now pulls the matching versions of
    its robotcode dependencies along with it. Previously the inter-package
    dependencies were unconstrained, so upgrading one package could leave
    its siblings on an older release and break the installation.

    The pins are kept in sync automatically on each release.

  • plugin: Suppress mypy type-arg warning on click.ParamType subclass (ecf88e7)

    A recent mypy / click version bump started flagging
    AddressPortParamType(click.ParamType) as 'Missing type arguments
    for generic type ParamType'. Suppress with # type: ignore[type-arg]
    inline — the class deliberately omits the type parameter because the
    value shape is heterogenous (parsed by convert).

  • plugin: Make click help-text generation deterministic (b45757c)

    The MutuallyExclusiveOption help text joined a Set[str] and
    EnumChoice returned set(choices).difference(excluded) — both
    produced non-deterministic ordering, so regenerating cli.md always
    re-shuffled the option lists. Sort the mutex set; preserve enum
    definition order via a list comprehension. Regenerate cli.md so the
    typo fix from eeb8979 actually lands in the docs and the option
    ordering is stable across future runs.

  • repl: Handle all return-value assignment forms in completion and argument hints (bad6e14)

    Keyword completion and the status-bar argument hint now work after any return-value assignment Robot Framework allows — including item assignment (${x}[0], ${d}[key]) and type hints (${x: int}), not just a plain ${x} =. The keyword is resolved with Robot Framework's own variable parser, so the REPL matches real assignment syntax exactly. The status-bar hint, which previously showed nothing once an assignment was present, is fixed too.

  • repl: Keep keyword completion working after a return-value assignment (dc5d0be)

    Starting a line with a return-value assignment — ${result}= Some Keyword, or several targets like ${a} ${b}= … — no longer turns off keyword completion in the keyword cell. Completion and Tab now skip the assignment targets and complete the keyword (and then its arguments) just as they would without an assignment.

  • repl: Prevent multiple concurrent REPL executions (1edfe13)

  • repl: ${_} now mirrors every keyword result, including None (45ec8fa)

    In the REPL, ${_} holds the result of the last keyword. Previously
    keywords that return None (such as Log) left ${_} untouched, so the
    most natural first step — Log hello then Log ${_} — failed with
    "Variable '${_}' not found".

    ${_} now always reflects the most recent keyword and is set to None
    when a keyword returns nothing. It's also seeded to None at startup, so
    ${_} resolves even before the first keyword runs.

  • repl: Resolve relative import paths against the working directory (8a34747)

    Imports such as Import Resource foo/my.resource typed at the REPL
    prompt now resolve relative to the REPL's working directory, just like
    the same import does in a .robot file. Previously such imports only
    worked when the path was absolute, prefixed with ${CURDIR}/, or
    reachable via the module search path.

    This matches Robot Framework's own resolution since RF 7.4. The REPL
    docs also note that on RF < 7.4 bare relative paths still fail (RF
    itself doesn't resolve them that way on those versions); use
    ${CURDIR}/... or put the directory on the module search path
    instead.

  • repl: Keep REPL startup working on pyreadline3 (Windows) (7ba1a01)

    robotcode repl crashed on startup on Windows when launched against
    pyreadline3 and a history file that still had legacy duplicates —
    pyreadline3 doesn't expose remove_history_item, so the dedup pass
    tripped on AttributeError before the prompt ever appeared.

    History dedup and .history del <N> now feature-detect
    remove_history_item and fall back to a clear_history + re-add_history
    rebuild when the running readline shim doesn't expose it. CPython's
    GNU readline path is unchanged.

  • repl: Preserve backslashes in dot-command argument paths (ed2b91a)

    .save C:\Users\…\scratch.robot mangled the path on Windows because
    shlex.split consumes backslashes as escape characters. Switch to a
    custom shlex with escape="" so paths arrive intact while shell-style
    quoting (for spaces in paths) still works.

  • repl: -d/--outputdir was silently ignored (06bc85d)

    robotcode repl -d ./results -o output.xml used to write the file
    into the working directory instead of ./results. The directory now
    honours -d, the output-dir setting in robot.toml, and robot's
    default precedence in that order. Both repl and repl-server are
    fixed.

  • repl, repl-server: -v / --variable and -V / --variablefile now work (7ecdd82)

    Both flags on robotcode repl and robotcode repl-server were broken
    -v BASE_URL:staging did not register the variable, and -V vars.yaml
    did not load the file. They now behave the same as the matching robot
    flags: -v sets inline name/value pairs, -V reads variables from
    .yaml / .py / .json files. Repeat either flag to set multiple at
    once.

  • robot: Fix Run Keyword If nested run keyword handling in all branches (1d283aa)

    All three Run Keyword If branches (IF, ELSE, ELSE IF) now use
    skip_args() + _analyze_keyword_call(analyze_run_keywords=True)
    so nested run keywords receive only branch-local tokens instead
    of consuming ELSE/ELSE IF markers.

  • robot: Correct type-hint handling in semantic analyzer (ecfde21)

    strip variable type hints only in declaration contexts
    preserve typed names in usage lookups
    resolve nested typed variable declarations correctly
    stop splitting type hints on additional colons in tokenizer
    add regression tests for variables, VAR, assignments and arguments

  • runner: Keep relSource in results JSON regardless of --full-paths (e4ff456)

    Aligns results with discover, which always carries both source
    (absolute) and relSource in its JSON output. Two changes:

    • _make_test_item / _make_diff_change / LogTest stop conditionally
      setting rel_source=None when --full-paths is on. The flag is now
      purely a TEXT-rendering hint.
    • _rel_to_cwd falls back to the original path (instead of None)
      when the source isn't anchored under cwd, mirroring
      discover.get_rel_source. Consumers like the VS Code extension can
      now rely on relSource being present whenever source is.

    _make_diff_change and _make_test_item no longer take full_paths,
    since the parameter became unused after the change. Call sites updated.

    Plus acceptance tests covering the harmonised behaviour on summary,
    show, log and diff, and a TEXT-only --full-paths test on
    discover tags (the last subcommand without coverage).

  • runner: Chdir into root_folder in discover files (b92acdc)

    The other discover subcommands wrap their work in
    with app.chdir(root_folder) via handle_options, so get_rel_source
    produces paths relative to the project root. discover files skipped
    that step, which made its default (non---full-paths) output depend on
    the caller's process cwd: if cwd was outside the project, paths fell
    back to absolute, breaking the documented semantics.

    Wrap the body in the same chdir context so the output is consistent
    with the rest of discover. Also flips a fragile test_files_default_ relative from ordering-dependent (it would fail in the full suite
    when an earlier test left cwd in a tmp dir) to robust.

  • runner: Honour subcommand semantics in discover JSON output (0f26a3f)

    Two discover subcommands silently differed between their TEXT and
    JSON renderers because the type filter only ran in the TEXT path:

    • discover tests -f json returned every item in
      collector.test_and_tasks — i.e. tasks slipped in too, even though
      the TEXT render correctly filtered to type == "test". Same shape
      bug on discover tasks. Filter the list before handing it to
      ResultItem so JSON and TEXT agree.
    • discover tags --not-normalized -f json ignored the flag and always
      returned the normalised tag dict; only the TEXT path consulted
      --normalized. Pick the right collector dict (tags vs
      normalized_tags) for both renderers.

    Both surfaced while wiring up the new discover acceptance tests.

  • runner: Page discover output through a single pager invocation (069c34e)

    Each discover subcommand opened the system pager multiple times in
    sequence — once for the main listing, then once for the statistics block
    (and once per key for discover info). The fix routes everything through
    a single echo_via_pager call so the user only sees one pager instance.

  • runner: Normalise tags in results commands (8f93500)

    Robot Framework treats tags as equal when they differ only in case,
    whitespace, or underscores (Bug 1bug_1Bug1). The new
    results commands didn't honour that:

    • stats --by tag showed each spelling variant as its own bucket,
      even though they referred to the same semantic tag.
    • tests[].tags in show / summary --failures echoed the raw form
      from the result file, so the same tag could appear with different
      spellings on different tests.
    • --search / --search-regex against tags compared the raw strings,
      so --search "bug 1" missed tests literally tagged bug_1.
    • filtersApplied.include/exclude echoed the user input verbatim, which
      didn't reflect how Robot actually interpreted the pattern.

    Every tag-handling spot now uses Robot's normalize(..., ignore="_"):

    • Stats buckets group by the normalised form and display the normalised
      label too.
    • _make_test_item emits already-normalised tags in JSON / TEXT.
    • _SearchMatcher carries a separate tag-aware predicate that
      normalises both haystack and needle (substring) or just the haystack
      (regex).
    • _canonical_tag_pattern normalises plain single-tag patterns in
      filtersApplied; patterns containing an uppercase AND / OR /
      NOT operator are echoed verbatim because each operand would need
      individual normalisation.

    Tests added: stats merges equivalent tags into one bucket; show
    emits normalised tags; --search is normalisation-aware against
    tags; filtersApplied echoes the canonical pattern form. The
    tagged.robot fixture grew three new tests (norm tag, norm_tag,
    NormTag) so the regression case is exercised end-to-end.

  • runner: Make results commands work against older Robot Framework outputs (20bb597)

    robotcode results failed on output.xml files generated by Robot
    Framework 5 and 6 — the older models use different attribute names than
    RF 7 (elapsedtime vs elapsed_time, starttime/endtime as the
    YYYYMMDD HH:MM:SS.fff string, no variables on WhileIteration, …).

    Each accessor now falls back to the legacy spelling and the iteration
    helper limits loop-variable lookups to ForIteration via isinstance,
    so summary, show, log, stats, and diff produce identical
    output regardless of which Robot version wrote the file. Timestamps in
    the legacy string format are normalised to ISO 8601 in the JSON
    contract.

    Smoke-tested across RF 5.0, 6.0, 7.0, 7.3, and 7.4.

  • runner: Escape <errors> in results log help to prevent VitePress build error (7dc79ef)

    The --execution-messages help text contained a raw <errors> tag that
    VitePress' Vue parser interpreted as an unclosed HTML element, breaking
    the generated cli.md docs page. Wrap it in backticks so it renders as
    inline code and is ignored by the template compiler.

  • schema: Revert robot.toml json-schema, because older versions of robotcode need this (fafb168)

  • vscode: Let Tab accept Copilot inline edit suggestions (f724aa2)

    When "4 Spaces Tab" was enabled, pressing Tab inserted four spaces
    instead of accepting or jumping to Copilot's inline edit (Next Edit)
    suggestions. Tab now yields to those suggestions and only inserts
    spaces when none is pending.

  • vscode: Update stale RF version UI strings from 4.1 to 5.0 (#605) (f846483)

    The quick-pick dialog shown when environment validation fails still
    referenced 'robotframework version 4.1 or higher' in both the
    'Select Python Interpreter' detail and the 'Retry' detail text.
    The actual enforced minimum has been 5.0 since commit 02cf495
    (refactor: introduce RF_VERSION constant and remove RF < 5.0 dead code).

Documentation

  • analyze: Document the -mi "*" allow-list pattern (b5b2444)

    Show that * matches every code, so -mi "*" plus re-introducing
    specific codes builds an allow-list — and point out it remaps severity,
    unlike the --code filter.

  • analyze: Document the --code filter (e72ac55)

    Fold --code into the "reporting only some diagnostics" section next to
    --severity, noting that both filter (rather than remap) and combine with
    AND.

  • analyze: Document --severity filter and unused diagnostic control (2da8116)

    • describe the new --severity output filter alongside the modifiers
    • note that KeywordNotUsed/VariableNotUsed can now be remapped or
      ignored like any other diagnostic
  • analyze: Streamline and correct analyze-code guide (0272ab9)

    • drop the flag table; defer to the CLI reference
    • move the JSON reference to the end
    • trim implementation internals from the SARIF section and give every
      output format a consistent invocation + example
    • note that the CI examples assume project setup via its package manager
    • fix a dead anchor link

    Corrections after review:

    • no paths analyzes/reports the whole project, not the profile's default
      paths; paths/--filter narrow the reported per-file diagnostics while
      import/library resolution always covers the whole project
    • explain why --collect-unused is opt-in (needs whole-project reference
      data, extra pass, slower with many keywords/variables)
    • note that a real run or discovery can surface errors static analysis
      misses (suite-wide checks like duplicate test names, PreRunModifier)
    • show how to read the bitwise exit code from the shell
  • analyze: Document CI workflows, drop scratch workflow (fcb0caa)

    Replace the fragmentary CI snippets in the analyze-code guide with full,
    copy-pasteable workflows for GitHub code scanning (SARIF upload), GitHub
    inline annotations, and GitLab Code Quality — including the
    security-events: write permission and current action versions
    (checkout@v5, upload-sarif@v4) that the manual trial surfaced.

    Remove the throwaway .github/workflows/analyze-code.yml now that the
    examples live in the documentation.

  • analyze: Add reference guide for analyze code (095b52a)

    Add a task-oriented guide for robotcode analyze code under
    docs/03_reference, in the style of the discover/results guides: what it
    checks, severities and diagnostic modifiers, exit codes and masks, all
    output formats (concise, json, sarif, github, gitlab), a flag reference,
    JSON/SARIF/GitHub/GitLab output references, CI recipes, and the cache
    subcommands. Link it from the reference index.

  • cli: Enhance command options documentation for severity and code filters (422d466)

  • cli: Document core, results and testdoc commands (b48a58d)

    Add the missing commands to the package overview: the core
    robotcode package now lists config and profiles, and the runner
    package lists results and testdoc. Close the reference with help
    hints and links to the related configuration, discovery, results and
    REPL pages.

  • config: Show TOML examples in robot.toml reference (c53da77)

    The "Examples:" blocks were carried over verbatim from Robot
    Framework's CLI help, so they showed --flags instead of TOML.
    Add a curated TOML_EXAMPLES map in generate_rf_options.py (with
    extend- prefix rewriting for extend-* variants) and regenerate
    model.py and config.md.

  • contributing: Add agent workflow guide and update contribution guide (9a024a2)

  • contributing: Clarify DCO vs GPG signing and unify branding (a1f116d)

    Reference the DCO in the CONTRIBUTING Legal Notice and recommend
    git commit -s. Clarify in both places that the DCO sign-off is
    separate from the GPG/SSH commit signature (git commit -S) required
    for merges.

    In AI_POLICY.md, drop the duplicated DCO statement and link to the
    Legal Notice instead. Align the PR template's tests/lint/signed-commits
    bullets with the CONTRIBUTING checklist, and unify the project name
    spelling to "RobotCode" across the three files (URLs and CLI names
    unchanged).

  • contributing: Add AI policy and refine contribution guide (688e6ae)

    Add a project-wide AI and Automated Contribution Policy (AI_POLICY.md)
    covering human responsibility, disclosure, low-context submissions, and
    enforcement.

    Restructure CONTRIBUTING.md:

    • new "Project-Wide Rules" section linking the AI policy and the
      payment/bounty rule
    • add a pre-commit hooks section (uvx / uv tool install / pipx)
    • mention uv / uvx alongside pipx for installing hatch
    • rewrite the Running Tests section: `hatch run test:test` as the
      recommended default, correct RF versions (5.0–7.4) and matrix size
      (5×8=40), clarify that bare `hatch run test` only runs in the
      default env
    • consolidate PR guidelines into a PR checklist + description +
      review process, referencing the new PR template
    • fix TOC duplicates and the empty Payment link

    Add a GitHub pull request template with checklist and AI/tooling
    disclosure block, and add the same disclosure block to the bug,
    enhancement, and question issue templates.

  • home: Refresh landing page (8dc70e0)

    Mirrors the reworked README pitch: tightened hero, consolidated feature
    cards, and a "Sponsor RobotCode" section with Individual/Corporate split.

    Adds a RandomTagline component, analogous to RandomHeroImage, so the
    hero tagline rotates randomly on each page load.

  • news: Add v2.6.0 release notes (7d6a701)

  • readme: Restructure and refresh (69c881b)

    • Sharpen What-is intro with Foundation / Core-Team partnership note
    • Restructure Key Features into Editor/IDE, Configuration, Command line
      groups with marketing-oriented bullets
    • Tighten Requirements (correct VS Code 1.108 / IntelliJ 2025.3,
      prominent LSP-editor note)
    • Add Command Line installation block for CI / non-IDE users
    • Fix broken doc links (troubleshooting page never existed,
      /support/ → /05_contributing/)
    • Split Sponsor RobotCode and Get Involved into separate sections
  • repl: Clarify REPL scripts are body-only .robotrepl files, not full suites (a169d86)

    Reword the file-execution section so it no longer implies a full .robot
    suite can be run. These inputs are REPL scripts — a test-case body only,
    with no *** Settings *** / *** Test Cases *** sections — and use the
    .robotrepl / .robotscript extension instead of .robot. A file with
    section headers fails with "No keyword with name '*** Settings ***'
    found"; import libraries and resources from inside the body with
    Import Library / Import Resource.

    Also note that .save only guarantees the export parses cleanly —
    REPL-only variables like ${_} still need an edit to run standalone — and
    tidy up wording, hyphenation, and a duplicated shortcuts list.

  • repl: Document the prompt-toolkit extra (2660b7b)

    New section in the REPL reference covers:

    • the prompt-toolkit extra (pip install 'robotcode-repl[prompt-toolkit]')
      and the upgrades it lights up — live as-you-type candidate popup,
      fish-style auto-suggest, Ctrl-R reverse search UI, bracket
      auto-match, multi-line cursor movement
    • the threaded completer + session cache that keep the popup
      responsive even with hundreds of importable modules on sys.path
    • the fact that history is shared on disk between backends, so
      swapping extras doesn't lose arrow-up recall
    • the [prompt-toolkit,gnureadline] combined extras as a sensible
      setup for libedit-backed Pythons
  • repl: Document history persistence and tab completion (1374da7)

    New "Prompt features" section in the REPL reference covers:

    • where the history file lives (project .robotcode_cache/ vs. the
      per-user cache directory, ROBOTCODE_CACHE_DIR override), how the
      fish-style dedup behaves, and the --no-history /
      ROBOTCODE_REPL_NO_HISTORY / ROBOTCODE_REPL_HISTORY_SIZE
      escape hatches
    • what Tab completes — keywords from loaded libraries and imported
      resources, suite-scope variables (${...} / @{...} / &{...}),
      %{...} environment variables, and the three Import arguments
      with plain / dotted / filesystem prefixes
    • the gnureadline extra for macOS' system Python and
      python-build-standalone interpreters (uv / rye / …) where the
      stdlib readline is libedit-backed and Tab would otherwise be
      degraded

    Also adds the REPL reference to the index page.

  • repl, repl-server: Correct misleading --source help text (87402e3)

    The flag only uses the parent directory of FILE as the REPL's
    working directory — it does not read or write the file, and does not
    set a suite name.

  • runner: Task-oriented guide for robotcode discover (f69fd9a)

    A new discovering-tests.md page covers the discover family the same
    way analyzing-results.md covers results. It walks every subcommand
    from the terminal perspective — all, tests, tasks, suites,
    tags, files, info — with examples and flag tables, then the
    shared Robot-native filters and --search / --search-regex
    behaviour, and finally the full JSON schema reference for scripts,
    CI pipelines, and editor integrations (TestItem, ResultItem,
    TagsResult, Info, diagnostics, filtersApplied) plus a set of
    jq-based CI recipes for sharding, tag reports, and parse-error gates.

  • runner: Cover extended search and results log --keyword-info / --suite-info (a7395f6)

    • analyzing-results.md: search-target list mentions test
      doc/template/timeout, keyword [Documentation]/[Tags]/[Timeout]
      (result-tree-only), and ancestor-suite Documentation/Metadata. New
      --keyword-info and --suite-info subsection explains what each flag
      adds to TEXT and JSON, with a combined example. log JSON schema
      shows the new suites array and per-test suite cross-reference plus
      a table for the keyword doc/tags/timeout fields.

    • cli.md: regenerated from hatch run create-cmd-line-docs to pick
      up the new --keyword-info / --suite-info flags on results log
      and the updated --search help text across results and discover.

  • runner: Fix "releative" typo in discover --help (eeb8979)

  • website: Use bundler module resolution (dd6f358)

  • website: Improve hero image navigation (09a8975)

  • Split REPL reference into shell and command-line debugger pages (7f0577f)

    The REPL reference mixed the interactive shell with the command-line
    debugger. It is now two pages:

    • "Interactive Robot Framework with robotcode repl" — the shell.
    • "Command-line debugging with robotcode robot-debug" — a dedicated page
      for the debugger: ways to pause a run (line, keyword, embedded
      Breakpoint keyword, exceptions, stop-on-entry), the debug command set
      (stepping, call stack, inspecting and setting variables, breakpoint
      management, exception filters, log-and-continue), a worked example with
      the embedded Breakpoint keyword, and how it relates to the VS Code
      debugger.

    Other improvements:

    • Each command reference page now states which optional package to
      install to run it: robotcode[repl], [runner], or [analyze].
    • The CLI reference now documents robotcode robot-debug (alias run-debug)
      and lists it under the repl package.
    • Fixed the .doc command description: it shows documentation for
      libraries and resources the session has imported, not on demand.
  • Add "Working with AI Agents" reference page (e7c8d68)

    Describe how RobotCode lets AI coding agents work through the project's
    own robotcode CLI instead of guessing:

    • the chat plugin (a skill) and what it teaches the agent — discover,
      libdoc, REPL, results, analyze on the resolved project
    • the bundled VS Code plugin and its enableChatPlugins toggle, plus the
      extension shipping the robotcode CLI on the integrated terminal PATH
    • installing the plugin in other agents via the Open Plugins marketplace
    • recommended project-context setup (AGENTS.md / CLAUDE.md): environment,
      library init steps, the system under test, conventions
    • the CLI's AI-agent detection and its override env vars/flags
    • troubleshooting
  • Refine contribution guide and AI policy (b93cbf6)

    • AI_POLICY: welcoming, principle-led intro; clarify that what
      matters is human ownership, not whether an autonomous agent was
      involved; frame the purpose around protecting maintainer time,
      which is what ultimately keeps RobotCode sustainable
    • CONTRIBUTING: add a "find your path" orientation block and a
      warmer Project-Wide Rules intro; lead commit-signing setup with
      the low-effort SSH path; document that documentation-only changes
      need neither the test matrix nor linting (there is no Markdown
      linter); remove decorative emoji
    • PULL_REQUEST_TEMPLATE: mark the tests and linting checks N/A for
      documentation-only / non-code changes
  • Add results analysis guide (ebb2d12)

    A task-oriented user guide for the robotcode results family of
    commands, split into two parts:

    • Using the commands — what each subcommand does in the terminal,
      its useful flags, sort/filter semantics, and the pager / colour
      handling. Recipes for the common day-to-day questions: "what
      failed", "which test took longest", "drill into one test",
      "regression vs main".
    • JSON reference — schema rules (omitted vs null vs []),
      per-subcommand JSON shape, the body-item-type vocabulary used by
      log, the filtersApplied echo, and CI/jq recipes for pass-fail
      gates, slow-test reports, notification payloads and artefact
      gathering. Includes a note specifically for AI-driven workflows on
      why streaming the focused JSON beats parsing raw output.xml.

    Also linked from the reference index page.

  • Update json schema for robot.toml validations (6d4fd6d)

Performance

  • vscode: Smoother test explorer refreshes and live test updates (bc488d0)

    The Test Explorer used to flicker on every keystroke when the tree was
    expanded, slow to a crawl during multi-file edits (e.g. when an AI agent
    applies many edits at once), and sometimes show "AbortError" as a
    workspace entry at startup. Newly added or removed tests didn't appear
    in the tree until the refresh button was pressed.

    What's different now:

    • The tree only re-renders when the discovered tests actually change;
      identical re-discoveries leave the UI untouched.
    • One refresh runs at a time — newer edits cancel the in-flight
      subprocess instead of queuing up behind it.
    • Many edits in a short window collapse into a single workspace refresh.
    • External file changes no longer collapse expanded tree nodes.
    • Tests added or removed in a saved file appear/disappear immediately.
    • A workspace refresh no longer runs a redundant per-document discover
      for every open file.

    Also fixes a non-deterministic tag order from the discover command so
    the TS side can reliably detect real tag changes.

Refactor

  • diagnostics: Unify number-literal detection helpers (4dcb22f)

    Replace the direct robot.variables.finders.NumberFinder dependency and
    the two duplicated _try_resolve_number_literal static methods with
    shared try_resolve_number_literal / is_number_literal helpers in
    robot.utils.variables, consumed from model_helper, namespace_analyzer
    and the semantic analyzer.

  • language-server: Pure SemanticModel for signature help and code action (2a4e728)

    Both LSP feature paths now read everything off the SemanticModel — no
    RF AST walks, no ModelHelper position helpers. SemanticToken carries a
    precomputed LSP Range so consumers can use native operators
    (pos in tok.range, range in tok.range, tok.range.extend(...))
    instead of per-coordinate arithmetic. Output unchanged.

  • repl: Unify the setting-alias flag and tidy completion internals (c3d50fe)

    Review follow-up, no behaviour change:

    • Use one name, setting_import_aliases, for the flag everywhere (it was
      include_setting_aliases on candidates_for_rich).
    • Make _RobotCompleter's flag public so the _smart_tab key binding reads it
      off the active completer without reaching into a private attribute.
    • Move the assignment-target helpers above their first caller so there is no
      forward reference.
    • Add completer-level tests: the >>> completer forwards the flag into the
      candidate service, and Tab after a setting alias opens completion instead of
      inserting a cell separator.
  • runner: Extract discover data models and align info JSON casing (95c119b)

    The TestItem / ResultItem / Statistics / TagsResult / Info
    classes move out of discover.py into a dedicated _models.py,
    matching the layout the results family already uses. discover.py
    imports them from there, keeping the command file focused on click
    wiring and traversal.

    Info was the only discover model without CamelSnakeMixin, so
    discover info was emitting robot_version_string / python_version_string
    / system_version / robot_env while every other discover and
    results JSON key was already camelCase. It now uses the mixin too,
    so -f json discover info (and the TEXT key:value rendering) speaks
    the same robotVersionString / pythonVersionString / systemVersion
    / robotEnv shape as the rest. Tests and the reference docs follow.

  • runner: Adopt SuiteVisitor for body traversal in _search (000f2e9)

    Replace _body_matches' manual recursion + string-based item.type
    dispatch with a SuiteVisitor subclass that overrides one start_X per
    body-item type. Robot's own visitor handles the recursion; returning
    False from each hook short-circuits the rest of the traversal once a
    match is found.

    This drops every getattr in the file: the fully-qualified keyword name
    comes from the inherited Keyword.name property (which already composes
    libname.kwname on RF <7 result trees), result-tree-only .message is
    guarded with isinstance(item, StatusMixin), template (running-model
    only) is guarded with isinstance(test, RunningTestCase), and the
    For.assign vs For.variables rename is handled by one top-level
    RF_VERSION gate. RF 7+ Var/Group/Error types come in via a
    TYPE_CHECKING import with # type: ignore[attr-defined,unused-ignore].

    As a side-effect Error.values is now part of the searched fields —
    the old type-string dispatch silently skipped Error items.

  • runner: Adopt ResultVisitor for body traversal in results (5e5c88f)

    Replace manual body recursion (_collect_test_body's build-pattern and
    _count_all_messages' inline walk) with ResultVisitor subclasses that
    override the per-type end_X methods Robot's dispatch already provides.
    This removes the string-based item.type checks, eliminates the
    remaining getattr calls on stable Robot attributes, and lets every body
    item flow through one well-typed code path.

    Side cleanups in the same pass: hoist RF-version-conditional access
    (kwname/libname vs name/owner, For.assign vs For.variables,
    Result.elapsed_time vs elapsedtime, StatusMixin time fields) into
    top-level helpers gated on RF_VERSION, switch the Var/Group/Error
    imports to a TYPE_CHECKING-only # type: ignore[attr-defined,unused-ignore]
    form, and drop _eval_str — the profile is already evaluated before it
    reaches this code, and StringExpression.__str__ covers the rest.

  • runner: Drop more Any from results.py (15 → 6) (8b4f059)

    Three more rounds of tightening on top of the previous pass:

    • _eval_str / _iso accept object (we use generic isinstance /
      getattr / str() on them, no type-specific access).
    • _make_message_entry takes Message — it's only called for
      MESSAGE-typed body items, so the static type is precise.
    • _start_time / _end_time now return Optional[object] (forwarded
      unchanged to _iso, which accepts object).
    • The four RF-version-gated helpers (_iter_all_tests,
      _get_full_name, _keyword_name_and_owner, _loop_variables) get
      proper TestSuite / TestCase parameters where the polymorphism
      isn't on the input type. Internally they use getattr for the
      attributes whose existence depends on the runtime RF version, so
      mypy stays happy across the whole RF matrix without # type: ignore. Same behaviour, just less guesswork at call sites.

    Any remains on the six positions where it's the only honest answer:
    the heterogeneous body-item helpers (_keyword_name_and_owner,
    _loop_variables, _elapsed_seconds, _start_time, _end_time,
    inner build). Each item type carries different attributes; we
    dispatch by type string and getattr. Typing them as a Union of 9+
    result-tree classes would be heavier than the type safety it provides.

  • runner: Replace Any with proper Robot types in results.py (e70314f)

    Twenty-one stable-API positions get real type hints. Any survives only
    where it's genuinely the right answer:

    • The four RF-version-gated helpers (_iter_all_tests,
      _get_full_name, _keyword_name_and_owner, _loop_variables) —
      each branch accesses attributes that exist only on the matching RF
      version, and mypy sees only the installed one.
    • Body-item helpers and the inner build(item) in _collect_test_body
      — body items are a Keyword | For | If | Try | While | Var | Return | Group | Message union and we dispatch by getattr(item, "type").
    • _elapsed_seconds / _start_time / _end_time — called on suites,
      tests and keywords; truly polymorphic.
    • _iso, _eval_str, msg — string-or-something accepting helpers.

    For everything else, the actual Robot class lands in the signature:
    TestSuite and TestCase from robot.result, Result for the loaded
    execution result, ExecutionErrors for the errors collection,
    RobotBaseProfile for the resolved profile, and SuiteVisitor for the
    modifier list. No behaviour change; mypy is happy on every RF version
    in the matrix.

  • runner: Collapse results filter pipeline into a single pass (93c3cf6)

    Every results subcommand used to filter twice: first delegating
    include/exclude/suite/test to TestSuite.filter() via
    _apply_tree_filters, then re-walking the (still-full) tree inline to
    apply --status and --search. The matcher and the status-set lived
    in each command body; helpers like _collect_counts /
    _collect_failures took the matcher and status filters as parameters.

    Collapse that into one pre-pass and one pre-pass only:

    • Add a ByStatus SuiteVisitor in _search.py (Robot has no native
      status filter, so this stays ours, but in the same shape as
      ByLongName / SearchModifier).
    • Extend _apply_tree_filters to also accept status_filters and the
      search matcher, bundling every project-specific filter into a single
      ModelModifier pass that visits the tree once.
    • After the pre-pass, every surviving test has cleared every filter, so
      _collect_counts and _collect_failures shrink to a plain tally /
      [t for t in suite ... if t.status == "FAIL"]. The
      _normalise_statuses / _raw_test_search_matches helpers and the
      _STATUS_KEY_MAP constant lose their last callers and go away too.
    • One subtle behaviour shift: search now matches test.name and
      test.longname separately (via SearchModifier), where the old
      _raw_test_search_matches only saw the full name. That's a strict
      superset — an anchored regex like ^Pass.* now matches a test
      literally named Passing Test Three. Updated
      test_search_regex_anchors_and_alternation to pick a pattern that
      matches no field instead of relying on the old narrower contract.
  • runner: Polish results stats UX and docstrings (36e3996)

    • --by now displays the actual choices ([tag|suite|status]) in --help
      instead of the placeholder DIMENSION metavar, so the allowed values
      are discoverable without reading the docs.
    • stats --by suite groups by the suite's full name (e.g.
      MyProject.Login) rather than the bare leaf name. Suites that share an
      end name in different parts of the tree are now distinguished.
    • Drop internal report.html / "Mirrors X" justifications from
      user-facing docstrings — that's plan-rationale, not help text.
  • tests: Standardize on pytest-mock and shared analyzer fixtures (441618c)

    Replaces direct unittest.mock usage with the project's existing
    pytest-mock convention so tests are stricter (autospec) and new tests
    need less boilerplate.

  • Drop three orphaned private symbols (38a7995)

    Dead code surfaced by an unused-symbol sweep across packages/:

    • _BRANCHES (repl/_indent.py): a frozenset that was only there
      to document which Robot keywords (ELSE, ELSE IF, EXCEPT,
      FINALLY) the indent counter ignores. Replaced with a short
      inline comment — same self-documenting effect, no orphan symbol.
    • _split_comma (analyze/code/cli.py): a click callback added
      alongside the --xm / --xe exit-code-mask options, then
      superseded by _parse_exit_code_mask and never wired up.
    • _cancel_all_running_tasks (core/concurrent.py): originally
      the public cancel_running_callables; the FutureEx → Task
      rename privatised it but no caller followed.

Don't miss a new robotcode release

NewReleases is sending notifications on new releases.