Metadata-Version: 2.3
Name: robotter
Version: 0.21.2
Summary: Composition for GenAI dotfiles.
Author: David Brownell
Author-email: David Brownell <github@davidbrownell.com>
License: MIT
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.14
Requires-Dist: dbrownell-common>=0.16.0
Requires-Dist: jinja2>=3.1.6
Requires-Dist: pyyaml>=6.0.0
Requires-Dist: typer>=0.24.1
Requires-Python: >=3.14
Project-URL: Homepage, https://github.com/davidbrownell/robotter
Project-URL: Documentation, https://github.com/davidbrownell/robotter
Project-URL: Repository, https://github.com/davidbrownell/robotter
Description-Content-Type: text/markdown

**Project:**
[![License](https://img.shields.io/github/license/davidbrownell/robotter?color=dark-green)](https://github.com/davidbrownell/robotter/blob/master/LICENSE)

**Package:**
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/robotter?color=dark-green)](https://pypi.org/project/robotter/)
[![PyPI - Version](https://img.shields.io/pypi/v/robotter?color=dark-green)](https://pypi.org/project/robotter/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/robotter)](https://pypistats.org/packages/robotter)

**Development:**
[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
[![ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![ty](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ty/main/assets/badge/v0.json)](https://github.com/astral-sh/ty)
[![pytest](https://img.shields.io/badge/pytest-enabled-brightgreen)](https://docs.pytest.org/)
[![CI](https://github.com/davidbrownell/robotter/actions/workflows/CICD.yml/badge.svg)](https://github.com/davidbrownell/robotter/actions/workflows/CICD.yml)
[![Code Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/davidbrownell/f15146b1b8fdc0a5d45ac0eb786a84f7/raw/robotter_code_coverage.json)](https://github.com/davidbrownell/robotter/actions)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/davidbrownell/robotter?color=dark-green)](https://github.com/davidbrownell/robotter/commits/main/)

<!-- Content above this delimiter will be copied to the generated README.md file. DO NOT REMOVE THIS COMMENT, as it will cause regeneration to fail. -->

## Contents
- [Overview](#overview)
  - [Rendering a template](#how-to-use-robotter)
    - [Example Configuration](#example-configuration)
  - [Rendering a skill](#rendering-a-skill)
    - [Example Skill](#example-skill)
    - [Skill Template Directories](#skill-template-directories)
  - [Editing an agent's configuration](#editing-an-agents-configuration)
  - [Editing a skill](#editing-a-skill)
  - [Browsing an agent's global configuration](#browsing-an-agents-global-configuration)
  - [Browsing an agent's skills](#browsing-an-agents-skills)
- [Installation](#installation)
- [Development](#development)
- [Additional Information](#additional-information)
- [License](#license)

## Overview
`robotter` composes GenAI "dotfiles" (the instruction/configuration files read by AI coding agents) from a single source template.

Different agents read their configuration from different locations under different filenames. `robotter` renders one [Jinja2](https://jinja.palletsprojects.com/) template and writes the result to the appropriate location(s) for a target agent, at either global (user-level) or project scope:

| Agent | Value | Project Configuration | Global Configuration |
| --- | --- | --- | --- |
| Claude Code | `claude-code` | `AGENTS.md` | `~/.claude/AGENTS.md` |
| Cline | `cline` | `.clinerules/main.md` | `~/Documents/Cline/Rules/main.md` |
| Cursor | `cursor` | `.cursor/rules/main.mdc` | `~/.cursor/rules/main.mdc` |
| Gemini CLI | `gemini-cli` | `GEMINI.md` | `~/.gemini/GEMINI.md` |
| GitHub Copilot | `github-copilot` | `.github/copilot-instructions.md` | `<VS Code user>/prompts` |
| Grok | `grok` | `AGENTS.md` | `~/.grok/AGENTS.md` |
| OpenAI Codex | `openai-codex` | `AGENTS.md` | `~/.codex/AGENTS.md` |
| OpenCode | `opencode` | `AGENTS.md` | `~/.config/opencode/AGENTS.md` |

Some agents also support "skills" — reusable instruction sets stored under a per-skill location, keyed by a skill name. `robotter` writes rendered skills to the appropriate location(s) for those agents:

| Agent | Value | Project Skill | Global Skill |
| --- | --- | --- | --- |
| Claude Code | `claude-code` | `.claude/skills/<name>/SKILL.md` | `~/.claude/skills/<name>/SKILL.md` |
| Cline | `cline` | `.cline/skills/<name>/SKILL.md` | `~/.cline/skills/<name>/SKILL.md` |
| Cursor | `cursor` | `.cursor/skills/<name>/SKILL.md` | `~/.cursor/skills/<name>/SKILL.md` |
| Gemini CLI | `gemini-cli` | `.gemini/skills/<name>/SKILL.md` | `~/.gemini/skills/<name>/SKILL.md` |
| GitHub Copilot | `github-copilot` | `.github/skills/<name>/SKILL.md` | `~/.copilot/skills/<name>/SKILL.md` |
| Grok | `grok` | `.grok/skills/<name>/SKILL.md` | `~/.grok/skills/<name>/SKILL.md` |
| OpenAI Codex | `openai-codex` | `.agents/skills/<name>/SKILL.md` | `~/.agents/skills/<name>/SKILL.md` |
| OpenCode | `opencode` | `.opencode/skills/<name>/SKILL.md` | `~/.config/opencode/skills/<name>/SKILL.md` |

Templates may include optional [YAML](https://yaml.org/) frontmatter (preserved in the rendered output) and may compose other templates via the `include_configuration("<relative path>")` function, letting you maintain shared content once and assemble agent-specific files from it (see [Example Configuration](#example-configuration) and [Example Skill](#example-skill) below).

Only files whose names include a `.jinja` or `.jinja2` extension are rendered as Jinja2 templates. The extension does not have to be the last one, so `instructions.jinja.md` is a template. Every other file is written verbatim, which means content such as `{{ ... }}` is preserved as-is.

When rendering globally, a file that is not a template is installed as a symbolic link to its source, so edits to the source take effect without rendering again. Templates are always written as files, and project-level rendering always copies. Pass `--copy` to copy non-template files instead of linking them. If a symbolic link cannot be created (for example, on Windows without Developer Mode or administrator rights), rendering fails; pass `--copy` to render anyway.

### How to use `robotter`
Render a template to an agent's configuration location(s):

```shell
uvx robotter render <template> <agent> [<dir>] [--copy] [--verbose] [--debug]
```

| Argument / Option | Description |
| --- | --- |
| `<template>` | Path to the template file to render. |
| `<agent>` | Target agent: `claude-code`, `cline`, `cursor`, `gemini-cli`, `github-copilot`, `grok`, `openai-codex`, or `opencode`. |
| `<dir>` | Render project-level configuration under this directory. When omitted, global (user-level) configuration is rendered. |
| `--copy` | Copy a non-template file instead of creating a symbolic link to it when rendering globally. Project-level rendering always copies. |
| `--verbose` | Write verbose information to the terminal. |
| `--debug` | Write debug information to the terminal. |

**Examples**

Render `instructions.jinja.md` to the current user's global Claude Code configuration:

```shell
uvx robotter render instructions.jinja.md claude-code
```

Render `instructions.jinja.md` to the project-level OpenCode configuration under `./my-project`:

```shell
uvx robotter render instructions.jinja.md opencode ./my-project
```

#### Example Configuration
A configuration file is a [Jinja2](https://jinja.palletsprojects.com/) template with optional [YAML](https://yaml.org/) frontmatter. Use the `include_configuration("<relative path>")` function to compose shared content from another configuration file, letting you maintain that content once and reuse it across multiple templates.

The following `instructions.jinja.md` template includes a shared `shared/coding-standards.md` file:

```jinja
---
description: Instructions for AI coding agents
---
# Project Instructions

## Overview
This project composes GenAI dotfiles from a single source template.

## Coding Standards
{{ include_configuration("shared/coding-standards.md") }}
```

The included `shared/coding-standards.md` file is not a template, so its content is included verbatim (its frontmatter, if any, is ignored when included):

```markdown
- Prefer clarity over cleverness.
- Write tests for all new functionality.
- Document public interfaces.
```

The path passed to `include_configuration` is resolved relative to the file that contains the call, so a template in one directory can include a file located in a subdirectory (`"shared/coding-standards.md"`) or a parent directory (`"../coding-standards.md"`). Included files may themselves call `include_configuration`, allowing configuration to be composed from arbitrarily nested fragments.

Rendering the `instructions.jinja.md` template above produces the following output (the frontmatter is preserved; the `include_configuration` call is replaced with the rendered content of the included file):

```markdown
---
description: Instructions for AI coding agents
---
# Project Instructions

## Overview
This project composes GenAI dotfiles from a single source template.

## Coding Standards
- Prefer clarity over cleverness.
- Write tests for all new functionality.
- Document public interfaces.
```

### Rendering a skill
Some agents support "skills" — reusable instruction sets stored under a per-skill location. Render a skill template to an agent's skill location(s):

```shell
uvx robotter render_skill <template> <agent> [<dir>] [--copy] [--verbose] [--debug]
```

| Argument / Option | Description |
| --- | --- |
| `<template>` | Path to the skill template file to render, or to a [skill template directory](#skill-template-directories) whose files are each rendered into a skill named after the directory. |
| `<agent>` | Target agent: `claude-code`, `cline`, `cursor`, `gemini-cli`, `github-copilot`, `grok`, `openai-codex`, or `opencode`. |
| `<dir>` | Render the project-level skill under this directory. When omitted, the global (user-level) skill is rendered. |
| `--copy` | Copy non-template files instead of creating symbolic links to them when rendering globally. Project-level rendering always copies. |
| `--verbose` | Write verbose information to the terminal. |
| `--debug` | Write debug information to the terminal. |

When `<template>` is a file, the skill name is taken from its frontmatter `name` attribute (required); the rendered output is written to the per-skill location derived from that name (see the table in the [Overview](#overview) above). When `<template>` is a directory, the directory name is the skill name and frontmatter is not consulted for it — see [Skill template directories](#skill-template-directories) below. Not every agent supports skills; `render_skill` fails for an agent that does not.

#### Example Skill
A single-file skill template is a [Jinja2](https://jinja.palletsprojects.com/) template with [YAML](https://yaml.org/) frontmatter that must include a `name` attribute; that name determines the per-skill location the rendered output is written to. Like a configuration template, a skill template may compose shared content via `include_configuration("<relative path>")`.

The following `review_skill.jinja.md` skill template declares its name in frontmatter and reuses the shared `shared/coding-standards.md` file from the [Example Configuration](#example-configuration) above. Note that the template filename (`review_skill.jinja.md`) and the `name` attribute (`review`) are independent — for a single-file template the rendered output location is derived from `name`, not the filename:

```jinja
---
name: review
description: Review changes before committing
---
# Review

Review the pending changes before committing, confirming they satisfy the project's coding standards:

{{ include_configuration("shared/coding-standards.md") }}
```

Rendering the template preserves the frontmatter and replaces the `include_configuration` call with the rendered content of the included file, producing the following `SKILL.md`:

```markdown
---
name: review
description: Review changes before committing
---
# Review

Review the pending changes before committing, confirming they satisfy the project's coding standards:

- Prefer clarity over cleverness.
- Write tests for all new functionality.
- Document public interfaces.
```

**Examples**

Render `review_skill.jinja.md` to the current user's global Claude Code skill location (`~/.claude/skills/review/SKILL.md`):

```shell
uvx robotter render_skill review_skill.jinja.md claude-code
```

Render `review_skill.jinja.md` to the project-level Claude Code skill under `./my-project` (`./my-project/.claude/skills/review/SKILL.md`):

```shell
uvx robotter render_skill review_skill.jinja.md claude-code ./my-project
```

#### Skill Template Directories
A skill that needs supporting files — additional instructions, scripts, data — can be authored as a directory instead of a single file. Pass the directory as `<template>` and every file beneath it is written into one skill named after the directory:

```
review/
├── SKILL.jinja.md
├── reference.md
└── scripts/
    └── check.py
```

Rendering that directory for Claude Code produces `~/.claude/skills/review/SKILL.md`, `~/.claude/skills/review/reference.md`, and `~/.claude/skills/review/scripts/check.py`. The nested structure is preserved, and the `.jinja`/`.jinja2` extension is removed from each template's output filename (`SKILL.jinja.md` is written as `SKILL.md`).

Directory templates differ from single-file templates in the following ways:

| | Single file | Directory |
| --- | --- | --- |
| Skill name | The frontmatter `name` attribute (required) | The directory name; frontmatter is not consulted for the name |
| Files written | One | Every file beneath the directory, recursively |
| Required content | — | Must produce `SKILL.md` at its top level (`SKILL.md` or, for example, `SKILL.jinja.md`) |

Template files (those with a `.jinja` or `.jinja2` extension) are rendered with frontmatter preserved exactly as it is for a single-file template. Every other file is reproduced byte for byte (linked when rendering globally, copied otherwise), so images, scripts, and data files are never corrupted by having a leading `---` consumed as frontmatter. Symbolic links are created before any template is written, so a host that cannot create them fails without writing any files.

When rendering globally, a directory that contains no templates is installed as a single symbolic link to the source directory, replacing any existing skill directory, so files later added to or removed from the source take effect without rendering again.

All files are rendered before any file is written, so a malformed template fails without leaving a partially installed skill behind. A failure during the writes themselves (a permission error, a full disk) can still leave the skill incomplete.

`render_skill` fails, writing nothing, when the directory is empty, when it does not produce a top-level `SKILL.md`, when multiple files produce the same output filename (for example, `SKILL.md` and `SKILL.jinja.md`), when a file's output name matches a directory (for example, `scripts.jinja` and `scripts/run.py`), or when the directory name is not a valid skill name.

> [!NOTE]
> On Windows, the `SKILL.md` requirement is satisfied case-insensitively because the filesystem is, so a directory containing only `skill.md` is accepted there and rejected on Linux and macOS. Name the file `SKILL.md` exactly; agents that load skills look for that spelling.

**Examples**

Render the `review/` directory to the current user's global Claude Code skill location (`~/.claude/skills/review/`):

```shell
uvx robotter render_skill review claude-code
```

Render the `review/` directory to the project-level Claude Code skill under `./my-project` (`./my-project/.claude/skills/review/`):

```shell
uvx robotter render_skill review claude-code ./my-project
```

### Editing an agent's configuration
Open an agent's rendered configuration file in an editor:

```shell
uvx robotter edit <agent> [<dir>] [--verbose] [--debug]
```

| Argument / Option | Description |
| --- | --- |
| `<agent>` | Target agent: `claude-code`, `cline`, `cursor`, `gemini-cli`, `github-copilot`, `grok`, `openai-codex`, or `opencode`. |
| `<dir>` | Edit project-level configuration under this directory. When omitted, global (user-level) configuration is edited. |
| `--verbose` | Write verbose information to the terminal. |
| `--debug` | Write debug information to the terminal. |

The configuration file must already exist (for example, produced by a prior `render`); `edit` fails if it does not. The editor is selected from the `VISUAL` or `EDITOR` environment variable when set, otherwise the operating system's default handler for the file is used.

**Examples**

Edit the current user's global Claude Code configuration:

```shell
uvx robotter edit claude-code
```

Edit the project-level OpenCode configuration under `./my-project`:

```shell
uvx robotter edit opencode ./my-project
```

### Editing a skill
Open an agent's rendered skill file in an editor:

```shell
uvx robotter edit_skill <name> <agent> [<dir>] [--verbose] [--debug]
```

| Argument / Option | Description |
| --- | --- |
| `<name>` | Name of the skill to edit (for a single-file template, the name declared in its frontmatter; for a [skill template directory](#skill-template-directories), the directory name). |
| `<agent>` | Target agent: `claude-code`, `cline`, `cursor`, `gemini-cli`, `github-copilot`, `grok`, `openai-codex`, or `opencode`. |
| `<dir>` | Edit the project-level skill under this directory. When omitted, the global (user-level) skill is edited. |
| `--verbose` | Write verbose information to the terminal. |
| `--debug` | Write debug information to the terminal. |

The skill file must already exist (for example, produced by a prior `render_skill`); `edit_skill` fails if it does not. The editor is selected from the `VISUAL` or `EDITOR` environment variable when set, otherwise the operating system's default handler for the file is used. Not every agent supports skills; `edit_skill` fails for an agent that does not.

**Examples**

Edit the current user's global Claude Code `review` skill (`~/.claude/skills/review/SKILL.md`):

```shell
uvx robotter edit_skill review claude-code
```

Edit the project-level Claude Code `review` skill under `./my-project` (`./my-project/.claude/skills/review/SKILL.md`):

```shell
uvx robotter edit_skill review claude-code ./my-project
```

### Browsing an agent's global configuration
Open an agent's global (user-level) configuration directory in a file browser:

```shell
uvx robotter browse <agent> [--verbose] [--debug]
```

| Argument / Option | Description |
| --- | --- |
| `<agent>` | Target agent: `claude-code`, `cline`, `cursor`, `gemini-cli`, `github-copilot`, `grok`, `openai-codex`, or `opencode`. |
| `--verbose` | Write verbose information to the terminal. |
| `--debug` | Write debug information to the terminal. |

The directory is opened using the operating system's default file browser. The directory must already exist (for example, produced by a prior `render`); `browse` fails if it does not.

**Example**

Browse the current user's global Claude Code configuration directory:

```shell
uvx robotter browse claude-code
```

### Browsing an agent's skills
Open an agent's skills directory in a file browser:

```shell
uvx robotter browse_skills <agent> [<dir>] [--verbose] [--debug]
```

| Argument / Option | Description |
| --- | --- |
| `<agent>` | Target agent: `claude-code`, `cline`, `cursor`, `gemini-cli`, `github-copilot`, `grok`, `openai-codex`, or `opencode`. |
| `<dir>` | Browse the project-level skills directory under this directory. When omitted, the global (user-level) skills directory is browsed. |
| `--verbose` | Write verbose information to the terminal. |
| `--debug` | Write debug information to the terminal. |

The directory is opened using the operating system's default file browser. The directory must already exist (for example, produced by a prior `render_skill`); `browse_skills` fails if it does not. Not every agent supports skills; `browse_skills` fails for an agent that does not.

**Examples**

Browse the current user's global Claude Code skills directory (`~/.claude/skills`):

```shell
uvx robotter browse_skills claude-code
```

Browse the project-level Claude Code skills directory under `./my-project` (`./my-project/.claude/skills`):

```shell
uvx robotter browse_skills claude-code ./my-project
```

<!-- Content below this delimiter will be copied to the generated README.md file. DO NOT REMOVE THIS COMMENT, as it will cause regeneration to fail. -->

## Installation
Note that it isn't necessary to install `robotter` when running via `uvx`.

| Installation Method | Command |
| --- | --- |
| Via [uv](https://github.com/astral-sh/uv) | `uv add robotter` |
| Via [pip](https://pip.pypa.io/en/stable/) | `pip install robotter` |

### Verifying Signed Artifacts
Artifacts are signed and verified using [py-minisign](https://github.com/x13a/py-minisign) and the public key in the file `./minisign_key.pub`.

To verify that an artifact is valid, visit [the latest release](https://github.com/davidbrownell/robotter/releases/latest) and download the `.minisign` signature file that corresponds to the artifact, then run the following command, replacing `<filename>` with the name of the artifact to be verified:

```shell
uv run --with py-minisign python -c "import minisign; minisign.PublicKey.from_file('minisign_key.pub').verify_file('<filename>'); print('The file has been verified.')"
```

## Development
Please visit [Contributing](https://github.com/davidbrownell/robotter/blob/main/CONTRIBUTING.md) and [Development](https://github.com/davidbrownell/robotter/blob/main/DEVELOPMENT.md) for information on contributing to this project.

## Additional Information
Additional information can be found at these locations.

| Title | Document | Description |
| --- | --- | --- |
| Code of Conduct | [CODE_OF_CONDUCT.md](https://github.com/davidbrownell/robotter/blob/main/CODE_OF_CONDUCT.md) | Information about the norms, rules, and responsibilities we adhere to when participating in this open source community. |
| Contributing | [CONTRIBUTING.md](https://github.com/davidbrownell/robotter/blob/main/CONTRIBUTING.md) | Information about contributing to this project. |
| Development | [DEVELOPMENT.md](https://github.com/davidbrownell/robotter/blob/main/DEVELOPMENT.md) | Information about development activities involved in making changes to this project. |
| Governance | [GOVERNANCE.md](https://github.com/davidbrownell/robotter/blob/main/GOVERNANCE.md) | Information about how this project is governed. |
| Maintainers | [MAINTAINERS.md](https://github.com/davidbrownell/robotter/blob/main/MAINTAINERS.md) | Information about individuals who maintain this project. |
| Security | [SECURITY.md](https://github.com/davidbrownell/robotter/blob/main/SECURITY.md) | Information about how to privately report security issues associated with this project. |

## License
`robotter` is licensed under the <a href="https://choosealicense.com/licenses/MIT/" target="_blank">MIT</a> license.
