# What is Tessl?

The core components of the Tessl platform, the agentic-development problems they solve, and why teams choose Tessl.

{% hint style="info" %}
**Takeaways**

* What are the core components of the Tessl platform?
* What problems do those components solve?
* Why choose Tessl over the alternatives?
  {% endhint %}

Tessl is an open platform for managing agentic development across your organization. It takes you from scaling skills to building your software factory, one workflow at a time.

Tessl works with every popular coding agent and is made up of six components:

| Component                      | What it is                                                                                                                                                                                                                                                                                                         |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Registry & package manager** | Discover, install, version, and roll back skills and plugins like any other dependency. One searchable index of public and private context, agent-agnostic.                                                                                                                                                        |
| **Governance**                 | Scan and score every skill against best practices and your own company standards, with Snyk security scoring built in. RBAC controls who can create, publish, and view skills, while install and publish policies and required skills enforce your standards across every workspace, backed by a full audit trail. |
| **Evals**                      | Measure a skill or plugin's actual impact by running your agent on real-world tasks with and without the context. Every change ships with evidence instead of a guess.                                                                                                                                             |
| **Observability**              | See where skills actually activate across agent sessions, not just where they're installed. Mine agent logs to find recurring mistakes.                                                                                                                                                                            |
| **Context and Findings**       | Connect SCM accounts to map skills and configuration across repositories, then inspect reported drift, overlap, and unmanaged copies at the Organization level.                                                                                                                                                    |
| **Tessl Agent**                | A conversational agent that drives the platform and autonomously monitors and improves your agentic setup. Runs alongside your existing agents (open beta).                                                                                                                                                        |

## What problems does Tessl solve?

See how Tessl solves the most common agentic development problems:

* Installing skills from public sources exposes you to new supply-chain and prompt-injection risk. → [Protecting yourself from insecure skills](/tutorials/protecting-against-insecure-skills)
* Skills sprawl into overlapping, drifting copies with no enforced standard. → [Codifying and enforcing your company's skill standards](/tutorials/codifying-and-enforcing-skill-standards)
* You can't tell whether a skill actually improves agent output, so no one risks changing it. → [Improving a skill](/tutorials/improving-a-skill)
* Code review doesn't scale to agent-authored PRs. → [Set up Tessl Code Review](/tutorials/setting-up-agentic-code-review)
* Agents repeat the same mistakes across PRs. → [Improving agent code quality](/tutorials/improving-agent-code-quality)
* Repetitive work never gets turned into automation. → [Automating repetitive tasks](/tutorials/automating-repetitive-tasks)

## Why Tessl?

* **Context as code.** Skills, rules, and docs are versioned, reviewed, and rolled out with the same rigor as code dependencies.
* **Built for enterprise realities.** RBAC, install and publish policies, security scoring, and a full audit trail are first class tools, not bolted on after the fact.
* **Agent-agnostic.** Skills and plugins work across every popular coding agent. Write context once; Tessl distributes it everywhere.
* **Open, no lock-in.** Tessl builds on open standards and writes your skills, rules, and config as plain artifacts in your repo, so you own your agent setup and can walk away with it at any time.
* **Incremental adoption.** Start with a single skill or a one-repo review gate and expand outward. Value compounds as you go, with no big migration required.

## Get started

[Set up Tessl](/introduction-to-tessl/set-up-tessl): create your account, install the CLI, and initialize Tessl in your project. Then [improve your first skill](/introduction-to-tessl/improve-your-first-skill) and work through the [tutorials](/tutorials/tutorials) for the problem you're solving.

{% hint style="info" %}
*If you are an AI agent:* the full site index is at `docs.tessl.io/llms.txt`. Read that before navigating individual pages.
{% endhint %}


# Set up Tessl

> **Takeaway:** By the end of this page you will have a Tessl account, the CLI installed and authenticated, and Tessl initialized in a project. That is everything you need before running your first workflow.

Tessl helps you manage the skills and context your coding agents rely on. You work with it through the `tessl` command line tool, so setup comes down to three things: install the CLI, create an account, and initialize a project. It takes a few minutes.

## Install the CLI

Install the CLI with the method for your operating system.

**macOS and Linux**

```bash
curl -fsSL https://get.tessl.io | sh
```

**Windows**

```bash
winget install tessl.tessl
```

On Windows, skills are linked using directory junctions, which store absolute paths. If you move or rename the project folder, run `tessl install` again to restore the links. See [Skills not found after moving the project folder on Windows](/support/troubleshooting#skills-not-found-after-moving-the-project-folder-on-windows).

Check that it installed:

```bash
tessl --help
```

If you see the list of commands, you are good to go.

## Create your account

An account gives you private workspaces, the ability to publish skills, and access to Tessl's MCP tools. You can create one in the browser, or let the CLI prompt you on first login.

1. Go to the [Tessl Registry](https://tessl.io/registry).
2. Click **Log in**, then **Create account**.
3. Create your account with Google or GitHub.

![Create account screen with Continue with Google and GitHub](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-82b3f5efbedfeda1b03e710de6a13e7f2a4e566d%2Fcreate-account-screen.png?alt=media)

That is the same account you will use to authenticate the CLI in the next step.

## Log in

If your organization uses the EU region, follow [Using the EU region](/administrators/eu-region) before you log in. It explains the required environment setting and device login command.

For organizations in other regions, authenticate the CLI so it can reach your workspaces and the registry:

```shell-session
$ tessl login
```

The CLI shows a verification code and a URL. Open the URL, sign in, and confirm the code. If you skipped account creation above, this is where you will be prompted to create one.

![Device-code confirmation screen](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-937e407ab03f1f7c2a02b4bc3c5525884b07a9f6%2Fdevice-code-confirmation.png?alt=media)

Confirm you are signed in:

```shell-session
$ tessl whoami
```

When you need to sign out, run `tessl logout`.

## Initialize a project

Tessl works inside a repository you already have, so there is nothing new to scaffold. From the root of that repository, run:

```bash
cd your-repo
tessl init
```

This sets up your coding agent to use Tessl, creating a `tessl.json` if one does not already exist. It does not create or link a remote Tessl project on its own. If the repository is not linked to a project yet, run `tessl project create` to create one or `tessl project link` to link to an existing one. New to projects and workspaces? See Core concepts.

Setting up your agent means configuring the MCP connection so it can talk to Tessl. This is automatic for Claude Code, Cursor, Gemini, Codex, Copilot CLI, and Copilot in VS Code; see [Custom agent setup](/reference/custom-agent-setup) for anything it does not detect.

## Keep the CLI updated

The CLI updates itself in the background, so most of the time you do not need to think about it. Updates run only when you invoke a command, and they never block your work. If you want to update on demand, pin a version, or remove the CLI entirely, the [Installation reference](/introduction-to-tessl/set-up-tessl/installation) covers every option.

## Next

You have an account, an authenticated CLI, and an initialized project. Next, put it to work: scan your real skills, find one that needs improvement, and publish a better version in **Improve your first skill**.


# Installation

A step-by-step guide on how to install,  update or remove the Tessl CLI.

## Install

{% stepper %}
{% step %}
**Download the CLI**

The CLI can be installed using Curl (native), Homebrew or npm (deprecated).

{% tabs %}
{% tab title="Native" %}

```
curl -fsSL https://get.tessl.io | sh
```

{% endtab %}

{% tab title="Homebrew" %}

```
brew tap tesslio/tap #  Recommended as of Homebrew 6.0.0
brew trust tesslio/tap #  Recommended as of Homebrew 6.0.0
brew install tesslio/tap/tessl
```

{% endtab %}

{% tab title="winget" %}

```sh
winget install tessl.tessl
```

{% endtab %}

{% tab title="npm (deprecated)" %}

```
npm install -g @tessl/cli
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
**Test the CLI**

Run the following command to check that everything is working.

```shell-session
tessl --help
```

{% endstep %}

{% step %}
**Authenticate with Tessl \[optional]**

If your organization uses the EU region, follow [Using the EU region](/administrators/eu-region) before you log in. Set the EU environment in your shell and use device login as described there.

```shell-session
$ TESSL_ENV=app-eu tessl login --device
```

For organizations in other regions, log in to Tessl to access all CLI functionality:

```shell-session
$ tessl login
```

* This will display a verification code and URL

```
Please log in at the URL below (if your browser didn't open automatically):

  https://auth.tessl.io/device

Then confirm the following code:

  XYZA-BCDE

⠏ Waiting for confirmation…
```

* Open the URL in your browser and login to Tessl with either your GitHub or Google account.

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-45c365a2cf49eea7f87e0b6c2ae6c4b390e87af7%2Fimage%20(16)%20(1)%20(1).png?alt=media" alt="" width="375"><figcaption></figcaption></figure>

* Once authenticated, you can enter the device code in your browser window.

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-f3cfeaeb158ccaefd38ee97030e82663709e10ce%2Fimage%20(17)%20(1)%20(1).png?alt=media" alt="" width="375"><figcaption></figcaption></figure>

You can now verify your authentication status with:

```shell-session
$ tessl whoami
```

To log out when needed:

```shell-session
$ tessl logout
```

{% endstep %}
{% endstepper %}

## Automatic Updates

The Tessl CLI automatically checks for updates and installs them in the background. Updates run silently without interrupting your workflow.

* Updates check every 3 hours by default
* Updates only occur when you run CLI commands; they might briefly delay command exit
* To disable automatic updates, set `TESSL_AUTO_UPDATE_INTERVAL_MINUTES=0`
* Update logs are saved to `~/.tessl/auto-update.log` for troubleshooting

{% hint style="info" %}
Automatic updates are disabled in CI environments and on `winget`
{% endhint %}

## Manual Update

To manually update your CLI to the latest version:

{% tabs %}
{% tab title="Native" %}

```shell-session
tessl cli update
```

This command will update your CLI to the latest version for your current release channel.
{% endtab %}

{% tab title="Homebrew" %}

```shell-session
tessl cli update
```

This command will update your CLI to the latest version for your current release channel.
{% endtab %}

{% tab title="winget" %}

```sh
winget upgrade tessl.tessl
```

Running `tessl cli update` will provide a hint to use `winget upgrade tessl.tessl` instead
{% endtab %}

{% tab title="npm (deprecated)" %}

```shell-session
npm update -g @tessl/cli
```

{% endtab %}
{% endtabs %}

## Update to Specific Version

You can find all releases in the [changelog](/changelog-cli). To install/upgrade to a specific version:

{% tabs %}
{% tab title="Native" %}
Example to update to a specific version:

```sh
tessl cli update --target 0.86.0
```

{% endtab %}

{% tab title="Homebrew" %}
Installing a specific version is not supported in `brew`. To update to a specific version execute:

```sh
tessl cli update --target 0.84.0
```

{% endtab %}

{% tab title="winget" %}
Example to update to a specific version:

```sh
winget upgrade tessl.tessl --version 0.84.0
```

Running `tessl cli update` will provide a hint to use `winget upgrade tessl.tessl` instead
{% endtab %}

{% tab title="npm (deprecated)" %}
Example to install a specific version:

```sh
npm install -g @tessl/cli@0.84.0
```

{% endtab %}
{% endtabs %}

## Uninstall

{% tabs %}
{% tab title="Native" %}

```sh
rm -rf ~/.local/bin/tessl ~/.local/share/tessl
```

To remove Tessl config files, delete the `~/.tessl` directory
{% endtab %}

{% tab title="Homebrew" %}

```sh
brew uninstall tessl
rm -rf ~/.local/bin/tessl ~/.local/share/tessl
```

To remove Tessl config files, delete the `~/.tessl` directory
{% endtab %}

{% tab title="winget" %}

```sh
winget uninstall tessl.tessl
```

{% endtab %}

{% tab title="npm (deprecated)" %}
Determine which `npm` Tessl package you have:

```sh
npm ls -g | grep tessl
```

To uninstall `@tessl/cli`

```sh
npm uninstall -g @tessl/cli
```

To uninstall `tessl`

```sh
npm uninstall -g tessl
```

Remove the native binaries:

```sh
rm -rf ~/.local/bin/tessl ~/.local/share/tessl
```

To remove Tessl config files, delete the `~/.tessl` directory
{% endtab %}
{% endtabs %}


# Improve your first skill

> **Takeaway:** You will connect an SCM account, explore your Organization's Context and Findings, improve a local skill, and publish a new version your team can install.

This is the end-to-end loop you will repeat with Tessl: see what context your Organization has, find what needs attention, make it better, and ship it. A few terms show up along the way, namely skills, plugins, Organizations, workspaces, reviews, and evals. You can follow every step without reading ahead; each term links to a fuller explanation in [Core concepts](/introduction-to-tessl/core-concepts). As a quick orientation: an Organization owns SCM connections and reporting, a workspace controls private publishing, and a project ties Tessl to a single repository.

Before you start, make sure you have completed [Set up Tessl](/introduction-to-tessl/set-up-tessl): create an account, install and authenticate the CLI, and initialize a project. An Organization admin must connect the SCM account.

## Step 1: Connect an SCM account

In the Tessl web app, select your Organization, then open **Settings** and **Integrations**. Under **Source control**, add your provider and authorize the repositories Tessl should scan. See [Connect Tessl to GitHub](/distribute/connect-tessl-to-github) for the GitHub flow.

Tessl starts an account scan after the SCM connection is created. The connection can succeed before the scan starts or finishes, so use the connection page to check scan status, retry a failed scan, or change its **Scan scope**.

## Step 2: Explore Context

Select your Organization, then open **Context** in the sidebar. Context shows the skills and configuration Tessl observed, including their repositories and paths. You can search the collection and filter it by state, score, security result, and other attributes.

![Organization Context showing scanned skills and configuration](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-6d4bd64482c3c91dbe3941ebbc0c1a160ecf9e4e%2Forganization-context.png?alt=media)

After scan analysis succeeds, Tessl starts indexing the repository skills it found. There is no separate private-indexing switch or first-party approval step. Indexing, review, and security scoring are background work with separate failure paths, so a Context item can appear before it has scores, and not every item is guaranteed to receive a score.

## Step 3: Inspect Findings and results

Open **Findings** to see issues reported from the latest account scans. Use the filters to narrow the list by category, priority, repository activity, status, or workspace. Select a finding to review its evidence and suggested next step. Some advanced finding details depend on your Organization's plan.

![Organization Findings showing issues reported by Tessl](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-863d49446a64d30c2b573817dc6325ca816a4bba%2Forganization-findings.png?alt=media)

A skill can need attention for different reasons, and a single skill can have more than one result. They are worth telling apart because each has its own fix.

The first is a **low review score**. A review measures the quality of the skill itself: how clear and actionable its instructions are, and how well its description tells an agent when to use it. This is the kind of problem you fix by improving the skill.

The second is a **security finding**. Tessl scans skills and plugins for risky or malicious patterns in their content and behavior, such as prompt injection hidden in the instructions, hardcoded secrets, suspicious download URLs, or obfuscated text, and warns you about anything critical or high severity. This is about safety rather than quality, so it is separate from the review score.

For this walkthrough, pick a skill with a low review score, a security result, or a Finding such as drift, overlap, or stale content. If the skill has no score yet, you can still review it directly in the next step. Check out the repository that contains the skill and change into it before you run the review.

## Step 4: Improve it

Run a review to see the detailed breakdown:

```bash
tessl review run ./<skill> --workspace engteam
```

The review runs server-side against your workspace and returns three scores: validation (structure and formatting), description (how well the description triggers the skill at the right time), and content (how well the instructions guide the agent once loaded). Each comes with specific suggestions.

To apply those suggestions automatically, run a fix:

```bash
tessl review fix ./<skill> --workspace engteam
```

`tessl review fix` runs an improve-and-review loop, applying changes and re-reviewing until the score is high enough or it reaches its iteration limit, improving the skill in place. Run a review once more to confirm the score went up. Aim for 70% or higher before you publish.

A security finding is handled differently, because it is about safety, not quality. Tessl never blocks you; it warns you before installing a skill or plugin with critical or high severity findings so you can decide whether to proceed. If it is your own skill, fix the flagged content, for example by removing a hardcoded secret or a suspicious URL. If it is someone else's, treat a serious finding as a reason not to install, since it can signal a compromised skill. You can read more in Core concepts.

## Step 5: Publish a new version

Once the skill passes review, publish it to your workspace and bump the version in one step:

```bash
tessl skill publish ./<skill> --workspace engteam --bump patch
```

Use `patch`, `minor`, or `major` depending on the size of the change. Skills publish as **private** by default, so only members of your workspace can install them, and a review runs automatically on publish. Teammates pick up your new version by installing it:

```bash
tessl install engteam/<skill>
```

That is the full loop. You connected or uploaded repository context, found a skill to improve, checked its quality, improved it against best practices, and shipped a better version to your team.

## Where to go next

* Improve skills in more depth, including evaluating them against real tasks, in the Tutorials section.
* Roll context out across your repositories automatically with the [repository integration](/distribute/distribute-plugins-across-multiple-repositories).
* Understand the building blocks you just used in Core concepts.


# Core concepts

> **Takeaway:** Understand the building blocks of Tessl, namely the kinds of context, how they are packaged, where they live, and how Tessl measures quality, so the rest of the docs make sense.

If you have worked through the getting started flow, you have already used most of these ideas. This page names them and explains how they fit together.

## Context as software

Giving an agent good context is useful, but on its own it does not last. Libraries change, APIs evolve, and conventions drift, and static instructions quietly go stale. Tessl treats agent context the way you treat code: something you build, evaluate, distribute, and keep up to date. That single idea, context managed with a real lifecycle, is what everything below supports.

## The two kinds of context

Tessl works with two types of context, each suited to a different job.

| Type   | When it is used | Purpose                                            |
| ------ | --------------- | -------------------------------------------------- |
| Rules  | Always          | Your team's standards, applied to every task       |
| Skills | When relevant   | Procedural workflows, loaded when the task matches |

Rules and skills are where Tessl starts, not where it stops. Plugins can also carry other agent capabilities alongside context; see the [Plugins](#plugins) section below.

## Skills

A skill is a folder of instructions, and sometimes scripts, that teaches an agent how to do something: a specific workflow, a procedure, or a way of using a tool correctly. An agent loads a skill when its description matches the task at hand. Skills are the primary unit of context in Tessl, and they are agent-agnostic, so the same skill works across Claude Code, Cursor, Gemini, Codex, and others.

## Plugins

A plugin is a versioned bundle. It packages skills and rules, and can also carry MCP servers, which connect agents to external tools and services, and hooks, which run deterministic commands in response to specific agent events: `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `SessionStart`, and `Stop`. Everything in a plugin is installed, versioned, and updated as one unit, much like a package in npm or pip. When you want to share a coherent set of context and capabilities rather than a single skill, you publish a plugin.

## The Registry

The [Tessl Registry](https://tessl.io/registry) is where context is discovered and distributed. Your team's private context lives in your workspace; the public registry, with thousands of evaluated skills and plugins you can browse and install, is there when you want to reach beyond it. It is also where your published quality scores appear, so others can judge whether a skill is worth installing.

## Organizations

An Organization is the account-wide boundary for people, SCM connections, and reporting. Its **Context** view shows skills and configuration observed across connected or uploaded repositories. Its **Findings** view shows issues derived from the latest account scans. Organization members can view Context and Findings, while Organization admins manage SCM connections and scan settings.

## Workspaces

A workspace is the shared, private publishing boundary for a team. It holds the skills and plugins you publish privately and controls who can view, install, and publish them. Workspace context stays with your team unless you choose to share it.

## Projects

A project is the stable anchor for Tessl inside a repository. It identifies the repository and gives Tessl a fixed place to attach repository-connected data, such as evaluation runs, so your results stay tied to the right codebase over time even as branches and checkouts come and go.

## How Organizations, projects, workspaces, and plugins fit together

These concepts are easy to mix up because each one groups things, but they work on different axes. A plugin is content and capability: the skills, rules, MCP servers, and hooks you package. An Organization is account-wide access, SCM connections, Context, and Findings. A workspace is the team boundary for private publishing. A project is location: the tie between Tessl and one repository, so project results stay attached to the right codebase.

In practice, your Organization connects SCM accounts and reports Context and Findings across repositories. Teams publish private plugins to workspaces, and each project maps one repository into a workspace. An Organization can contain many workspaces, and a workspace can hold many plugins and back many projects.

## Reviews and evals

Tessl offers two ways to judge context quality, and they answer different questions. It is worth being clear on the distinction, because the names sound similar.

A **review** checks a single skill against best practices. It is fast and cheap, it runs automatically when you publish, and it produces a score across three areas: validation (structure and format), description (how well the description triggers the skill at the right time), and content (how well the instructions guide the agent once loaded). It is also where a team enforces its own skill-authoring standards, much like static analysis for code. Use a review for quick, structural feedback on the skill itself.

A **scenario eval** measures impact rather than structure. It runs an agent on real tasks, once with your context and once without, and compares the results, so you can see how much the context actually improves performance. Each scenario pairs a task with a rubric of weighted criteria used to grade the result. Evals are slower and more involved, and they attach to a project. Use an eval when you want evidence that context helps, not just that it is well formed.

In short: a review tells you whether a skill is well written; an eval tells you whether it works.

## Security checks

Alongside quality, Tessl checks the safety of skills and plugins. A security scan looks for risky or malicious patterns in a component's content and behavior, such as prompt injection buried in instructions, hardcoded secrets, suspicious download URLs, unverifiable external dependencies, or hidden and obfuscated text. Findings are graded by severity. Tessl surfaces critical and high severity findings when you install, warning rather than blocking and leaving the decision to you. A serious finding on a third-party skill can signal a supply-chain attack, so treat it with care.

## Next

With the vocabulary in place, the Tutorials section goes deeper on creating, improving, and distributing context.


# Overview

End-to-end walkthroughs for the most common agentic-development workflows on Tessl.

{% hint style="info" %}
**Takeaways**

* Which workflow solves the problem you're facing?
* Which Tessl components and commands each workflow uses.
  {% endhint %}

Each tutorial walks through one common agentic-development workflow from start to finish. Start with the one that matches the problem you're solving.

| Tutorial                                                                                                     | Use it when                                                                                  |
| ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| [Using Tessl as a package manager](/tutorials/using-tessl-as-a-package-manager)                              | You want to install, version, and publish skills like code dependencies.                     |
| [Protecting yourself from insecure skills](/tutorials/protecting-against-insecure-skills)                    | Installing skills from public sources exposes you to supply-chain and prompt-injection risk. |
| [Codifying and enforcing your company's skill standards](/tutorials/codifying-and-enforcing-skill-standards) | Skills sprawl into overlapping, drifting copies with no enforced standard.                   |
| [Improving a skill](/tutorials/improving-a-skill)                                                            | You can't tell whether a skill actually improves agent output, so no one risks changing it.  |
| [Set up Tessl Code Review](/tutorials/setting-up-agentic-code-review)                                        | Code review doesn't scale to agent-authored PRs.                                             |
| [Improving agent code quality](/tutorials/improving-agent-code-quality)                                      | Agents repeat the same mistakes across PRs.                                                  |
| [Automating repetitive tasks](/tutorials/automating-repetitive-tasks)                                        | Repetitive work never gets turned into automation.                                           |


# Using Tessl as a package manager

Use Tessl as a package manager for agent context: find and install skills, keep them up to date, and publish your own to a workspace.

{% hint style="info" %}
**Takeaways**

* How to find, install, and update skills from the registry or a git repo.
* How to publish your own skill to a workspace, public or private.
* How to develop a plugin locally and reinstall it on every change.
  {% endhint %}

Tessl manages agent context the way npm or pip manages code dependencies. Skills, rules, and docs are versioned packages you install into a project, update when new versions ship, and publish for others to consume. This tutorial covers the basics of using Tessl: the day-to-day package-manager flows you reach for most.

## Find and install a skill

Search the registry for skills and plugins. Tessl search is intelligent: instead of matching literal keywords, it interprets the meaning of your query and evaluates it against each result's name, description, and source, then ranks them by how closely they fit what you described. So you can search in plain language by what a skill does, as well as by its package name:

```bash
tessl search "react testing"
```

You can also browse the registry UI, open a skill, and copy its install command straight from the page.

Many commands take a workspace name. Run `tessl workspace list` first to see the workspaces you have access to, then use one of those names wherever a command shows the `<workspace>` placeholder.

Install a skill or plugin by its registry name, a GitHub repo, or a local path:

```bash
tessl install <workspace>/<skillname>  # from the registry
tessl install github:acme/skills       # from a GitHub repo
tessl install file:./local-plugin      # from a local directory
```

By default Tessl installs into the current project and records the dependency in your `tessl.json` manifest. Pass `--global` to install into `~/.tessl` for every project, and `--skill` to pick specific skills from a multi-skill source. Without `--agent`, Tessl installs for every coding agent it detects in the project; pass `--agent` to target a specific one instead.

## Keep installed skills up to date

See which installed dependencies have newer versions:

```bash
tessl outdated
```

This lists each dependency with its current, compatible-update, and latest version. Apply the updates:

```bash
tessl update
```

`tessl update` selects the compatible updates and lets you choose which to apply. A new major version that could break compatibility is held back until you pass `--force`. Pass `--yes` to apply all compatible updates without prompting. Remove a dependency you no longer want with `tessl uninstall <workspace>/<skillname>`.

## Publish your own skill

If you have a `SKILL.md` that is not yet a package, turn it into one:

```bash
tessl skill import ./path-to-skill --workspace <workspace>
```

This writes a `.tessl-plugin/plugin.json` manifest under the skill directory. If you omit `--workspace`, the manifest is written with the workspace set to `"local"` and you will need to update `.tessl-plugin/plugin.json` to replace `"local"` with your workspace name before you can publish.

Publish the skill to your workspace:

```bash
tessl skill publish ./path-to-skill --workspace <workspace>
```

The first publish starts at version `0.1.0`. On later publishes, pass `--bump patch`, `--bump minor`, or `--bump major` to increment the version. Use `--dry-run` to validate what would be published without publishing.

## Choose public or private

A skill is published privately by default: it goes to its workspace, where only members can find and install it. If you expect to see it in the public registry and do not, this is usually why. Add `--public` to publish it for anyone to install:

```bash
tessl skill publish ./path-to-skill --workspace <workspace> --public
```

Workspaces are how Tessl scopes who can see and install a package. List the workspaces you belong to with `tessl workspace list`. A skill's full name includes its workspace, for example `<workspace>/<skillname>`, so workspace names are visible to anyone who can see the package.

## Develop a plugin locally

A plugin is a bundle of skills, rules, commands, and hooks described by a `.tessl-plugin/plugin.json` manifest. You can install one straight from a local directory and iterate on it before publishing.

First add the local plugin to your project:

```bash
tessl install file:./my-plugin
```

Then run install in watch mode, which re-syncs every local source in your manifest whenever its files change:

```bash
tessl install --watch-local
```

Leave it running while you edit `./my-plugin`. Each save reinstalls the updated plugin into your project, so you can test changes against a real codebase before you publish. When it is ready, publish the whole bundle with `tessl plugin publish`:

```bash
tessl plugin publish ./my-plugin --workspace <workspace>
```

Like `tessl skill publish`, it accepts `--bump patch|minor|major` to increment the version and `--dry-run` to validate without publishing.

## Where to go next

* [Creating skills](/creating-skills-and-plugins/create-a-skill) and [Creating plugins](/creating-skills-and-plugins/create-a-plugin) to go deeper on authoring.
* [Distributing via registry](/distribute/distributing-via-registry) to share with your team or publicly.
* [Protecting yourself from insecure skills](/tutorials/protecting-against-insecure-skills) to review what you install for security.


# Protecting yourself from insecure skills

Inspect skills across your Organization, read their Snyk-powered security scores, and use install policies to block risky skills before they reach a developer.

{% hint style="info" %}
**Takeaways**

* How to inspect skills across your Organization and tell first-party skills from matched third-party ones.
* How Snyk-powered security scores appear in Context, and how to review any skill on demand.
* How an admin sets org, workspace, or project install policies to block risky skills.
  {% endhint %}

Skills run inside your agent with access to your code, your shell, and your secrets. A skill pulled from a public source can carry a prompt-injection payload or instructions that exfiltrate data, and the risk compounds once a skill spreads across repos. The danger is not only external: a first-party skill can leak credentials through an honest mistake by the developer who wrote it, or carry a deliberate payload from an insider. This tutorial maps what is already installed, reads each skill's security score, and gates future installs against a policy.

## Find skills across your Organization

The primary path is to [connect an SCM account](/use/understand-your-skill-estate), then open your Organization's **Context** page. Tessl scans the repositories in scope and shows the skills and configuration it observed.

You can also upload a GitHub scan through your local `gh` CLI:

```bash
tessl inventory import --org your-org --workspace engteam
```

This relies on the [GitHub CLI](https://cli.github.com/) being installed and authenticated locally, and on being logged in to Tessl for the upload, so run `gh auth login` and `tessl login` first. The workspace selects the reporting Organization and checks your publishing access. Upload results remain owned by that workspace, so only people who can view the workspace can view those Context and Findings results. The upload does not establish SCM ownership and does not require an SCM connection.

Without `--repo`, the scan selects eligible repositories and scans up to the 100 most recently pushed. It does not provide complete repository coverage. Within the repositories it scans, it collects the `SKILL.md` files and manifests it finds. Skills that match a published registry skill are third-party; skills unique to your repositories are first-party. Open your Organization's **Context** page to browse the result and **Findings** to inspect reported issues. Use `--repo` to choose a specific subset, or `--dry-run ./inventory.json` to write it to disk without uploading.

## Read the security scores

Security scores are powered by Snyk. Each score is a severity: `LOW`, `MEDIUM`, `HIGH`, or `CRITICAL`.

Indexing and security scoring run as separate background work after scan analysis. A skill can appear in Context before its security score is available, and not every skill is guaranteed to receive a score. Use the available scores and Findings to prioritize investigation, then run an on-demand review when you need a current result for one skill.

## Review a single skill on demand

You do not need an Organization scan to check one skill. Review any skill directly from its directory:

```bash
tessl review run security ./path-to-skill --workspace engteam
```

This is the quickest way to vet a skill before you install it, or to gate a skill in CI. The review is powered by Snyk and returns the same severities. Add `--json` for machine-readable output, or `--fail-on high` to make the command exit non-zero when a finding meets or exceeds a severity. The `--fail-on` values are lowercase: `low`, `medium`, `high`, `critical`.

## Gate risky installs with a policy

Context, Findings, and security scores help you identify risk. An install policy stops a risky skill from being installed at all. As a security admin, this is your main lever.

Policies can be set at three levels, and the tightest one wins:

* Org level: the broadest reach and your most impactful control as a security admin. It applies to every workspace and project in the org.
* Workspace level: tightens the org policy for a single workspace.
* Project level: tightens it further for a single project.

Set policy in the app under `Settings → Policy`. Setting org-level policy is an organization admin action, so if you do not see it, ask your admin. A policy combines three kinds of rule:

* Security threshold: choose the severity at which an install is warned and the severity at which it is blocked, for example warn at `MEDIUM` and block at `HIGH`.
* Source restriction: limit the registry to your own org, or to Tessl plus your org, and choose whether installs from git sources are allowed at all. When git sources are allowed, add a git source allowlist to permit only specific hosts and orgs, for example `github.com/your-org`, so developers can still install public skills from your trusted vendors while everything else is blocked.
* Minimum release age: for git sources, require a release to be a minimum number of days old before it can be installed.

Once a policy is in place, `tessl install` evaluates every source against it:

* A source under the warn threshold installs without interruption.
* A source that hits the warn threshold prompts for confirmation. A developer can proceed, or pass `--accept-warnings` to skip the prompt in automation.
* A source that hits the block threshold cannot be installed. There is no override.

```bash
tessl install your-org/some-skill
```

When the skill's score meets the block threshold, the install is refused and the offending finding is shown. The headline uses a severity label: `Passed` for `LOW`, `Advisory` for `MEDIUM`, `Risky` for `HIGH`, and `Critical` for `CRITICAL`. A `Risky` headline below therefore means a `HIGH` score that met the block threshold:

```
Blocked by install policy (not added to tessl.json):
  ✖ Security  Risky · Do not use without reviewing
    your-org/some-skill
    W011 Third-party content exposure detected.
    View full report: https://app.tessl.io/...
```

{% hint style="info" %}
You can also set a publish policy that blocks publishing a skill whose security score is under a threshold, so risky skills never enter your registry in the first place. See [Codifying and enforcing your company's skill standards](/tutorials/codifying-and-enforcing-skill-standards).
{% endhint %}

## Where to go next

* [Understand your organization's context](/use/understand-your-skill-estate) for SCM connections, Context, and Findings.
* [Administrating organizations, workspaces, and roles](/administrators/administrating-organizations-workspaces-and-roles) to control who can install and publish.
* [Codifying and enforcing your company's skill standards](/tutorials/codifying-and-enforcing-skill-standards) to extend governance from security to quality.


# Codifying and enforcing your company's skill standards

Capture your team's quality bar as a custom review rubric and enforce it on every skill in CI.

{% hint style="info" %}
**Takeaways**

* How to gate pull requests on a review score using `--threshold`.
* How to authenticate CI with an API key so reviews run without manual login.
* How to fork the default rubric to encode your organization's own quality bar.
* How to apply a custom reviewer consistently across local reviews and CI.
* How to scale your standard from per-run flags to organization and workspace policy.
  {% endhint %}

When a team shares skills, quality stops being a personal matter. A weak skill that one person tolerates becomes everyone's problem once it is in the shared repository, shaping how every agent on the team behaves. This tutorial shows you how to hold a quality line automatically, first by setting a score threshold that blocks substandard skills from merging, and then by forking the default rubric to encode what your organization actually cares about.

## The core workflow

1. Run `tessl review run --threshold` locally to confirm the score gate works
2. Create an API key and add it as a CI secret so reviews can run non-interactively
3. Add a review step to your pull request workflow
4. Install `tessl/review-plugin-creator` and build a custom reviewer plugin
5. Apply the custom reviewer via `--review-plugin` in local reviews and CI
6. Set organization and workspace policy so your standard applies by default

## Prerequisites

* Tessl installed and initialized (`tessl init`)
* Authenticated (`tessl login`)
* A workspace to attribute reviews to
* A GitHub repository with Actions enabled (for the CI steps)

## Step 1: Test the threshold locally

Before wiring anything into CI, confirm the score gate behaves as expected:

```bash
tessl review run ./my-skill --workspace <workspace> --threshold 80
```

`--threshold` takes a 0-100 integer. The command exits non-zero when the score falls below it, which is what fails a CI step. The default is 0, which never fails. Any value above 0 enforces the gate.

Add `--json` when you need machine-readable output in a script that parses the result:

```bash
tessl review run ./my-skill --workspace <workspace> --json --threshold 80
```

`--workspace` is required when `--json` is set, because a non-interactive run cannot prompt for one.

## Step 2: Authenticate CI

A review runs against your workspace, so CI needs to authenticate without an interactive login. Create an API key for the workspace:

```bash
tessl api-key create --workspace <workspace>
```

Copy the key and add it as a repository secret named `TESSL_TOKEN` in your GitHub repository settings (**Settings → Secrets and variables → Actions → New repository secret**).

## Step 3: Add the review step to CI

Tessl publishes a GitHub Action that installs the CLI. Add a job to your pull request workflow:

```yaml
name: Skill review

on:
  pull_request:

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: tesslio/setup-tessl@v2
        with:
          token: ${{ secrets.TESSL_TOKEN }}
      - name: Review skill
        run: tessl review run ./my-skill --workspace <workspace> --json --threshold 80
```

The step fails when the score falls below 80. Adjust the path, workspace, and threshold to match your repository. For a full reference of flags and options, see [Review, lint & publish with GitHub Actions](/distribute/review-and-publish-with-github-actions).

## Step 4: Build a custom reviewer

The default rubric scores against general best practices. To score against your organization's own standards, build a custom reviewer plugin with the `create-review-plugin` skill:

```bash
tessl install tessl/review-plugin-creator
```

Ask your agent to create a reviewer. It scaffolds the plugin structure, helps you define judges and their weights, writes the rubric files, and validates the finished plugin by running a test review.

A reviewer plugin is a standard Tessl plugin. Its key components are a `config.json` that sets the weight each judge contributes to the score, and one rubric file per judge that defines the scoring dimensions. The weights across validation and all judges must sum to 1.0.

The default rubric is worth forking rather than building from scratch. It scores a skill's description on specificity, completeness, trigger term quality, and distinctiveness, and the content on conciseness, actionability, workflow clarity, and progressive disclosure. Forking lets you adjust those weights or add judges for concerns specific to your team.

## Step 5: Apply the custom reviewer

Point any review at your reviewer with `--review-plugin`, passing a local path or a `workspace/plugin` registry reference:

```bash
tessl review run ./my-skill --workspace <workspace> --review-plugin <reviewer-plugin>
```

The same flag works in `tessl review fix` and in your CI gate, so the standard you define in the rubric is the standard you enforce everywhere:

```bash
tessl review run ./my-skill --workspace <workspace> --review-plugin <reviewer-plugin> --json --threshold 80
```

Pass `--review-plugin` explicitly on each run, or set your reviewer as the workspace default so every review in that workspace uses your standard without the flag. The next step covers how that default fits into wider policy.

## Step 6: Set policy across your organization

The steps so far enforce your standard one run at a time: a threshold on a command, a reviewer flag you pass, a CI job you maintain. Policy is how that standard scales beyond a single run, so it applies by default across an organization or a workspace without anyone having to remember a flag.

Policy is set in the app under `Settings → Policy`. Setting organization-level policy is an organization admin action, so if you do not see it, ask your admin.

### Organization-level policy

An **organization** is the top-level entity, and policy set there applies to every workspace and project inside it. This is where you set a single standard for the whole company, so a workspace inherits it by default rather than starting from nothing. For a security admin, this is the broadest and most impactful control.

### Workspace-level scoping

Each **workspace** inherits the organization policy and can tighten it for its own skills. A workspace can raise the bar above the organization default, but it cannot loosen what the organization sets: lower levels only tighten, never relax, and when levels disagree the strictest one wins. Projects can tighten a workspace policy further in the same way. This lets a stricter team hold a higher bar without weakening the company-wide floor.

The workspace default reviewer is a separate workspace setting, configured under `Settings → Reviews` rather than in the policy editor. As covered in Step 5, setting it means every review in that workspace applies your rubric without passing `--review-plugin`. For more on reviewer defaults, see [Define your own quality standards](/codifying-and-enforcing-your-skill-standards/define-your-own-quality-standards).

### Require a minimum skill review score before publishing

An organization or workspace can require every skill in a plugin version to meet a minimum review score before that version becomes available. This is a publish-time quality check, not an install-time check or an eval-score check. Tessl evaluates every skill separately: it does not average their scores. A score equal to the threshold passes.

To set the organization-wide floor, open **Settings → Policy → Publish policy → Skill review score policy**. Turn on **Enable minimum skill review score**, then enter an integer from 0 to 100 in **Minimum skill review score**. Changes save automatically.

You can set the same policy in workspace settings. Open **Effective policy** to see the threshold that applies and where it came from. When both levels set a threshold, the higher threshold wins. A workspace can raise the organization floor, but it cannot weaken it.

For example, a threshold of 80 blocks a plugin version with skill scores of 91 and 78. Scores of 91 and 85 satisfy the review-score rule. The version must also pass every other enabled publication check.

The policy applies to versions published after you enable it. It does not re-check versions that are already published. For help with a blocked version, see [Publication checks and blocked versions](/creating-skills-and-plugins/publish-and-update#publication-checks-and-blocked-versions).

### Public-skill controls

Publishing skills publicly is controlled at both levels. An organization can block public publishing across all its workspaces, and an individual workspace can block it for itself. A workspace can only add restriction, so when the organization blocks public publishing, a workspace cannot re-enable it. To let only some workspaces publish public skills, leave public publishing allowed at the organization level and block it in each workspace that must stay private.

### Install gating

An install policy stops a skill that fails your security bar from being installed at all. It gates on the Snyk-powered security score, not the review quality score: choose the severity at which an install is warned and the severity at which it is blocked, so a skill that meets the block threshold is refused with the offending finding shown and no override. You can also restrict installs by source and require a minimum release age for git sources. For the full walkthrough of install policy, see [Protecting yourself from insecure skills](/tutorials/protecting-against-insecure-skills).

{% hint style="info" %}
Install policy gates on the security score. Publish policy can gate on both the security score and a minimum skill review score. You can also use `--threshold` in CI to check skill quality before publishing. A review score is not an eval result and does not guarantee runtime performance.
{% endhint %}

## Next steps

* [Gate skill quality in CI](/codifying-and-enforcing-your-skill-standards/gate-skill-quality-in-ci) - full reference for `--threshold`, `--json`, and `tessl api-key create`.
* [Define your own quality standards](/codifying-and-enforcing-your-skill-standards/define-your-own-quality-standards) - deeper coverage of reviewer plugin structure and the weight invariant.
* [Review, lint & publish with GitHub Actions](/distribute/review-and-publish-with-github-actions) - complete GitHub Actions workflow for skill CI.


# Improving a skill

Use reviews and evals to measure a skill's impact, find what's holding it back, and ship an improvement with evidence.

{% hint style="info" %}
**Takeaways**

* How to run a review to get a quality score and identify what to fix.
* How to apply automated improvements with `tessl review fix`.
* How to generate evaluation scenarios and measure whether a change actually helps.
* How to use Skill Optimizer to run the full cycle automatically.
  {% endhint %}

A skill can look well-written and still fail to improve what an agent does. This tutorial walks through both checks: a review, which tells you whether the skill is well-formed; and an evaluation, which tells you whether it actually changes agent behaviour. Together they give you the evidence to ship a skill with confidence.

## The core workflow

1. Run a review to get a quality score and see what to fix
2. Apply automated fixes and re-run until the score is in good shape
3. Generate evaluation scenarios from the skill
4. Run an evaluation to measure the before/after delta
5. Edit, re-run, and confirm the improvement holds

## Prerequisites

* Tessl installed and initialised (`tessl init`)
* Authenticated (`tessl login`)
* A workspace to run reviews against
* A plugin containing the skill you want to improve (see [Create a skill](/creating-skills-and-plugins/create-a-skill) if you have a standalone `SKILL.md`)

## Step 1: Run a review

A review scores the skill on how well it is written: whether the description is specific enough for an agent to load it at the right moment, and whether the content is concise, actionable, and complete.

```bash
tessl review run ./my-skill --workspace <workspace>
```

The output shows a score out of 100 broken down by dimension:

```
Judge Evaluation
  Description: 72%
    specificity: 2/3 - names the domain but lists variations of one action
    completeness: 2/3 - trigger present but capability understated
    ...
  Content: 85%
    conciseness: 3/3
    actionability: 2/3 - steps present but no worked example
    ...
Review Score: 79%
```

A score above 90 means the skill conforms well to best practices. 70-89 is good. Below 70 needs work. The dimension breakdown tells you where to focus: a low description score usually means the skill is not activating reliably; a low content score usually means the instructions are not specific enough for the agent to act on.

## Step 2: Apply fixes

`tessl review fix` runs an automated improve-and-review loop. It applies edits, re-runs the review, and iterates until the score stops improving:

```bash
tessl review fix ./my-skill --workspace <workspace>
```

Pass `--threshold 85` to keep going until it hits that score, `--max-iterations 5` to allow more rounds, or `--yes` to apply changes without prompting. Re-run `tessl review run` afterwards to confirm the final score.

## Step 3: Generate evaluation scenarios

A review measures whether the skill reads well. An evaluation measures whether it actually changes what an agent does. Generate scenarios from the skill:

```bash
tessl scenario generate ./my-skill
```

Generation runs server-side. Check progress with `tessl scenario list --mine`, then download once it completes:

```bash
tessl scenario download --last
```

By default the generator targets three scenarios. Pass `--count 5` for broader coverage. Only scenarios that pass a feasibility check are saved, so you may get fewer than you ask for on a narrowly scoped skill.

## Step 4: Run an evaluation

```bash
tessl eval run ./my-skill
```

By default the agent solves each scenario twice, once without the skill and once with it, and a judge scores both. The summary shows the baseline average, the with-skill average, and the difference:

```
Summary
  Scenarios:                3 completed
  Without this plugin avg:  71%
  With this plugin avg:     92%
```

A large positive difference means the skill reliably moves the agent toward the right behaviour. Little or no difference usually means the skill is not activating, or the instructions are not specific enough to change what the agent does. Run with `--runs 3` to average out model variance before drawing conclusions.

## Step 5: Edit and confirm

If the evaluation shows little impact, the most common causes are a description that is too vague to trigger the skill reliably, content that tells the agent what to do without showing how, or instructions that are ambiguous enough for the agent to ignore. Make your edits, re-run the review to confirm the score held, then re-run the evaluation:

```bash
tessl review run ./my-skill --workspace <workspace>
tessl eval run ./my-skill --label "after-edits"
```

Compare the two runs with `tessl eval list --mine`. The scenarios from the first run are saved in the plugin's `evals/` directory and become a regression test you can re-run after every future change.

## Shortcut: Skill Optimizer

If you want the full cycle handled automatically, install Skill Optimizer and ask your agent to improve a skill:

```bash
tessl install tessl/skill-optimizer
```

Skill Optimizer runs review, applies fixes, escalates to scenario generation and evaluation, diagnoses failures, and re-runs until scores improve. Use it when you want results fast; use the manual commands when you want to understand what is happening or debug a specific dimension.

## Next steps

* [Check a skill's quality using review](/improving-your-skills/reviewing-skills) - full reference for `tessl review run`, all flags, and score interpretation.
* [Prove a skill works using evaluation](/improving-your-skills/evaluate-skill-quality-using-scenarios) - deeper coverage of scenario generation, agent selection, and reading eval output.
* [Codifying and enforcing your company's skill standards](/tutorials/codifying-and-enforcing-skill-standards) - gate future changes in CI so the bar you set here holds for everyone.


# Set up Tessl Code Review

Install the Tessl Review GitHub App, enable a repository, choose when reviews run, ask for a review by comment, and configure lenses with .tessl-code-review\.yml.

{% hint style="info" %}
**Takeaways**

* Install the Tessl Review GitHub App from the Tessl web app, then enable each repository you want reviewed. Installing and enabling are two separate steps.
* Choose a trigger policy, set the review behavior, and ask for a review by mentioning `@tessl-code-review` in a comment.
* Route lenses and set the finding policy with an optional `.tessl-code-review.yml` file.
  {% endhint %}

For GitLab merge requests, use the CLI locally or integrate it into your own GitLab CI pipeline. This isn't a GitLab app. See [Run Code Review with GitLab](/tutorials/setting-up-agentic-code-review/code-review-in-gitlab).

The Tessl Review GitHub App is the supported way to run Tessl Code Review on GitHub pull requests. It reviews the exact head of a pull request and publishes one native GitHub review, running on Tessl's own infrastructure. There is no workflow to write, no token to store, and no runner to configure: you install the App, enable a repository, and reviews run from there.

Reviews run on Tessl's infrastructure. Each run mints a GitHub token scoped to the single repository it reviews, so no long-lived credential sits in your repository.

## Install and enable are two separate steps

Getting reviews on a repository takes two distinct actions, and both are required:

1. **Install the App** on a GitHub account or organization. This grants Tessl access to the repositories you select.
2. **Enable the repository** in Tessl. This decides whether reviews actually run.

Installing the App does not start any reviews. A repository the App can reach but that you have not enabled is reviewed by nothing. This is the most common point of confusion, so confirm both steps are done before you expect a review.

## Step 1: Install the App

1. Sign in to [tessl.io](http://tessl.io).
2. Select your organization, then open **Code reviews** in the sidebar.
3. Select **Connect GitHub**.
4. On GitHub, choose the account or organization to install into, and choose whether Tessl may access all repositories or a selected list.

You can install Tessl Reviewer more than once, for example on a personal account and one or more organizations. The repositories they reach share one table on **Code reviews**, each row naming the account it came from.

{% hint style="info" %}
Start every install from the Tessl web app. Do not follow a `github.com/apps/...` install link copied from elsewhere: the web app builds each install link for one person and expires it within minutes, so a shared or copied link fails by the time you use it.
{% endhint %}

## Step 2: Enable a repository

After the App is installed, return to **Code reviews** and select a repository to enable. A repository stays unreviewed until you enable it, even when the App already has access to it.

Enabling a repository is where the review policy below is set: its trigger policy and review behavior.

## Choose a trigger policy

The trigger policy decides when a review runs. Two policies are available:

| Policy             | Label                    | When it reviews                                                                                                                                               |
| ------------------ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `open-and-command` | When opened or requested | When a pull request is opened or reopened, when a draft is first marked ready for review, and when someone requests a review by comment. This is the default. |
| `every-push`       | After every update       | The same as above, plus when the pull request is pushed to.                                                                                                   |

Start with **When opened or requested**. It reviews a pull request once when it opens and again whenever someone asks, without spending a review on every commit.

Under **After every update**, closely spaced pushes are debounced onto the latest head rather than each starting its own review. A burst of commits collapses to one review of the newest commit, so an intermediate head you pushed and quickly replaced may never be reviewed on its own. Expect coverage of where the branch settles, not a separate review per push.

**Draft pull requests are never reviewed**, under either policy. A draft is reviewed when it is first marked ready for review, and that is what triggers its first review.

## Choose the review behavior

Three separate things decide what a review does to a pull request. Keep them apart: two are Tessl settings on the repository, and the third is your own GitHub configuration, which is the only one that actually holds a merge.

**Review behavior** (**Enforcing** or **Advisory**) sets how the result is published. **Enforcing is the default.**

* **Enforcing.** Tessl publishes a verdict: it approves when nothing requires changes, and requests changes otherwise. The `Tessl Reviewer` check follows the verdict, passing on an approval and failing when changes are requested.
* **Advisory.** Tessl publishes its findings as a comment and never approves or requests changes. The check always passes.

**Auto-approve after resolved** is a separate switch, available only in Enforcing mode and off by default. When it is on, Tessl approves the pull request once its own blocking findings are resolved.

Neither setting blocks a merge on its own. A failing check or a requested-changes review only holds a pull request if your repository's **branch protection or ruleset** requires that check to pass, or requires the review's approval. Set that up in GitHub if you want a review to gate merging; leave it off and every review, Enforcing or Advisory, stays informational.

## Ask for a review by comment

Mention `@tessl-code-review` in a comment on a pull request to request a review. The comment can be on the Conversation tab or an inline comment on the diff.

Tessl reads the comment and classifies it into one of three intents. Who may trigger each one differs:

* **review**: another round on the current head. Phrases like "take another look", "review the fixes", or "check the latest commits" ask for this. It is the default: anything that is not clearly one of the other two is treated as a review. Anyone who can comment can ask for an ordinary review; it does not require write access.
* **full**: a fresh review of the whole change, including minor findings. Only a clear request for a complete, fresh, or thorough review of the entire pull request counts as full. A full review is honored only for the pull request's author or someone with write access to the repository; from anyone else it is treated as an ordinary review.
* **approve**: a clear instruction to approve the pull request. Honored only for the pull request's author or someone with write access. This is independent of the auto-approve switch: even with auto-approve off, an authorized commenter can still ask Tessl to approve.

The comment is treated as data, not as instructions, so its wording cannot change how it is classified beyond naming one of these intents.

## What gets published

A completed review publishes:

* **One native GitHub review** carrying a short judgment and the findings.
* **Findings anchored to changed lines** where GitHub allows it. A finding GitHub cannot anchor to a changed line is carried in the review body instead.
* **A check run** reporting the result on the reviewed head.

Reviews reconcile across runs. When a later review runs on the same pull request:

* a finding that has been fixed is marked resolved;
* a finding that is still present is not posted again;
* a finding that had been resolved but has returned is reopened.

If GitHub refuses the review submission, Tessl retries by publishing the findings as a plain pull request comment. That fallback is itself a write to GitHub and can also fail, for example if the installation has lost the write permission it needs. When both the review and the comment fail, the run ends as a failed publication with nothing posted on the pull request; check the `Tessl Reviewer` check run and the App's permissions if a review you expected never appears.

## Configure reviews with `.tessl-code-review.yml`

A repository can route lenses to parts of a change and set the review's finding policy with a `.tessl-code-review.yml` file at the repository root. The file is optional: without it, the App runs the built-in `standard` profile.

### How the App finds the file

The App resolves the file in two halves, and both matter:

* **Presence is decided on the default branch.** The App reads your repository's default branch and looks for `.tessl-code-review.yml` at the root. If the file is not on the default branch, the review runs the built-in `standard` profile, and nothing in the file matters, including a copy added in the pull request under review.
* **Content is read from the pull request head.** Once the file exists on the default branch, the App re-reads it at the pull request's head commit and uses that content. This is why a change to the file takes effect on the pull request that makes it, once the file already exists on the default branch.

A repository therefore opts in by merging the file to its default branch. After that, edits to the file are reviewed with the edited content on the pull request that changes them.

The two halves have to agree. A pull request that **deletes** the file from a repository that has it on the default branch leaves the App with a profile it is required to read and cannot: the review does not fall back to `standard`, and no review is published on that pull request. Remove the file in a pull request that you merge on its own, and expect the pull request doing the removal to go unreviewed.

{% hint style="warning" %}
Because the App reads the file's content from the pull request head, a contributor who can push to a pull request can change the effective review policy in the same change. Protecting only `.tessl-code-review.yml` is not enough: a lens that points at a repository-local skill (like the local lenses in the worked example below) is read from the pull request head too, so an author who cannot touch the profile can still weaken the review by editing the skill it references.

That is the whole skill, not just its entrypoint. A skill's `SKILL.md` can pull in files beside it, such as `references/`, `scripts/` and `assets/`, and those instructions carry the same weight as the ones in the entrypoint, so protecting `SKILL.md` alone leaves the rest of the bundle open.

To require separate approval for review-policy changes, protect **every repository-local source the profile trusts** with `CODEOWNERS`, not just the profile file: the `.tessl-code-review.yml` file, and the whole directory of each local lens it references rather than that lens's `SKILL.md` on its own. Then enable required code-owner approval in your branch protection or ruleset. A lens pinned to a registry reference is fixed by its version and needs no such protection. See the [`.tessl-code-review.yml` reference](/reference/configuration#tessl-code-review-yml).
{% endhint %}

### A worked example

```yaml
schemaVersion: 1
effort: adaptive
reviewMode: standard
requestChangesAt: major
ignore:
  - '**/*.generated.ts'
  - vendor/**
lenses:
  - ref: ./review-lenses/backend/SKILL.md
    effort: high
    globs:
      - apps/backend/**
      - '!apps/backend/**/*.generated.ts'
  - ref: tessl/code-review@0.2.0#review-security-and-privacy
    globs:
      - infra/**
  - ref: ./review-lenses/general/SKILL.md
```

`schemaVersion: 1` and a non-empty, ordered `lenses` list are required. Everything else is optional. The keys, in brief:

* `lenses[].ref`: a local `SKILL.md` path, an installed skill name, or a pinned registry reference such as `tessl/code-review@0.2.0#review-security-and-privacy`. Pin every registry reference so the review does not change when a plugin publishes a new version.
* `lenses[].globs`: repository-relative POSIX patterns selecting the files the lens sees. Patterns are evaluated in order; a `!` prefix excludes a previous match. A lens without `globs` applies to the whole change.
* `lenses[].effort`: a fixed `low`, `medium`, or `high` for that one lens.
* `effort`: how hard every lens thinks: `low`, `medium`, `high`, or `adaptive`. `adaptive` uses `medium` on a full review and `low` on a re-review.
* `ignore`: up to 64 positive patterns excluded from every lens. Each pattern excludes what it matches, so a leading `!` is rejected. `ignore` overrides lens selection.
* `reviewMode`: `strict`, `standard` (the default), or `relaxed`, the blocking strength an eligible finding needs.
* `requestChangesAt`: `critical`, `major` (the default), `minor`, or `nit`, the minimum consequence eligible to block. Low-confidence fresh findings never block, and pre-existing findings never block.

At most eight lenses run. A lens whose globs match no changed file does not run and does not count toward that limit. If no lens matches the change, the review is **skipped, which is not an approval**.

For the exhaustive key list and every edge case, see the [`.tessl-code-review.yml` reference](/reference/configuration#tessl-code-review-yml).

## Permissions the App holds

The Tessl Review GitHub App requests exactly these repository permissions:

| Permission    | Level | Why                                                                                                                  |
| ------------- | ----- | -------------------------------------------------------------------------------------------------------------------- |
| Metadata      | read  | Read basic repository information.                                                                                   |
| Pull requests | write | Publish the native review and its comments.                                                                          |
| Issues        | read  | GitHub serves a pull request's conversation comments through its issue endpoints, so reading them needs issues read. |
| Checks        | write | Report the review result as a check run.                                                                             |
| Contents      | write | Reconcile review threads across runs.                                                                                |

The App does not request Actions, Workflows, Statuses, Administration, or Members access. It reviews code; it does not take other actions in your repository.

Reviews run on Tessl's infrastructure. Each run uses a token minted for that run and scoped to the single repository under review, so nothing long-lived is stored in your repository.

## Troubleshooting

| Symptom                                                        | What to check                                                                                                                                                                                                                                                               |
| -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The App is installed, but no reviews run                       | Enabling is a separate step from installing. Enable the repository in the Tessl web app.                                                                                                                                                                                    |
| A repository cannot be enabled, or shows a permissions request | The installation may be waiting for an organization owner to approve the App's requested permissions. Ask an owner to approve the request on GitHub.                                                                                                                        |
| A review was published as a comment instead of a native review | GitHub refused the review submission, so Tessl published the findings as a plain comment instead.                                                                                                                                                                           |
| A review you expected never appeared                           | Both the native review and the comment fallback can fail, for example when the installation has lost write access. Check the `Tessl Reviewer` check run and the App's permissions, then request the review again.                                                           |
| A pull request was not reviewed                                | Draft pull requests are never reviewed; mark it ready for review. A run is also skipped when no lens matches the change, which is not an approval.                                                                                                                          |
| A mention did nothing                                          | Confirm the comment names `@tessl-code-review` in its text, rather than quoting it or wrapping it in backticks. Asking for a full review or an approval also needs write access to the repository, unless you opened the pull request; an ordinary review request does not. |

## Run Code Review in your own CI

For GitLab, add the CLI directly to a pipeline you own. See [Run Code Review with GitLab](/tutorials/setting-up-agentic-code-review/code-review-in-gitlab) for credentials, a job example, and publication behavior.

For GitHub, if you cannot install a GitHub App, an unsupported GitHub Action can run Tessl Code Review from a workflow you own. It is not maintained and may fall behind the CLI. See [Running Code Review in your own CI](/tutorials/setting-up-agentic-code-review/code-review-action).

## Related capabilities

The [`tessl code review`](/reference/cli-commands#tessl-code-review) CLI command runs a review locally and prints the result, and its `--publish` flag can post that review to the pull request or merge request it reviewed. Use it to try a review on a diff you already understand. The GitHub App remains the supported way to review pull requests automatically.

To turn recurring review feedback into skills, rules, tests, or verifiers, continue with [Improving agent code quality](/tutorials/improving-agent-code-quality).


# Run Code Review with GitLab

Review GitLab merge requests locally or integrate the Tessl CLI into your own GitLab CI pipeline.

GitLab support is part of the Tessl CLI, available in version 0.113.0 and later. It isn't a GitLab app or a managed integration. You run `tessl code review` locally or add it to a GitLab CI pipeline you own. You configure the runner, credentials, and review cadence.

## Review locally

Install the [Tessl CLI](/introduction-to-tessl/set-up-tessl/installation), sign in with `tessl login`, and authenticate GitLab with `glab`:

```sh
$ glab auth login --hostname gitlab.com
```

From a checkout of the repository, review a merge request by URL:

```sh
$ tessl code review --pr https://gitlab.com/engteam/my-project/-/merge_requests/42
```

The CLI collects the title, description, commits, notes, inline discussions, replies, and discussion resolution state. It reviews the merge request's source commit, including when a CI checkout contains a merged result.

In a GitLab checkout, you can also use its merge request number:

```sh
$ tessl code review --pr 42
```

`--pr` selects both GitHub pull requests and GitLab merge requests. An explicit URL selects the provider and project. If your local fetch or push remotes include both GitHub and GitLab, use a full URL instead of a number. In GitLab merge request pipelines, `GITLAB_CI=true` and `CI_MERGE_REQUEST_PROJECT_PATH` select GitLab even with mixed remotes. Outside those pipelines, use a full URL when remotes are mixed.

For self-managed GitLab, use the full URL for your host, or set `GITLAB_HOST` to the checkout's host when using a number. Authenticate `glab` against that same host. Explicit Git ranges with `--base` and `--head` remain available without GitLab access.

## Publish findings and review again

By default, the review prints its result without publishing. Add `--publish comment` to post a summary and inline discussions:

```sh
$ tessl code review --pr 42 --publish comment
```

Findings GitLab can't anchor to the diff stay in the summary. Answer a finding in its discussion: addressed, refuted, or declined. Later reviews read the replies, reconcile earlier findings, and resolve or reopen discussions where appropriate. Rerunning against the same source commit and target diff reuses existing finding threads. Replies are read when you run the CLI again; comments alone don't trigger another review.

Publication checks the source and target revisions before writes. If either changes during the review, run a fresh review against the current merge request. When a review is asked to consult linked documents, its summary includes a collapsed record of which sources were fetched and which weren't.

`--publish verdict` currently falls back to a comment with an explicit notice. It doesn't submit native GitLab approvals or request-changes decisions, create a Tessl-owned status check, or configure merge gating. Your CI job decides whether the review result should fail the pipeline.

## Add a job to your GitLab CI pipeline

Configure these variables in **Settings > CI/CD > Variables** before adding the job:

| Variable       | Purpose                                                                                                                                          |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `TESSL_TOKEN`  | A [Tessl API key](/reference/cli-commands#tessl-api-key-create) for the workspace running the review.                                            |
| `GITLAB_TOKEN` | A GitLab access token that can read the merge request and publish discussions. Use the `api` scope for publication. `GL_TOKEN` is also accepted. |

A read-only review can use `read_api` instead of `api`. Fetching missing Git objects also requires repository read access through the checkout's Git credentials or the token. `CI_JOB_TOKEN` alone can't provide the full merge request context and discussion publication.

{% hint style="warning" %}
**This example is for trusted code only.** A merge request pipeline reads its CI configuration from the source branch. Starting a job manually and masking its output don't prevent that configuration from sending tokens elsewhere.

Store both tokens as **masked and protected** variables. Protect both the source and target branches, and restrict permission to push to them to people you trust with these credentials. Don't expose either token to fork pipelines or unprotected source branches. The job rules below select the intended pipelines; GitLab's protected-variable permissions enforce the credential boundary even if someone changes those rules.

For untrusted merge requests, use a separate review workflow whose CI configuration and runner setup come from a trusted branch. Supply the merge request URL as data. The example below doesn't implement that workflow.
{% endhint %}

This example requires GitLab 18.1 or later and the **Allow merge request pipelines to access protected variables and runners** setting. GitLab permits that access only when both branches are protected and belong to the same project, and the triggering user has push or merge access to the target branch. See [Control access to protected variables and runners](https://docs.gitlab.com/ci/pipelines/merge_request_pipelines/#control-access-to-protected-variables-and-runners).

Add this job to `.gitlab-ci.yml`, and add the `review` stage to your existing `stages` list:

```yaml
stages:
  - review

tessl_code_review:
  image: node:22
  stage: review
  variables:
    GIT_DEPTH: '0'
  rules:
    - if: >-
        $CI_PIPELINE_SOURCE == "merge_request_event" &&
        $CI_MERGE_REQUEST_SOURCE_BRANCH_PROTECTED == "true" &&
        $CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED == "true" &&
        $CI_MERGE_REQUEST_SOURCE_PROJECT_ID == $CI_MERGE_REQUEST_TARGET_PROJECT_ID
      when: manual
      allow_failure: true
  before_script:
    - curl -fsSL --connect-timeout 10 --max-time 120 https://install.tessl.io/scripts/0.113.0.sh -o /tmp/install-tessl.sh
    - timeout --kill-after=10s 120s sh /tmp/install-tessl.sh
    - export PATH="$HOME/.local/bin:$PATH"
  script:
    - tessl code review --publish comment --json > tessl-code-review.json
  artifacts:
    when: always
    paths:
      - tessl-code-review.json
```

This example makes the job manual and optional for protected source and target branches in the same project, so it doesn't hold a merge while waiting for a run. Start it from GitLab when you want a review. Remove `when: manual` to run it on every matching trusted pipeline. Keep the protected-branch and same-project restrictions. Your GitLab configuration must allow [merge request pipelines](https://docs.gitlab.com/ci/pipelines/merge_request_pipelines/).

No `--pr` is needed in a GitLab merge request pipeline: the CLI selects the MR from GitLab's predefined metadata. An explicit URL remains useful for jobs outside an MR pipeline. The job calls the CLI directly and doesn't use the GitHub Action or install a GitLab app.

The installer-script download has a 10-second connection limit and a 120-second transfer limit. The installer, including its own binary downloads, has a 120-second limit with a 10-second termination grace period. An installation failure can occur before the review JSON artifact exists.

`GIT_DEPTH: '0'` supplies complete history for merge-base calculation. In shallow checkouts, the CLI retrieves the selected ancestry without a depth limit; fetching has a 60-second timeout.

A completed review with findings can still exit successfully. This example publishes comments without gating on findings. If you want a gate, set `allow_failure: false`, inspect the JSON `status` first, then `outcome.approved` and `outcome.findings[].requiresChanges`, and configure the job's failure behavior yourself. A skipped review isn't an approval. A publication failure exits nonzero and preserves the completed outcome alongside its failure details.

To configure lenses and finding policy, pass `--profile ./.tessl-code-review.yml` in the job. The CLI doesn't discover that file automatically. See the [Code Review configuration reference](/reference/configuration#tessl-code-review-yml) and the [CLI reference](/reference/cli-commands#tessl-code-review).


# Running Code Review in your own GitHub CI

Unsupported. Inputs and outputs of the tesslio/code-review-action GitHub Action, kept for people who cannot install the Tessl Review GitHub App.

For GitLab merge requests, call the Tessl CLI directly from your own pipeline. See [Run Code Review with GitLab](/tutorials/setting-up-agentic-code-review/code-review-in-gitlab).

{% hint style="warning" %}
The GitHub App is the supported way to run Tessl Code Review. This page is kept for people who cannot install a GitHub App. It is unsupported and not maintained, and it may fall behind the CLI.
{% endhint %}

The [`tesslio/code-review-action`](https://github.com/tesslio/code-review-action) GitHub Action runs Tessl Code Review on a pull request and publishes one native GitHub review. This page is the input and output reference. To set up reviews the supported way, see [Set up Tessl Code Review](/tutorials/setting-up-agentic-code-review).

The Action owns pull request resolution, checkout of the exact head, Tessl CLI setup, its own check run, and result artifacts. The Tessl CLI the Action installs runs the review and publishes it. The caller workflow owns when reviews run, the permissions the job holds, the token secrets, and whether findings block merging.

## Advisory versus gate

One input decides the consequence of a review, and it appears throughout this reference: `mode`. In `advisory` mode the review is published as a GitHub `COMMENT` and no review outcome fails the `Tessl Code Review` check. In `gate` mode the review publishes the verdict it reached (approving when nothing requires changes, requesting changes otherwise) and the check follows it, so branch protection can hold the pull request on a blocking finding. A technical failure to run or publish fails the job in both modes. In gate mode, `requestChangesAt` sets the consequence baseline, `reviewMode` sets the strength required to block, `mode` decides whether the check then fails, and branch protection decides whether a failing check holds the pull request.

## Inputs

| Input                  | Required | Default               | Purpose                                                                                                                                                                                                                         |
| ---------------------- | -------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tessl-token`          | yes      | none                  | Tessl API token the CLI uses. Pass it from a repository secret; never inline it.                                                                                                                                                |
| `mode`                 | no       | `advisory`            | Publication policy: `advisory` or `gate`. See [Advisory versus gate](#advisory-versus-gate).                                                                                                                                    |
| `profile`              | no       | `standard`            | Named review profile, or a repository-relative path to a `.yml`/`.yaml` profile. See [Configuration files](/reference/configuration#tessl-code-review-yml).                                                                     |
| `lenses`               | no       | `''`                  | JSON array with the complete ordered lens selection. Replaces the profile's default lenses rather than adding to them. At most eight lenses.                                                                                    |
| `effort`               | no       | `''`                  | Reasoning effort for every lens: `low`, `medium`, `high`, or `adaptive`. Overrides any profile effort. Empty lets the profile and CLI resolve it.                                                                               |
| `cli-version`          | no       | `latest`              | Tessl CLI version to install. `latest` tracks the current release. Pin an exact version to fix review behavior alongside the Action reference.                                                                                  |
| `github-token`         | no       | `${{ github.token }}` | The identity that authors the published review. See [`github-token`](#github-token).                                                                                                                                            |
| `allowed-associations` | no       | `''`                  | Comma-separated author associations whose comments may request a review, for example `OWNER,MEMBER,COLLABORATOR`. Empty accepts any commenter. Applies to comment events only, and never refuses the pull request's own author. |
| `approver-logins`      | no       | `''`                  | Comment-author logins, separated by commas or newlines, permitted to ask the review to approve instead of reviewing. See [`approver-logins`](#approver-logins).                                                                 |
| `pr-number`            | no       | `''`                  | Pull-request number for events that carry no pull-request context, such as a comment or a manual dispatch. Empty on `pull_request` events, where the Action resolves the pull request itself.                                   |

### `github-token`

Left at its default, the review is authored by the workflow's own token, which posts as `github-actions[bot]`, the same identity as the repository's other jobs. Pass a token that carries its own identity to author the review distinctly:

* a GitHub App installation token minted per run with [`actions/create-github-app-token`](https://github.com/actions/create-github-app-token), so reviews arrive as the App's `[bot]` login; or
* a dedicated machine user's personal access token.

A supplied token needs `pull-requests: write` to publish. `contents: write` is optional and buys exactly one thing: reopening a resolved thread whose finding still requires changes. GitHub files thread resolve and unresolve under Contents, so without write there the reviewer republishes the finding as a new comment. Whether an App's approving review satisfies a required-approving-review rule is decided by the App installation's write access, not by the token's scopes. Mint the token in a step before the Action, for example with [`actions/create-github-app-token`](https://github.com/actions/create-github-app-token), and pass its output as `github-token`.

### `approver-logins`

A comment can instruct Tessl to approve a pull request rather than review it, which skips the review. The same allowlist also permits requesting a full re-review of the whole change instead of the narrower incremental round, so a login named here gains both privileged intents. Repository owners, members, and collaborators may already do both, by the association `allowed-associations` reads. A GitHub App cannot: it comments with association `NONE` whatever its permissions, so no association names one. List a login here, carrying the `[bot]` suffix the event payload spells (capitalization does not matter), to permit that specific commenter. Naming a login here also admits it past `allowed-associations` for an ordinary review, so read the two inputs together. Empty names no one and does not stop a member using either.

## Outputs

A later step in the same job can read these once the Action step has an `id`. Give the `uses:` step `id: review`, then reference `steps.review.outputs.<name>`.

| Output            | Meaning                                                          |
| ----------------- | ---------------------------------------------------------------- |
| `status`          | Terminal Action status.                                          |
| `head-sha`        | Exact reviewed pull-request head SHA.                            |
| `review-id`       | Created or reused GitHub review ID, when available.              |
| `result-path`     | Absolute path to the structured CLI result on the runner.        |
| `result-artifact` | Name of the retained public result artifact on the workflow run. |

Use the outputs for reporting and workflow coordination. Do not add a second review publisher: publication, retry reconciliation, and duplicate prevention belong to the Tessl CLI the Action runs, and a second publisher gives the repository two paths that can disagree.


# Improving agent code quality

Turn recurring agent mistakes into fixes. Mine PRs and session logs with the Tessl Agent, then prove the fix with evals.

{% hint style="info" %}
**Takeaways:** After reading this page you will know how to identify concrete quality problems in agent-written code using the `find-optimizations` skill, how to turn those findings into actionable tickets, how to fix the issues, and how to set up a weekly automated scan so the process runs continuously — all driven through the `tessl agent`.
{% endhint %}

This tutorial starts from a concrete symptom: agents are getting something wrong repeatedly. It covers finding what the pattern is, filing tickets, fixing the root cause, and automating the scan so it runs on its own.

## The core workflow

1. Identify the problem area (e.g. "agents are getting the frontend layout wrong")
2. Run `find-optimizations` to extract patterns from PR review history
3. File tickets for the findings
4. Fix the issues by improving skills, rules, or verifiers
5. Set up a scheduled scan to catch new patterns before they accumulate

## Prerequisites

* Tessl installed and initialized (`tessl init`)
* Authenticated (`tessl login`)
* Access to a workspace with at least one repository connected

## Step 1: Find optimization opportunities

The `find-optimizations` skill reads PR review history and CI run results to extract patterns where agents needed corrections, then proposes improvements that would prevent those corrections in the future.

The `tessl agent` ships with the `tessl/harness-engineering` plugin pre-bundled, so `find-optimizations` is available without installing anything.

{% hint style="info" %}
Prefer your own coding agent? Install the plugin with `tessl install tessl/harness-engineering` and the skill becomes available there too. The rest of this tutorial assumes the `tessl agent`.
{% endhint %}

Give the agent a description of the mistake you are seeing plus a specific PR or time range to look at:

> Agents keep breaking our frontend layout. Look over PRs from the last 2 weeks and tell me the pattern.

The skill collects PR review comments, review summaries, CI run results, and the diffs of commits that addressed them. It categorizes each piece of feedback into an improvement type and proposes a concrete next step.

A typical output looks like this:

```
1. Type: rule
   Summary: Agents are not checking for null session before accessing user properties
   Evidence: PRs #1189, #1201, #1218 — reviewer corrected undefined access three times
   Suggested approach: Add a rule in plugins/frontend/rules/ that instructs agents to
   always check session?.user before accessing nested properties

...
```

The skill does not make any changes. It only produces output for you to review.

## Step 2: File tickets for the findings

Once you have the numbered list of findings, create tickets for the ones worth fixing. The `tessl agent` can create them directly. Refer back to the findings by number:

> Create a ticket from item 2.

For each ticket, the agent includes the evidence (PR numbers), the proposed improvement type, and the suggested approach. This gives whoever implements the fix enough context to act without re-running the analysis.

The `tessl agent` builds up memory about how you like to create and assign tickets — which issue tracker you use, how you format titles, who things get assigned to. It records this under `.tessl/memory/preferences/` (run `/setup-memory` if it has not been initialized yet), so after the first ticket you rarely have to repeat yourself. The next time you say "file a ticket from item 1," it already knows where it goes.

## Step 3: Fix the issues

Each ticket identifies the improvement type. Ask the `tessl agent` to implement the fix — it uses its `plugin-creator` skill to decide where the change belongs and to scaffold and validate it:

> Implement the fix from item 1 — a rule that makes agents check `session?.user` before accessing nested properties.

The approach the agent takes depends on the type:

**Rule** — an always-on instruction that every agent reads on every task. Rules are the right tool for conventions and constraints that apply without exception.

**Skill** — a procedural workflow the agent activates for specific tasks. Skills are the right tool when the fix involves a multi-step process (e.g. "when creating a route, first check the registry, then create the route, then register it").

**Verifier:** an LLM-as-judge check on committed files. Verifiers are the right tool when the fix is a binary, observable invariant about file structure (e.g. "component files must export at least one element with data-testid"). See [Verifiers overview](/codifying-and-enforcing-your-skill-standards/verifiers-overview) to compare verifiers with rules, skills, and linters, then [Verify agent changes in CI](/codifying-and-enforcing-your-skill-standards/verify-agent-changes-in-ci) to set up the check.

**Refactor** — structural code changes that make the right pattern easier to follow, paired with a rule or verifier to enforce the resulting convention going forward.

## Step 4: Verify the improvement

After implementing a fix, confirm it helps. Use evals to measure the impact of your context changes on agent task completion. Ask the `tessl agent` to set this up — it generates scenarios from commits in the problem area and runs the eval with and without the new context:

> Generate eval scenarios from PRs #1189, #1201, and #1218, then run the eval with and without my new frontend-skills plugin and compare the scores.

Under the hood the agent runs `tessl scenario generate` to build scenarios from those commits, then `tessl eval run` twice — once as a baseline and once with `--context` pointed at the new plugin. If the with-context variant scores higher on the criteria that correspond to the problem area, the fix is working.

To gate skill quality in CI so regressions are caught automatically, ask the agent to wire `tessl review run --json --threshold 80` into a check — it exits non-zero if the skill quality score drops below 80%.

## Step 5: Automate the weekly scan

Once the manual process is familiar, automate it so the scan runs without prompting. Ask the `tessl agent` to set up the schedule — it uses its `workflow-automator` skill to build the CI workflow and pick sensible defaults:

> Set up a weekly job to find future optimizations.

The agent runs `find-optimizations` headlessly with `tessl launch skill find-optimizations --agent tessl-agent`, passing a short static `--instructions` string (any dynamic content like date ranges is handled by the agent reading repository files). The defaults it chooses, and how you might change them:

* **A weekly schedule plus manual dispatch** — a Monday-morning cron with a `workflow_dispatch` trigger so you can also run it on demand. Adjust the cadence to match how fast your PR volume grows.
* **Findings written to an artifact** — the scan output is uploaded so the team can review it without re-running the analysis. Point it at a file, an issue, or a Slack message depending on where your team looks.
* **`TESSL_TOKEN` and `ANTHROPIC_API_KEY` from repository secrets** — the agent confirms these exist and walks you through creating any that are missing.

Review the weekly output. When findings recur across multiple weeks, escalate them to tickets and implementation work. This creates a continuous feedback loop: agents write code, the scan surfaces patterns, you improve the skills and rules, agents get better.

## Next steps

* Catch issues at pull request time before they accumulate into patterns by [setting up Tessl Code Review](/tutorials/setting-up-agentic-code-review). Add `tessl change verify` separately when a recurring problem can be expressed as a targeted, observable invariant.
* Discover other manual workflows in your repository that can be automated with the [Automating repetitive tasks](/tutorials/automating-repetitive-tasks) tutorial and the `find-automations` skill.


# Automating repetitive tasks

Identify repetitive work in your development loop and turn it into repeatable, automated workflows with the Tessl Agent.

{% hint style="info" %}
Looking for the easiest way to automate recurring work? See [Schedules](/automations/overview/overview), no CI configuration needed.
{% endhint %}

{% hint style="info" %}
**Takeaways:** After reading this page you will know how to use the `find-automations` skill to identify manual workflows in your repository that should be automated, how to turn findings into implementation tickets, and how to have the `tessl agent` build a CI workflow that runs an agent on a schedule to handle recurring work.
{% endhint %}

Every repository accumulates manual chores: the same kind of dependency update PR every week, a maintainer who manually applies the same changelog format to every release, a reviewer who leaves the same comment about a missing label. These are automation candidates. The `find-automations` skill reads your PR history to surface them.

## The core workflow

1. Run `find-automations` to identify manual workflows in your repository
2. Review the numbered list of automation candidates
3. File tickets for the ones worth building
4. Have the `tessl agent` build the automation as a CI workflow
5. Run `find-automations` on a schedule to catch new candidates as they emerge

## Prerequisites

* Tessl installed and initialized (`tessl init`)
* Authenticated (`tessl login`)
* `gh` CLI installed and authenticated (for reading PR data)
* Access to a workspace

## Step 1: Find automation opportunities

The `find-automations` skill reads recent PR history to identify manual workflows, recurring chores, and coordination patterns that could be automated.

The `tessl agent` ships with the `tessl/harness-engineering` plugin pre-bundled, so `find-automations` is available without installing anything.

{% hint style="info" %}
Prefer your own coding agent? Install the plugin with `tessl install tessl/harness-engineering` and the skill becomes available there too. The rest of this tutorial assumes the `tessl agent`.
{% endhint %}

Ask the agent to run it:

> Run find-automations on this repo and show me the candidates.

The skill inspects the last 90 days of PR history. For each candidate it finds, it produces a structured output entry. A typical output looks like this:

```
Summary: Found 4 clear automation candidates across 3 months of PR history.

1. Title: Automate weekly dependency updates
   Summary: The same contributor opens a dependency update PR every Monday morning.
   Pattern Type: established
   Why Automate: Predictable, low-judgment, repeating chore.
   Evidence: PRs #1180, #1190, #1202, #1215, #1223 — all titled "chore: update deps",
             all opened by @alice between 9am and 10am UTC on Mondays.
   Suggested Execution Mode: scheduled
   Suggested Trigger: Weekly on Monday morning
   Manual Trigger: none
   Implementation Direction: Run npm outdated, apply compatible updates, run tests,
                             open a PR if anything changed. Skip major bumps.

...
```

The skill is read-only. It does not modify any files, create commits, or open issues.

## Step 2: File tickets for the candidates

Review the numbered list and pick the candidates worth building. The `tessl agent` can create the tickets directly — refer to findings by number:

> Create a ticket from item 1.

For each ticket, the agent includes the evidence, the suggested execution mode, and the implementation direction from the finding. This gives the implementer enough context to act without re-running the analysis.

The `tessl agent` builds up memory about how you like to create and assign tickets — which issue tracker you use, how you format titles, who things get assigned to. It records this under `.tessl/memory/preferences/` (run `/setup-memory` if it has not been initialized yet), so after the first ticket you rarely have to repeat yourself.

## Step 3: Build the automation

Each finding includes an implementation direction. Ask the `tessl agent` to build it — it uses its `workflow-automator` skill to classify the task, create any plugin it needs, set up CI secrets, and write the workflow. It runs the underlying commands and reports them back; you stay in control of the design decisions.

### Recurring chores

For purely mechanical tasks — applying a label, posting a comment, running a script — no agent is needed at runtime. Ask the `tessl agent` to set up a standard GitHub Actions workflow:

> Build the stale-PR automation from item 3 as a scheduled GitHub Actions workflow.

The agent wires a plain Action (for example, `actions/stale`) on a daily schedule. The settings it chooses — the stale label, the inactivity window, the message — come straight from the finding's implementation direction, and you can adjust any of them.

### Agent-driven tasks

For tasks that require judgment — writing changelog entries, reviewing code, updating documentation — the automation runs an agent headlessly with `tessl launch skill`.

`tessl launch skill <skill> --agent <agent>` is the entrypoint for agent workflows: it runs an installed plugin skill through an agent in a CI environment, with per-run inputs passed via a short `--instructions` string. The `--agent` flag selects the backend.

The pattern is:

1. Create a plugin that encodes what the agent should do
2. Run the plugin with `tessl launch skill` in a CI workflow

Ask the `tessl agent` to do both:

> Build the release-notes automation from item 2: create a plugin that generates a formatted changelog block, then run it from a GitHub Actions workflow on release-branch pushes.

As it works, the agent runs the relevant CLI commands and names what it creates:

* `tessl plugin new` — scaffolds the plugin and skill ([reference](/reference/cli-commands#tessl-plugin-new))
* `tessl plugin lint` and `tessl review` — validate the plugin and skill ([reference](/reference/cli-commands#tessl-plugin-lint))
* `tessl plugin publish` — publishes it so CI can install it ([reference](/reference/cli-commands#tessl-plugin-publish))
* `tessl install` — installs the plugin in the workflow ([reference](/reference/cli-commands#tessl-install))
* `tessl launch skill <skill> --agent tessl-agent --yolo` — runs the skill headlessly in CI

The defaults the agent chooses for the workflow, and how you might change them:

* **`--agent tessl-agent`** — the workflow runs the Tessl built-in agent. The `tessl agent` is particularly good at harness and repo-maintenance tasks like these. For automations that build product features, you may prefer a coding agent backend (`claude-code`, `codex`, `cursor`); pass it to `--agent` instead.
* **`--yolo`** — lets the agent apply edits without prompting, which a headless CI run requires.

## Step 4: Run find-automations on a schedule

As your repository grows, new patterns emerge. Set up a weekly scan so new automation candidates surface before they become entrenched manual chores. Ask the `tessl agent` to schedule it:

> Set up a weekly GitHub Actions job that runs find-automations over the last 7 days and saves the candidate list where the team can review it.

The agent runs `find-automations` headlessly with `tessl launch skill find-automations --agent tessl-agent` on a weekly cron plus `workflow_dispatch`, and uploads the findings as an artifact for the team to review. Adjust the cadence and the output destination to fit your team.

Review the weekly artifact. When candidates appear across multiple weeks, escalate them to tickets. This prevents the backlog of manual chores from growing quietly.

## Next steps

* Use [Improving agent code quality](/tutorials/improving-agent-code-quality) and the `find-optimizations` skill to find recurring patterns in PR review feedback that suggest improvements to skills and rules, not just workflows.
* Use [Tessl Code Review](/tutorials/setting-up-agentic-code-review) for pull request findings, and add `tessl change verify` separately to enforce structural invariants automatically.
* Combine both scans into a single scheduled workflow that runs both skills and aggregates their findings.


# Automations overview

Automate recurring work by declaring schedules in your tessl.json and pushing them to your default branch.

Automations run plugins on a schedule so recurring work happens without CI or GitHub Actions configuration.

## Define an automation in tessl.json

The primary way to create an automation is to declare a schedule in your project's `tessl.json`. Add a `schedules` block and push it to your default branch. Tessl syncs the manifest and creates the schedule when it can resolve the project and apply the entry. The schedule lives next to the code it runs against, so it is reviewed and version-controlled like any other change.

See [Schedules](/automations/overview/overview) for prerequisites, the `schedules` block reference, validation, and schedule behavior.

## Example schedules

{% content-ref url="/pages/jqRZh2ForMPs2Jpj4zox" %}
[Example Schedules](/automations/overview/overview/example-schedules)
{% endcontent-ref %}


# Schedules

No CI setup, no GitHub Actions config. Declare a schedule in your tessl.json, push to your default branch, and Tessl runs it for you.

{% hint style="info" %}
**Takeaways:**

* A schedule runs a plugin against a repo on a cron, with no CI or GitHub Actions config needed.
* The primary way to create one is a `schedules` block in your `tessl.json`. Push to your default branch and Tessl syncs it.
* An unparseable or invalid file never deletes anything. Removing a schedule from a valid file is what deletes it.
* Run `tessl schedule validate` before you push to catch errors locally.
* When repo-defined schedules are enabled for your organization, they are read-only in the web app and CLI. Change the file to change them.
  {% endhint %}

## What is a schedule?

A schedule runs a plugin on a cron. You pick a plugin, a repo, and a time, and Tessl runs the plugin against that repo on that schedule. Each time the schedule fires, Tessl starts a cloud run and reports the result back so you can see what happened.

Tessl skips a tick if the previous run from the same schedule is still active when the next one would start. There is no queue: a skipped tick does not run later.

Think of it as two parts working together. The **plugin** describes the task: it tells the agent what to do. The **schedule** describes the timing: it tells Tessl when to do it.

## What you need first

* A Tessl account with a workspace.
* GitHub connected to your workspace. See [Connect Tessl to GitHub](/distribute/connect-tessl-to-github).
* A plugin that describes the task. See [Create a plugin](/creating-skills-and-plugins/create-a-plugin).
* An environment. A scheduled run cannot mint its own GitHub token, so it needs a `GITHUB_TOKEN` from its environment to check out your repo and open PRs. The environment also holds any other secrets the plugin needs. See [Environments](/automations/environments).

## Define a schedule in tessl.json

The primary way to create a schedule is to declare it in your project's `tessl.json`. Add a `schedules` block, push it to your default branch, and Tessl reads the file and creates the schedule for you. The schedule lives next to the code it runs against, so it is reviewed and version-controlled like any other change.

The `name` field helps Tessl resolve the manifest to a project. Use the `workspace/project` format, for example `"name": "my-org/my-app"`. Before schedules sync, an active project must exist in that workspace, be connected to the same GitHub repository, and use the manifest's directory as its repository subpath. The GitHub App installation must currently have access to the workspace. When Tessl resolves that binding, schedules from the manifest appear under the project's **Schedules** tab in the web app. Schedule sync does not create or reassign projects.

Tessl discovers and syncs each `tessl.json` in a repository independently. In a monorepo, a manifest such as `apps/backend/tessl.json` manages only its own schedules, without affecting the root or sibling manifests. Deleting that subdirectory manifest removes only the schedules managed by that manifest in workspaces that the GitHub App installation can still access. Schedules in workspaces it can no longer access are not reconciled by that deletion.

### Let the schedule-setup skill do it for you

If you'd rather describe the schedule in plain language than write the JSON yourself, install [tessl/schedule-setup](https://tessl.io/registry/tessl/schedule-setup).

When invoking the skill, tell your agent what you want to run and when ("run the docs audit every Monday at 9am London time using the production environment"). The skill asks clarifying questions one at a time, picks the most readable schedule format, writes and validates the entry, and explains what happens on push. It can also author a new repo-local skill for your schedule to use if you don't have one yet.

### The schedules block

`schedules` is a map of schedule key to schedule entry. The key is the schedule's stable identity: a lowercase kebab-case slug, not a display label. Renaming a key removes the old schedule from the file and adds the new one. Sync validates the complete change before deleting the old schedule and creating the new one. If the new entry fails validation, neither operation is applied.

Each entry needs `skill`, `environment`, and one timing field. Use either `cron` or its alias, `schedule`, but not both.

| Field          | Required   | What it is                                                                                                                                     |
| -------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `skill`        | Yes        | The plugin to run: a registry ref `workspace/plugin[@version][#skill]`, or a repo-local `file:path`.                                           |
| `cron`         | One of two | A five-field cron expression, supported shorthand, or structured schedule object. The minimum interval between runs is 5 minutes.              |
| `schedule`     | One of two | Alias for `cron`. Use one timing field, not both.                                                                                              |
| `environment`  | Yes        | The workspace environment the run executes in, referenced by **name**. It supplies the GitHub token and any other secrets the plugin needs.    |
| `timezone`     | No         | IANA timezone the cron is interpreted in (for example `America/New_York`). Defaults to `UTC`.                                                  |
| `baseBranch`   | No         | The branch each run checks out and opens PRs against. When omitted, each run uses the project's Loop default branch or the repository default. |
| `workdir`      | No         | A subdirectory of the repo the plugin runs in.                                                                                                 |
| `agent`        | No         | Which agent runs the plugin (`claude-code` or `claude` (default), `cursor`, `codex`, `gemini`, `tessl` or `tessl-agent`).                      |
| `model`        | No         | Which model the agent uses (e.g. `claude-sonnet-4-6` for `claude`, `auto` for `cursor`, `gpt-5.4` for `codex`).                                |
| `inputs`       | No         | Skill input values, as a map of string keys to string values.                                                                                  |
| `instructions` | No         | Free-text directions appended to the plugin prompt for this schedule.                                                                          |
| `description`  | No         | A human-readable description of the schedule.                                                                                                  |

Raw cron expressions have five fields: minute, hour, day of month, month, and day of week. `0 9 * * 5` means "at 9:00 on every Friday". `0 8 * * *` means "at 8:00 every day".

You can also use these shorthand strings:

* `hourly`
* `daily at HH:MM`
* `weekdays at HH:MM`
* `weekly on DAY at HH:MM`
* `monthly on Nth at HH:MM`
* `every N minutes` or `every N hours`, when `N` divides evenly into 60 or 24
* `every N minutes between HH:MM-HH:MM`; the start is inclusive and the end is exclusive, and multi-hour windows use whole-hour endpoints
* `every N minutes on weekdays`

For schedules that don't fit those patterns, use an object with `minute`, `hour`, `day`, `month`, and `weekday` fields. Omitted fields mean every value. Numeric fields accept a number, a list of numbers, `{ "every": N }`, or `{ "between": [N, M] }`. `month` and `weekday` also accept names or lists of names.

```json
{
  "cron": {
    "minute": { "every": 15 },
    "hour": { "between": [9, 17] },
    "weekday": ["mon", "tue", "wed", "thu", "fri"]
  }
}
```

Tessl compiles shorthand strings and objects to cron before validation and storage. The timezone and 5-minute minimum apply to every input form.

### A complete example

```json
{
  "name": "my-org/my-repo",
  "schedules": {
    "weekly-changelog": {
      "skill": "tessl/changelog-writer",
      "schedule": "weekly on friday at 09:00",
      "environment": "production",
      "timezone": "America/New_York",
      "description": "Draft the weekly changelog every Friday morning."
    },
    "api-docs-audit": {
      "skill": "tessl/docs-audit",
      "cron": "weekly on monday at 08:00",
      "environment": "production",
      "instructions": "Focus on the API reference section only."
    }
  }
}
```

This declares two schedules. `weekly-changelog` runs the `changelog-writer` plugin every Friday at 9:00 New York time. `api-docs-audit` runs the `docs-audit` plugin every Monday at 8:00 UTC, steered at the API reference by its `instructions`.

### Behaviors to know

Schedule identity is the key name. Renaming a key is therefore a delete and a create, not an update. For example, changing `nightly-scan` to `nightly-check` deletes the old schedule and its Temporal schedule, and creates a new schedule with the new key. Prior launch runs are preserved but are no longer linked to a schedule. If the new entry fails validation, neither the deletion nor the creation is applied.

The `schedules` block is the source of truth. Removing the block from `tessl.json`, or setting it to `{}`, means “no schedules” and deletes all schedules managed by that manifest. To recover from an accidental removal, revert the commit and push again.

### Validate before you push

Run `tessl schedule validate` before you push to catch errors locally. You can also [run the validator in your CI workflow](/automations/overview/overview/validate-in-ci):

```bash
tessl schedule validate
```

It reads your `tessl.json`, checks the `schedules` block, and reports every problem against the schedule key it belongs to. It exits non-zero when the block is invalid, so it also works as a CI check. A file with no `schedules` block is valid.

`validate` checks the file only: its shape, its cron expressions, and the minimum interval. It cannot check what your workspace decides when the file syncs, so a file that passes here can still be rejected at sync time. It does **not** check:

* whether the plugin reference resolves to a plugin your workspace can use
* whether the named environment exists in your workspace, or carries a GitHub token
* whether the agent and model are in your organization's catalogue
* the limit on active schedules per workspace

## How the sync works

When you push to your default branch, Tessl reads the `schedules` block and reconciles schedules across every workspace reachable through your GitHub App installation. For each workspace where the named environment resolves:

* A key in the file that has no schedule yet is **created**.
* A key whose entry changed is **updated**.
* A key that was removed from the file is **deleted**.

If an entry cannot be applied in a given workspace (for example, the named environment does not exist there, or the workspace has reached its active-schedule limit), that entry is skipped for that workspace. Other entries and other workspaces are still reconciled.

Tessl only reads the `schedules` block. Changes to other parts of `tessl.json` are ignored. Most pushes that do not touch `tessl.json` are also ignored. A force push, branch creation, or push with an empty or truncated commit list bypasses that optimization. Tessl reads the manifest at the pushed revision and attempts reconciliation; individual entries can still be skipped if they cannot be applied.

The sync runs off the default branch only. A schedule declared on a feature branch does not sync until the change reaches your default branch.

## The safety model

Tessl never deletes a schedule because it could not understand your file. Deletion only ever follows from a file it read cleanly.

* **Unparseable or invalid: nothing changes.** If the file is malformed JSON, cannot be read, or its `schedules` block fails validation, Tessl makes no changes at all. A typo in the block does not delete your schedules; it leaves them exactly as they are. Fix the file and push again.
* **Valid and read cleanly: the file wins.** Once Tessl reads a valid file, the file is the source of truth. Remove one key and that schedule is deleted. Remove the whole `schedules` block, or delete a `tessl.json`, and every repo-defined schedule managed by that manifest is deleted.

So the only way to delete a repo-defined schedule is to remove it from a valid file and push. An error in the file is always safe.

## Read-only in the web app and CLI

When repo-defined schedules are enabled for your organization, a schedule defined in `tessl.json` is read-only from the web app and the CLI. Attempts to edit, pause, resume, or delete it through those surfaces return an error telling you it is defined in `tessl.json`. To change it, change the file and push.

You can still watch a repo-defined schedule and its runs in the web app and CLI. Only mutations are blocked.

A repo-defined schedule reflects the file for entries that were successfully applied at sync time. A skipped new entry is absent until the next sync reconciles it. A skipped update leaves the existing schedule at its previous configuration, and it can continue running until a later sync applies the update.

## Create from the CLI or web app (alternative)

You can also create a schedule directly from the CLI or the web app, without a `tessl.json` block. This is the older path and is not version-controlled. It may be disabled for some users.

{% tabs %}
{% tab title="CLI" %}
Use `tessl schedule create`. Pass the plugin as the first argument, then the repo, the cron, and the environment:

```bash
tessl schedule create tessl/changelog-writer \
  --repo my-org/my-repo \
  --cron "0 9 * * 5" \
  --environment production
```

This runs the `changelog-writer` plugin against `my-org/my-repo` every Friday at 9:00 UTC.

Common flags:

| Flag             | What it does                                                                                                                                                                                          |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--repo`         | The repository the plugin runs against. Required.                                                                                                                                                     |
| `--cron`         | When the schedule fires, as a cron expression. Required.                                                                                                                                              |
| `--environment`  | The workspace environment the run executes in. It supplies the GitHub token and any other secrets or API keys the plugin needs. Required when you specify the plugin, repo, and environment directly. |
| `--timezone`     | The timezone for the cron expression. Optional; defaults to UTC.                                                                                                                                      |
| `--agent`        | Which agent runs the plugin (for example `claude-code`, `cursor`, `codex`, `gemini`, or `tessl-agent`). Optional.                                                                                     |
| `--model`        | Which model the agent uses. Optional.                                                                                                                                                                 |
| `--instructions` | Extra directions layered on top of the plugin for this schedule. Optional.                                                                                                                            |
| `--base-branch`  | The branch to check out and open PRs against. Optional.                                                                                                                                               |

The cron expression has five fields: minute, hour, day of month, month, and day of week. `0 9 * * 5` means "at 9:00 UTC on every Friday". `0 8 * * *` means "at 8:00 UTC every day".

Use `--instructions` to point a generic plugin at a specific job:

```bash
tessl schedule create tessl/docs-audit \
  --repo my-org/my-repo \
  --cron "0 8 * * *" \
  --environment production \
  --instructions "Focus on the API reference section only"
```

{% endtab %}

{% tab title="Web app" %}
Open **Projects > \[project] > Schedules** and choose **Create schedule**. The form asks for the schedule (cron), a name, a description, and an environment. These are the primary fields.

Below the environment and mode selection is an **Advanced options** control. Expand it to set the agent, model, inputs, instructions, and base branch. Leave it collapsed to accept the defaults.
{% endtab %}
{% endtabs %}

## How instructions work

The `instructions` field layers extra directions on top of whatever the plugin already says. The plugin is the base recipe; instructions are the per-schedule customization. The plugin still runs its normal task. The instructions steer it for this one schedule.

This lets you reuse one plugin for several jobs. The same `docs-audit` plugin, pointed two ways in one `schedules` block:

```json
{
  "schedules": {
    "audit-api-reference": {
      "skill": "tessl/docs-audit",
      "cron": "0 8 * * 1",
      "environment": "production",
      "instructions": "Focus on the API reference section only."
    },
    "audit-getting-started": {
      "skill": "tessl/docs-audit",
      "cron": "0 8 * * 3",
      "environment": "production",
      "instructions": "Focus on the getting-started guide and its code samples."
    }
  }
}
```

Same plugin, two schedules, two different jobs.

## Monitoring your runs

Each schedule keeps a table of its past runs. Open a schedule in the web app to see its full run history, including output and duration. From the CLI, `tessl schedule runs <id>` lists runs with their ID, status, skill, repo, workspace, creator, and creation time.

| Status       | What it means                                                | What to do                   |
| ------------ | ------------------------------------------------------------ | ---------------------------- |
| In Progress  | Still running                                                | Wait                         |
| Completed    | The skill finished and reported a result                     | Nothing. Check the output.   |
| Agent failed | The skill reported that it could not complete the task       | Check the output for why     |
| Error        | The platform failed, or the run ended without a skill result | Check the run's error detail |
| Cancelled    | Someone stopped the run                                      | Nothing                      |

## Next steps

* [Environments](/automations/environments)
* [Connect Tessl to GitHub](/distribute/connect-tessl-to-github)
* [Create a plugin](/creating-skills-and-plugins/create-a-plugin)
* [Keep docs in sync](/automations/overview/overview/example-schedules/use-case-docs-audit)
* [Weekly changelog](/automations/overview/overview/example-schedules/use-case-changelog)
* [Review pattern mining](/automations/overview/overview/example-schedules/use-case-review-miner)
* [Test health monitor](/automations/overview/overview/example-schedules/use-case-test-health)
* [Release readiness check](/automations/overview/overview/example-schedules/use-case-release-check)


# Validate schedules in CI

Add tessl schedule validation to your continuous integration workflow.

Add `tessl schedule validate` to your CI workflow to catch errors in the `schedules` block of `tessl.json` before a change is merged. This is a manual setup: add the step to your own CI configuration.

## What it checks

The command reads `tessl.json` and validates the file without connecting to a Tessl workspace. It checks:

* The shape of the `schedules` block and each schedule entry.
* Schedule field names, including required fields and the rule that each entry uses either `cron` or `schedule`, but not both.
* Field types, including structured schedule values and string-only input values.
* Cron syntax and supported human-readable or structured schedule forms.
* The minimum interval between runs, which is five minutes.

The command reports every problem it finds and exits with a non-zero status when the file is invalid. CI uses that status to fail the check.

## What it does not check

This command validates the file only. It does not check server-side resources or workspace limits, including:

* Whether the named environment exists or contains the required secrets.
* Whether the referenced skill or plugin is installed and available to the workspace.
* Whether the workspace has capacity for another active schedule.

Those checks happen when Tessl syncs the file for a project and workspace.

## GitHub Actions

The following workflow installs the Tessl CLI and runs the validator on pull requests that change `tessl.json`:

```yaml
name: Validate Tessl schedules

on:
  pull_request:
    paths:
      - tessl.json
      - .github/workflows/validate-tessl-schedules.yml

jobs:
  validate-schedules:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Tessl CLI
        uses: tesslio/setup-tessl@v2
      - name: Validate Tessl schedules
        run: tessl schedule validate
```

The validation step does not need a Tessl token because it checks the local file. The CLI's non-zero exit status fails the GitHub Actions job when a schedule is invalid.

## File checks and server checks

This check catches file-level issues before merge, such as a misspelled field, an invalid cron expression, or a schedule that runs too often. After a valid file is merged and synced, Tessl performs server-side checks. For example, a nonexistent environment can still cause a validation error on the project's **Schedules** page after sync.

For the full schedule format, see [Schedules](/automations/overview/overview).


# Example Schedules

The following are examples of schedules you can get started with easily.

{% content-ref url="/pages/JLlCkfiAtjCVwoVPJGTN" %}
[Keep docs in sync](/automations/overview/overview/example-schedules/use-case-docs-audit)
{% endcontent-ref %}

{% content-ref url="/pages/HWaXqt9Dn6xIrkqsbmHh" %}
[Weekly changelog](/automations/overview/overview/example-schedules/use-case-changelog)
{% endcontent-ref %}

{% content-ref url="/pages/EaQxQY9dCJ7W5ygVmXG5" %}
[Review pattern mining](/automations/overview/overview/example-schedules/use-case-review-miner)
{% endcontent-ref %}

{% content-ref url="/pages/T7JswwvbhpNItJlwIBtN" %}
[Test health monitor](/automations/overview/overview/example-schedules/use-case-test-health)
{% endcontent-ref %}

{% content-ref url="/pages/zXsGx4834tQhkrWfh6yg" %}
[Release readiness check](/automations/overview/overview/example-schedules/use-case-release-check)
{% endcontent-ref %}


# Keep docs in sync

Keep your docs in sync with your code, automatically. A scheduled docs audit checks whether the docs still match the code and opens a PR when they have drifted.

{% hint style="info" %}
**Goal:** Keep your docs in sync with your code, automatically.
{% endhint %}

## What it does

Every morning, a schedule checks whether the docs still match the code. If they have drifted and no audit PR is already open, it opens a PR with the fix. This catches the common kind of rot where someone changes an API but forgets to update the docs, so the docs quietly go stale.

## The plugin

[`tessl/docs-audit`](https://tessl.io/registry/tessl/docs-audit) reads the code and the docs, finds where they disagree, and opens a PR that reconciles them. Install it with:

```bash
$ tessl install tessl/docs-audit
```

## Define the schedule

Run it every day at 7:00 UTC. Add this entry to the `schedules` block in your project's `tessl.json`, and keep any other entries already in that block. Replace `production` with the name of your environment (see the [Schedules overview](/automations/overview/overview) for the prerequisites).

{% stepper %}
{% step %}
**Add the schedule entry**

```json
{
  "schedules": {
    "docs-audit": {
      "skill": "tessl/docs-audit",
      "schedule": "daily at 07:00",
      "environment": "production"
    }
  }
}
```

{% endstep %}

{% step %}
**Validate and push**

Validation always checks the local file. For an authenticated linked project that can reach Tessl, it also gives an advisory warning when current workspace capacity may not accommodate additional schedules. Commit and push the change, then confirm Tessl applied the schedule in your workspace before checking its run history.

```bash
$ tessl schedule validate
$ git add tessl.json
$ git commit -m "chore(docs): add docs audit schedule"
$ git push
```

{% endstep %}

{% step %}
**Check the runs**

Open the schedule in the web app, or run `tessl schedule runs <id>` to see its run history.
{% endstep %}
{% endstepper %}

## Customize with instructions

Scope the audit to the part of the docs that drifts most by adding `instructions` to the same schedule entry. Keep all other entries in the `schedules` block:

```json
{
  "schedules": {
    "docs-audit": {
      "skill": "tessl/docs-audit",
      "schedule": "daily at 07:00",
      "environment": "production",
      "instructions": "Only check the API reference against the route handlers in src/api"
    }
  }
}
```

## Create your own

Want a different behavior? [Create your own plugin](/creating-skills-and-plugins/create-a-plugin) that reads the code and the docs and reconciles them your way.


# Weekly changelog

Get a changelog draft every week without anyone remembering to write it. A scheduled changelog writer scans the week's merged PRs and opens a PR with the draft.

{% hint style="info" %}
**Goal:** Get a changelog draft every Friday without anyone remembering to write it.
{% endhint %}

## What it does

Every Friday, a schedule scans the pull requests merged since the last changelog entry (falling back to the last 7 days if no entry exists) and drafts the next one. The plugin reads the merged work and writes the entry in your changelog's style, then opens a PR with the draft. If a draft PR from a previous run is still open, it skips opening another one.

## The plugin

[`tessl/changelog-writer`](https://tessl.io/registry/tessl/changelog-writer) scans the PRs merged since the last entry, drafts the next one, and opens a PR with it. Install it with:

```bash
$ tessl install tessl/changelog-writer
```

## Define the schedule

Run it every Friday at 16:00 UTC. Add this entry to the `schedules` block in your project's `tessl.json`, and keep any other entries already in that block. Replace `production` with the name of your environment (see the [Schedules overview](/automations/overview/overview) for the prerequisites).

{% stepper %}
{% step %}
**Add the schedule entry**

```json
{
  "schedules": {
    "weekly-changelog": {
      "skill": "tessl/changelog-writer",
      "schedule": "weekly on friday at 16:00",
      "environment": "production"
    }
  }
}
```

{% endstep %}

{% step %}
**Validate and push**

Validation always checks the local file. For an authenticated linked project that can reach Tessl, it also gives an advisory warning when current workspace capacity may not accommodate additional schedules. Commit and push the change, then confirm Tessl applied the schedule in your workspace before checking its run history.

```bash
$ tessl schedule validate
$ git add tessl.json
$ git commit -m "chore(docs): add weekly changelog schedule"
$ git push
```

{% endstep %}

{% step %}
**Check the runs**

Open the schedule in the web app, or run `tessl schedule runs <id>` to see its run history.
{% endstep %}
{% endstepper %}

## Customize with instructions

Match your changelog's house style by adding `instructions` to the same schedule entry. Keep all other entries in the `schedules` block:

```json
{
  "schedules": {
    "weekly-changelog": {
      "skill": "tessl/changelog-writer",
      "schedule": "weekly on friday at 16:00",
      "environment": "production",
      "instructions": "Group entries under Added, Fixed, and Changed. Skip dependency bumps."
    }
  }
}
```

## Create your own

Want a different format? [Create your own plugin](/creating-skills-and-plugins/create-a-plugin) that reads the git log and writes an entry your way.


# Review pattern mining

Stop repeating the same code-review comment. A scheduled review miner reads recent reviews, finds the feedback that keeps coming up, and proposes a lint rule for it.

{% hint style="info" %}
**Goal:** Stop reviewers repeating the same comment by turning it into a rule.
{% endhint %}

## What it does

Weekly, a schedule reads the last 50 code reviews and looks for feedback that keeps coming up. If reviewers keep saying the same thing, the plugin proposes a lint rule, an automated check, or a documented convention to address it. Reviewers spend less time repeating the same comment and more time on the decisions that need their judgment.

## The plugin

[`tessl/review-miner`](https://tessl.io/registry/tessl/review-miner) reads recent review comments, groups the recurring ones, and proposes a rule, check, or convention for each. Install it with:

```bash
$ tessl install tessl/review-miner
```

## Define the schedule

Run it every Monday at 9:00 UTC. Add this entry to the `schedules` block in your project's `tessl.json`, and keep any other entries already in that block. Replace `production` with the name of your environment (see the [Schedules overview](/automations/overview/overview) for the prerequisites).

{% stepper %}
{% step %}
**Add the schedule entry**

```json
{
  "schedules": {
    "review-miner": {
      "skill": "tessl/review-miner",
      "schedule": "weekly on monday at 09:00",
      "environment": "production"
    }
  }
}
```

{% endstep %}

{% step %}
**Validate and push**

Validation always checks the local file. For an authenticated linked project that can reach Tessl, it also gives an advisory warning when current workspace capacity may not accommodate additional schedules. Commit and push the change, then confirm Tessl applied the schedule in your workspace before checking its run history.

```bash
$ tessl schedule validate
$ git add tessl.json
$ git commit -m "chore(docs): add review miner schedule"
$ git push
```

{% endstep %}

{% step %}
**Check the runs**

Open the schedule in the web app, or run `tessl schedule runs <id>` to see its run history.
{% endstep %}
{% endstepper %}

## Customize with instructions

Focus the search on one area by adding `instructions` to the same schedule entry. Keep all other entries in the `schedules` block:

```json
{
  "schedules": {
    "review-miner": {
      "skill": "tessl/review-miner",
      "schedule": "weekly on monday at 09:00",
      "environment": "production",
      "instructions": "Only look for repeated feedback about error handling and logging"
    }
  }
}
```

## Create your own

Want a different behavior? [Create your own plugin](/creating-skills-and-plugins/create-a-plugin) that reads recent review comments and proposes a rule your way.


# Test health monitor

Catch flaky tests before everyone learns to ignore them. A scheduled test health monitor finds flaky or repeatedly failing tests and files a ticket for each one.

{% hint style="info" %}
**Goal:** Catch flaky tests before everyone learns to re-run and ignore them.
{% endhint %}

## What it does

Daily, a schedule checks for flaky or repeatedly failing tests. It files a ticket for each test that either failed and then passed on a re-run with no code change (flaky), or failed more than once in the window with no code change to explain it. This catches test rot early, before a flaky test becomes something everyone just re-runs and ignores.

## The plugin

[`tessl/test-health`](https://tessl.io/registry/tessl/test-health) reads recent CI history, finds the unstable tests, and files a ticket for each one worth fixing. Install it with:

```bash
$ tessl install tessl/test-health
```

## Define the schedule

Run it every day at 6:00 UTC. Add this entry to the `schedules` block in your project's `tessl.json`, and keep any other entries already in that block. Replace `production` with the name of your environment (see the [Schedules overview](/automations/overview/overview) for the prerequisites).

{% stepper %}
{% step %}
**Add the schedule entry**

```json
{
  "schedules": {
    "test-health": {
      "skill": "tessl/test-health",
      "schedule": "daily at 06:00",
      "environment": "production"
    }
  }
}
```

{% endstep %}

{% step %}
**Validate and push**

Validation always checks the local file. For an authenticated linked project that can reach Tessl, it also gives an advisory warning when current workspace capacity may not accommodate additional schedules. Commit and push the change, then confirm Tessl applied the schedule in your workspace before checking its run history.

```bash
$ tessl schedule validate
$ git add tessl.json
$ git commit -m "chore(docs): add test health schedule"
$ git push
```

{% endstep %}

{% step %}
**Check the runs**

Open the schedule in the web app, or run `tessl schedule runs <id>` to see its run history.
{% endstep %}
{% endstepper %}

## Customize with instructions

Set the bar for what counts by adding `instructions` to the same schedule entry. Keep all other entries in the `schedules` block:

```json
{
  "schedules": {
    "test-health": {
      "skill": "tessl/test-health",
      "schedule": "daily at 06:00",
      "environment": "production",
      "instructions": "Only file a ticket if a test failed 3+ times this week with no related code change"
    }
  }
}
```

## Create your own

Want a different behavior? [Create your own plugin](/creating-skills-and-plugins/create-a-plugin) that reads test results over time and flags the unstable ones your way.


# Release readiness check

Find release problems while there is still time to fix them. A scheduled release check verifies migrations, feature flags, the changelog, and the docs before each release window.

{% hint style="info" %}
**Goal:** Find release problems early, while there is still time to fix them.
{% endhint %}

## What it does

Before each release window, a schedule verifies the checklist: migrations are clean, feature flags are set correctly, the changelog is drafted, and the docs are up to date. Running it ahead of the release means problems surface early, while there is still time to fix them.

## The plugin

[`tessl/release-check`](https://tessl.io/registry/tessl/release-check) walks your release checklist against the changes since the last release and reports what is missing. Install it with:

```bash
$ tessl install tessl/release-check
```

## Define the schedule

Run it every Thursday at 10:00 UTC, ahead of a Friday release. Add this entry to the `schedules` block in your project's `tessl.json`, and keep any other entries already in that block. Replace `production` with the name of your environment (see the [Schedules overview](/automations/overview/overview) for the prerequisites).

{% stepper %}
{% step %}
**Add the schedule entry**

```json
{
  "schedules": {
    "release-check": {
      "skill": "tessl/release-check",
      "schedule": "weekly on thursday at 10:00",
      "environment": "production"
    }
  }
}
```

{% endstep %}

{% step %}
**Validate and push**

Validation always checks the local file. For an authenticated linked project that can reach Tessl, it also gives an advisory warning when current workspace capacity may not accommodate additional schedules. Commit and push the change, then confirm Tessl applied the schedule in your workspace before checking its run history.

```bash
$ tessl schedule validate
$ git add tessl.json
$ git commit -m "chore(docs): add release check schedule"
$ git push
```

{% endstep %}

{% step %}
**Check the runs**

Open the schedule in the web app, or run `tessl schedule runs <id>` to see its run history.
{% endstep %}
{% endstepper %}

## Customize with instructions

Match your own checklist by adding `instructions` to the same schedule entry. Keep all other entries in the `schedules` block:

```json
{
  "schedules": {
    "release-check": {
      "skill": "tessl/release-check",
      "schedule": "weekly on thursday at 10:00",
      "environment": "production",
      "instructions": "Check that all migrations have a rollback and every new flag has a default"
    }
  }
}
```

## Create your own

Want a different checklist? [Create your own plugin](/creating-skills-and-plugins/create-a-plugin) that walks your release checklist and reports what is missing.


# Administrating Organizations, Workspaces, and Roles

{% hint style="info" %}
**Takeaways**

* How organizations, workspaces, and roles fit together in Tessl.
* How to set up your organization, create workspaces, and invite users with and without SSO.
* Which role to give a user, with worked examples for common team setups.
  {% endhint %}

## How organizations, workspaces, and roles fit together

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-9a9660ce13be36c163afd22f8eedb57159de5518%2Fimage%20(157).png?alt=media" alt="Diagram showing an organization containing workspaces, with users assigned roles within each workspace."><figcaption></figcaption></figure>

An **organization** is the top-level entity, usually your billing or corporate entity. Inside it, **workspaces** group skills and give role-based access to the people who work on them. A user is granted a **role** per workspace, and that role decides what they can do there: install, publish, run evals, manage members, or administer the workspace. Skills are published as plugins, so the [role](/administrators/roles) permission tables describe these same actions in terms of plugins.

* **Organization: Acme**
  * **Org admin: Samira** manages all workspaces and users.
  * **Workspace: engteam**
    * **Manager: Jennifer** invites members and publishes.
    * **Publisher: Eddie** publishes skills for the team.
    * **Member: Joe** installs skills and runs reviews.
  * **Workspace: dsteam**

Actions such as running evaluations and publishing happen from the CLI against the Tessl platform, and what a user can do depends on their role and the workspace selected for the task. Access to skills and features in the web app follows the same rule.

## Set up your organization

An organization is created automatically for you when you sign up, and it is sometimes created during the presales phase. If someone has already invited you to an organization, you are added to that organization instead when you accept the invite, so you do not need to create one.

If your organization already has workspaces, create a new one from the workspace switcher in the sidebar, below the organization dropdown. If your organization has none yet, the sidebar instead shows a "Let's set up your first workspace" card: click its **Create workspace** button.

{% hint style="info" %}
When prompted, click **Create workspace** and name it after your team, for example *engteam*. Workspace names must be unique, and they appear in the names of any plugins published from them, which is most visible for publicly published plugins.
{% endhint %}

{% hint style="info" %}
If you are in an organization with teammates but have no workspaces, you were likely invited to the organization but not to a workspace. Contact one of your organization admins, who you can see on the organization **Members** screen.
{% endhint %}

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-cfba08c6f97b16128c385a7024bfae0efd4295a9%2Fimage%20(118).png?alt=media" alt="" width="155"><figcaption></figcaption></figure>

Open **Settings** for your organization from the organization dropdown at the top of the sidebar. Rename the organization to your company name, and enable public sharing if you want users to be able to share skills publicly.

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-6641311f4762c6cd0082d3631baa8346ee89197b%2Forg-dropdown-settings.png?alt=media" alt="Organization dropdown expanded with Settings highlighted" width="221"><figcaption></figcaption></figure>

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-7c38a3b1ca5fca56aa3773218cf40f5fa3de1c8d%2Forg-settings-rename.png?alt=media" alt="Organization settings screen with the name field"><figcaption></figcaption></figure>

## Invite users

### Without SSO

Only organization admins can invite users to the organization. Invite users from the organization's **Members** screen, which you open from the organization dropdown at the top of the sidebar, assigning the workspaces each user should access along with the workspace role they get there. Invited users join the organization as members, and you can promote them to organization admin later, as below. You can also change their workspace roles from **Settings → Members** in the workspace. A user must accept their invite before you can edit them further.

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-a08ff2d744ac06b8a0b3d6de9e9484c25bdced6c%2Forg-dropdown-members.png?alt=media" alt="Organization dropdown expanded with Members highlighted" width="221"><figcaption></figcaption></figure>

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-da3427cd4fcf8c8961ad10f6539467436c5d77cb%2Fimage%20(160).png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

### With SAML SSO

Users are provisioned on their first login and start with the organization **member** role and no workspace access. An administrator then adds each user to workspaces with the right roles from **Settings → Members** in each workspace. See [SSO](/administrators/sso) for setup.

## Promote a user to organization admin

Once a user exists, you can elevate them to organization admin to let them create workspaces and manage users. On the organization **Members** screen, assign the admin role.

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-0d6f3ccf33fb72b3b6d9f14b912cd469fcd92417%2Fimage%20(151).png?alt=media" alt="" width="218"><figcaption></figcaption></figure>

## Admin keys

Admin keys give programmatic access across workspaces, for integrations and automation. Set an expiry of up to one year.

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-be0741c1006d769df51b78dc6426771bdae606ce%2Fimage%20(152).png?alt=media" alt=""><figcaption></figcaption></figure>

## Manage workspace membership

Switch to a workspace from the workspace switcher in the sidebar, then open **Settings** and select the **Members** card to set [roles](/administrators/roles) for users who need more than the default access, such as running evals, publishing, or managing members. Search for a user, select a [role](/administrators/roles), and click **Add**.

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-c6c5d5f831bcab28cd650fdb0c001b845247500f%2Fimage%20(154).png?alt=media" alt=""><figcaption></figcaption></figure>

## Example role setups

These examples show common [role](/administrators/roles) assignments when rolling Tessl out.

### Samira, organization admin

Samira administers Tessl and champions skills. She needs to manage every workspace, assign users, and create new workspaces. Make her an organization admin.

### Jennifer, team manager

Jennifer runs a workspace. She needs to add members, publish, and remove other managers. Give her the **manager** or **owner** role on her workspace, depending on whether she needs to remove other owners or delete the workspace.

### Eddie, lead engineer

Eddie installs published skills and publishes skills for his team in the engteam workspace. Give him the **publisher** role there. He can be a **member** of other workspaces where he only needs to search and install.

### Joe, new-hire engineer

Joe searches and installs skills from the engteam workspace and runs reviews, but does not yet publish. Make him a **member** of the engteam workspace. The **consumer** role is for pure consumption only: a consumer can install skills but cannot run reviews or see who else is in the workspace.


# Roles

Organization and workspace roles and their permissions

{% hint style="info" %}
**Takeaways**

* Organization roles govern account-wide access, Context, Findings, SCM connections, and workspace creation.
* Workspace roles govern private plugins, publishing, evals, workspace members, and CLI scan uploads.
* A user can hold different roles in different workspaces.
  {% endhint %}

## Organization roles

Organization roles are assigned across the Organization. An Organization admin also inherits the owner role in every workspace in that Organization.

| Permission                                                          | member | admin |
| ------------------------------------------------------------------- | ------ | ----- |
| View Organization-owned Context, Findings, and scan history         | Yes    | Yes   |
| View SCM connections and scan history                               | Yes    | Yes   |
| Ignore or restore Findings                                          |        | Yes   |
| Manage Organization-scoped API keys                                 |        | Yes   |
| Create workspaces                                                   |        | Yes   |
| Connect or remove SCM accounts, set scan scope, and trigger rescans |        | Yes   |
| Inherit owner access in every workspace                             |        | Yes   |

Context and Findings from a workspace-owned upload remain visible only to people who can view that workspace.

## Workspace roles

Workspace roles are assigned per workspace. To learn more about creating a workspace, see [Workspace management](/reference/cli-commands#workspace-management).

| Permission                                         | consumer | member | publisher | manager | owner |
| -------------------------------------------------- | -------- | ------ | --------- | ------- | ----- |
| View and install private plugins in the workspace  | Yes      | Yes    | Yes       | Yes     | Yes   |
| View workspace users and permissions               |          | Yes    | Yes       | Yes     | Yes   |
| Generate workspace-scoped API keys                 |          | Yes    | Yes       | Yes     | Yes   |
| Review, security-scan, and fix skills from the CLI |          | Yes    | Yes       | Yes     | Yes   |
| Generate eval scenarios                            |          |        | Yes       | Yes     | Yes   |
| Run evals                                          |          |        | Yes       | Yes     | Yes   |
| Manage eval visibility                             |          |        | Yes       | Yes     | Yes   |
| Create and publish plugins                         |          |        | Yes       | Yes     | Yes   |
| Unpublish a plugin within two days of publishing   |          |        | Yes       | Yes     | Yes   |
| Roll out plugins to repositories                   |          |        | Yes       | Yes     | Yes   |
| Upload a scan with `tessl inventory import`        |          |        | Yes       | Yes     | Yes   |
| Set up and manage workspace integrations           |          |        |           | Yes     | Yes   |
| Invite users and change workspace permissions      |          |        |           | Yes     | Yes   |
| Remove other managers                              |          |        |           | Yes     | Yes   |
| Archive plugins                                    |          |        |           | Yes     | Yes   |
| Edit or delete workspace-scoped API keys           |          |        |           | Yes     | Yes   |
| Add or remove workspace owners                     |          |        |           |         | Yes   |
| Delete the workspace                               |          |        |           |         | Yes   |


# Using the EU region

Set up the Tessl CLI for an organization using the EU region.

Use this guide if your organization was provisioned on [eu.tessl.io](https://eu.tessl.io). The web app is at `https://eu.tessl.io`, and the CLI uses the API at `https://api.eu.tessl.io`.

## Set the EU environment

Make sure `TESSL_ENV=app-eu` is set in your shell environment before you run any `tessl` command. The CLI reads it each time it starts. Unless `TESSL_API_BASE_URL` or `TESSL_WEB_BASE_URL` is also set, this selects the EU API and web app.

Add the setting to your shell profile so new terminals inherit it. Use the profile for your shell:

```shell-session
$ echo 'export TESSL_ENV=app-eu' >> ~/.zshrc
$ source ~/.zshrc
```

```shell-session
$ echo 'export TESSL_ENV=app-eu' >> ~/.bashrc
$ source ~/.bashrc
```

On Windows PowerShell, persist the setting for your user, then open a new PowerShell window:

```powershell
[Environment]::SetEnvironmentVariable('TESSL_ENV', 'app-eu', 'User')
```

If you prefer not to change your profile, set the variable inline on each command instead:

```shell-session
$ TESSL_ENV=app-eu tessl login --device
```

## Log in

Use device login for the EU region:

```shell-session
$ tessl login --device
```

The CLI shows a code and the shared device login URL, `https://auth.tessl.io/device`. Open the URL, sign in, and enter the code.

Confirm that the CLI is using the EU environment:

```shell-session
$ tessl whoami
```

The output includes `Environment: app-eu`. It appears whether or not you are logged in, so you can also use this to check the setting before you log in.

## CLI files and global content

Unless `TESSL_GLOBAL_DIR` is set, the EU CLI stores its data in `~/.tessl_app-eu`. This is separate from the US CLI directory, `~/.tessl`, so credentials, globally installed tiles, and globally installed plugins do not transfer between regions. Install any global tiles and plugins again after you log in to the EU region.

If your environment sets `TESSL_API_BASE_URL`, `TESSL_WEB_BASE_URL`, or `TESSL_GLOBAL_DIR`, review or remove those overrides before you use the EU region. They take precedence over the regional defaults and can select a different endpoint or storage directory.

## CI, MCP servers, and agents

Set `TESSL_ENV=app-eu` in the environment that starts the CLI. This applies to CI jobs, MCP servers, and agent integrations that run `tessl`.

For example, set the variable in a CI job environment:

```yaml
env:
  TESSL_ENV: app-eu
```

Recipe sandboxes launched from the EU instance receive this setting automatically. You do not need to set it in a skill, recipe, or schedule.

## Availability

The public registry and public publishing are not available in the EU region.

The CLI will select your region at login in a future update. Until then, keep `TESSL_ENV=app-eu` set in the environment the CLI runs in.


# SSO

{% hint style="info" %}
**Takeaways**

* SAML SSO is a paid feature available on the Business and Enterprise tiers.
* Users are provisioned on first login as organization members with no workspace access.
* An administrator then grants each user workspace roles.
  {% endhint %}

## Enabling SAML based SSO

To enable SAML based Single Sign On (SSO), your Tessl account manager or technical contact will provide a link for your SSO system to connect and configure Tessl.

Your account manager can also send a link to add and verify your company's email domains. Tessl sends users to your SSO provider when their email address is on a verified domain, so verify every domain your users sign in with.

## Configuring Users

{% hint style="info" %}
Users are provisioned on first login
{% endhint %}

By setting up the integration, a user will authenticate via your SSO and be assigned the “member” role of the Organization when they visit Tessl. Your Tessl Org or Tessl Workspace administrators can add the user to workspaces with the desired roles by navigating to ***Settings → Members*** in each workspace.

For information on setting roles and managing users, see:

* [Organizations, Workspaces, and Roles](/administrators/administrating-organizations-workspaces-and-roles)
* [Roles](/administrators/roles)


# Using the Tessl agent

Six things teams use the agent for, the way you'd actually ask for each one, the command it runs underneath, and what you should see back.

## 01 See what you already have

Skills are multiplying across repos, and most teams can't say how many they have or who owns them. This is free, needs no changes to your code, and is the first thing worth running.

<details>

<summary>See what skills you already have</summary>

USE WHEN You have skills spread across repos and no single view of what exists or who owns it.

**Ask your agent**

"Scan our GitHub org and upload the results to Tessl."

**Under the hood**

`tessl inventory import`

Scans your repos, collects `SKILL.md` and manifest files, and uploads a snapshot. Forked repos are skipped.

**What to expect**

The uploaded results feed the reporting Organization's Context and Findings, where you can see what exists, where it lives, and what Tessl reported. Analysis and scoring continue in the background, so results can appear at different times.

Trial it on a subset first with `tessl inventory import --repo repo-a --repo repo-b`. The flag repeats as many times as you like.

It reads only `skill.md` files and manifests. No source code, no real names or email addresses (GitHub usernames only), and no repo clones: files are fetched one at a time through the API.

{% hint style="warning" %}
**Heads up:** the default scan covers your 100 most recently active repos. Large orgs can hit GitHub's limit of 5,000 API requests per hour mid-scan. Scans are diffed, so nothing is lost: wait for the reset and run it again.
{% endhint %}

</details>

***

## 02 Bring or create skills

Three starting points, depending on what you already have.

<details>

<summary>Import a skill you already have</summary>

USE WHEN Best if you already have a skill in your repo. The agent brings it in as-is.

**Ask your agent**

"Import the skill at plugins/my-plugin into Tessl."

**You provide**

The path to a skill you already have, either a `SKILL.md` file or the directory holding it.

**Under the hood**

`tessl skill import`

Wraps a standalone skill as a plugin: `SKILL.md` plus a `.tessl-plugin/plugin.json` manifest.

**What to expect**

Your skill unchanged, now with a manifest beside it, so it can be reviewed, evaluated, versioned, and published.

</details>

<details>

<summary>Create a new skill</summary>

USE WHEN Nothing to bring yet? The agent walks you through a new one from scratch.

**Ask your agent**

"Help me create a skill."

Or name it: `"run plugin-creator"`

**Under the hood**

Built-in `plugin-creator` skill

Interviews you about the skill's scope, scaffolds the plugin and skill directory, then validates the result.

**What to expect**

An interview about what the skill should cover, then a drafted `SKILL.md`. Check the `description` carefully: it's what decides whether an agent picks the skill up at the right moment.

</details>

<details>

<summary>Create context for recurring agent mistakes</summary>

USE WHEN You have PR review history, and the same corrections keep coming up in it.

**Ask your agent**

"Agents keep getting this wrong. Look at our last 2 weeks of PR reviews."

Or name it: `"run find-optimizations on the last 2 weeks"`

**You provide**

A PR number, or a time window in plain language such as "the last 2 weeks". It will not run without one.

**Under the hood**

Built-in `find-optimizations` skill

Batch-fetches feedback from change-requested PRs, then splits it across subagents so no PR body ever crowds out the context.

**What to expect**

A deduplicated, numbered list of improvements. Each one is typed (`skill`, `review`, `skill`, `rule`, `hook`, `test`, `refactor`, or `verifier`), backed by the PR numbers and specific comments that motivate it, and pointed at an exact destination rather than a vague suggestion.

It triages before it proposes. When a linter, typechecker, or schema validator could own the pattern, it names that tool instead of reaching for a verifier. Nothing gets changed: the output is the deliverable.

It reads your source control preferences from `.tessl/memory/` first, so it may run `/setup-memory` before the analysis if this is your first time. Needs real review history to work with, so run it on a repo where people leave PR comments.

</details>

***

## 03 Automate code review

Tessl Code Review reviews the exact pull request head and publishes one native GitHub review. A review can request required changes while keeping optional suggestions visible, and later rounds account for fixes and replies.

<details>

<summary>Set up Tessl Code Review</summary>

USE WHEN You want a first review before a human starts, or a repeatable review on every merge candidate.

**The supported path is the GitHub App**

The Tessl Review GitHub App is the supported way to run Tessl Code Review. It reviews pull requests on Tessl's infrastructure, with no workflow to write and no token to store. Open **Code reviews** in the Tessl web app and select **Connect GitHub**, then enable each repository you want reviewed. Read [Set up Tessl Code Review](/tutorials/setting-up-agentic-code-review) for install, the separate enable step, trigger policies, approval, the config file, and troubleshooting.

**If you cannot install a GitHub App**

An unsupported GitHub Action can run reviews from a workflow you own instead. The `tessl/code-review` plugin carries a setup skill that writes it for you:

```sh
tessl install tessl/code-review
```

If your `tessl.json` still declares `tessl/code-review-setup` or `tessl/code-review-lens-creator`, replace it with `tessl/code-review`. Those plugins were folded into `tessl/code-review` and are being retired; while a project declares one of the old names, `tessl agent` skips its built-in copy of the merged plugin so the same skill is not loaded twice, and the project gets only what the old plugin carried.

Ask your agent to set up code review, and it points you to **Code reviews** for your organization, where you select **Connect GitHub** to install the Tessl Review GitHub App. The agent cannot install the App for you: the install starts in the browser. It also tells you that the repository has to be enabled in Tessl afterwards, since installing the App alone does not start reviews. See [Set up Tessl Code Review](/tutorials/setting-up-agentic-code-review) for the whole setup.

If you cannot install a GitHub App, an unsupported GitHub Action can run Tessl Code Review from a workflow you own. Ask for the GitHub Action by name to get it. See [Running Code Review in your own CI](/tutorials/setting-up-agentic-code-review/code-review-action) for its input and output reference.

</details>

<details>

<summary>Choose and tune review lenses</summary>

A lens is a focused set of review instructions. The `standard` profile runs the lenses from the [`tessl/code-review`](https://tessl.io/registry/tessl/code-review) plugin by default: `review-correctness-and-data-integrity`, `review-maintainability-and-code-quality`, `review-scale-and-resilience`, and `review-security-and-privacy`. Begin without overriding the selection and evaluate the results on pull requests you understand.

Ask your agent to add repository-specific lenses only when you can name the concern and show examples of what a good review should catch:

> Add our versioned API-contract and data-handling lenses to Tessl Code Review. Keep the existing cadence and advisory mode.

Passing `lenses` replaces the profile's complete default selection, and a review supports at most eight lenses. Pin every registry reference (for example `tessl/code-review@0.2.0#review-security-and-privacy`) and keep each lens responsible for a distinct concern, so findings do not repeat each other. To fork a default lens, start from its source in [`tesslio/product-plugins`](https://github.com/tesslio/product-plugins); to write one from scratch, use the `create-code-review-lens` skill in the same [`tessl/code-review`](https://tessl.io/registry/tessl/code-review) plugin.

</details>

<details>

<summary>Move from advisory to gate mode</summary>

USE WHEN Advisory reviews have earned trust and every merge candidate can receive a review.

**Ask your agent**

"Change Tessl Code Review to gate mode and tell me which GitHub repository settings and required check I need."

**What to expect**

The workflow changes to `mode: gate`. An approved review attempts a native GitHub approval and reports a successful check. A review that requires changes attempts to request changes and reports a failed check.

The agent should also tell you to:

* allow GitHub Actions to create and approve pull requests;
* require the Action-owned check named exactly `Tessl Code Review`;
* use an automatic cadence or make requested reruns part of the merge process;
* test both an approved change and a change that should be blocked.

Do not require the caller workflow job as the review gate. Mention-driven and manually dispatched jobs are associated with the default branch, while the Action reports its own check against the reviewed pull request head.

</details>

<details>

<summary>Decide separately whether a human review is required</summary>

USE WHEN You want a repository-owned risk policy to decide which pull requests still need a human reviewer.

**Ask your agent**

"Set up a PR risk gate for this repo."

**You provide**

A choice of starter policy: `conservative-starter` or `official-review-policy-parity`. Read and edit the generated policy files before you depend on the gate.

**Under the hood**

```sh
tessl change risk init
tessl change risk --base origin/main \
  --fail-if-review-required --json
```

This combines deterministic git measurements with an advisory agent judgment, followed by a deterministic policy decision. It is separate from Tessl Code Review: one reviews the code, while the other decides whether repository policy requires a human.

**What to expect**

Starter files under `.github/pr-review-gate/`: a config, a policy, and a prompt. Two starters are available: `conservative-starter` is the default and requires human review for most changes; `official-review-policy-parity` tracks your existing branch protection more closely. Read and edit the policy before you rely on it.

In CI, `--fail-if-review-required` exits non-zero when the gate decides a human is needed.

{% hint style="warning" %}
**Heads up:** `change risk init` writes the policy files only. It does not create a workflow and does not touch branch protection. Add the check as advisory first, watch it on real PRs for a few weeks, and make it required once you trust the signal.
{% endhint %}

</details>

***

## 04 Enforce your standards

Verifiers check binary, observable facts about committed files. Reach for one when a reviewer keeps flagging the same structural pattern and no linter can own it.

<details>

<summary>Write your first verifier</summary>

USE WHEN A reviewer keeps flagging the same structural pattern and no linter can express it.

**Ask your agent**

"Create a verifier that checks every Fastify route file returns via our typed response helpers."

**You provide**

The rule you want enforced, in plain language and specific enough that someone could score a file pass or fail by reading it.

**Under the hood**

```sh
tessl change verify lint <file>
tessl change verify --dry-run --all --show-files
```

Validates the structure against the schema, then previews exactly which files would be judged before spending any judge calls.

**What to expect**

A verifier JSON file plus an entry in the `verify` block of `tessl.json`. Each verifier carries a `name`, an `instruction`, a `relevant_when` eligibility test, `context` a `scope` (file, paths, or diff) with its `scope_reason`, and a `checklist` of one to three binary rules.

Severity is never in the verifier file. It's repo policy, set per group in `tessl.json` as `info`, `warn`, or `error`. That way the same verifier can be advisory in one repo and blocking in another.

{% hint style="warning" %}
**Heads up:** most bad verifiers fail for one reason: a `relevant_when` that's too broad. "Backend file", "React component", and "any TypeScript file" are all too vague to judge consistently. Name the observable trait that makes a file in scope.
{% endhint %}

</details>

<details>

<summary>Run verifiers on every PR</summary>

USE WHEN Your first verifier has earned trust and you want it enforced in CI.

**Ask your agent**

"Run verifiers on every PR."

**You provide**

The verifier group already wired into `tessl.json`, plus a base ref. Nothing else.

**Under the hood**

`tessl change verify --base origin/main --github`

`--github` emits GitHub Actions annotations, so findings appear inline in the PR even when the run exits 0.

**What to expect**

Findings in the PR UI, anchored to the lines that violate the rule. Verifiers set to `error` make the check blocking; `warn` annotates without failing, unless you add `--fail-on-warn`.

Start every new verifier at `warn` while you calibrate. Promote it to `error` only after you've confirmed it avoids false positives on real PRs.

**Widen the blast radius one rung at a time**

`tessl change verify lint` → `tessl change verify --dry-run --all --show-files` → `tessl change verify --sample 5` → full run

</details>

***

## 05 Automate the chores

The recurring work that never makes it onto a sprint board: the scan someone runs by hand, the cleanup that gets deferred, the check everyone forgets.

<details>

<summary>Find chores worth automating</summary>

USE WHEN You suspect there is repeat work in this repo but have not pinned down what.

**Ask your agent**

"There's a chore I keep doing by hand."

Or name it: `"run find-automations"`

**Under the hood**

Built-in `find-automations` skill

Reads the repo and its history for work that recurs on a predictable trigger.

**What to expect**

A numbered list of opportunities. Each one carries a ticket-style title, its pattern type (`established`, `emerging`, or `one-off`), the evidence behind it, a suggested trigger and execution mode, and a description detailed enough to implement from. The numbering is the point: you can come back and say "create a ticket from item 2".

It looks for signals like the same person filing the same maintenance PR, the same reviewer leaving the same comment, repeated CI babysitting, and release or dependency chores. Strictly read-only: it won't write a file, comment on a PR, or trigger a workflow.

</details>

<details>

<summary>Turn a chore into a CI workflow</summary>

USE WHEN You have agreed on a candidate and want it running without anyone remembering to.

**Ask your agent**

"Run this recurring task automatically in CI."

Or name it: `"run workflow-automator"`

**You provide**

The task you want automated and roughly when it should run. It interviews you for the rest: what counts as a successful run, what the agent needs as input, the exact trigger, and any secrets or admin approvals required.

**Under the hood**

Built-in `workflow-automator` skill

Builds the scheduled GitHub Actions workflow and the skill it invokes.

**What to expect**

A committed workflow on a schedule you chose, plus the auth to run it. Create the key with `tessl api-key create --workspace <name>`, store it as a `TESSL_TOKEN` repository secret, and the `tesslio/setup-tessl@v2` action picks it up. Scheduled agent workflows need `ANTHROPIC_API_KEY` as a repository secret too.

The default shape is a weekly cron plus a `workflow_dispatch` trigger, so you can run it by hand while you're still building trust in it, with the results uploaded as an artifact you can read after each run.

</details>

<details>

<summary>Run a skill unattended</summary>

USE WHEN You want a skill to run without sitting through it, locally or in a Tessl cloud sandbox.

**Ask your agent**

"Run the find-automations skill on the payments repo."

**You provide**

The skill to run: a bare installed name such as `find-automations`, a registry ref `workspace/skill[@version]`, or `file:path/to/skill`. Plus an `--agent`. Pass per-run input with `--instructions "…"`, and add `--repo owner/name` when you use `--cloud`.

**Under the hood**

```
tessl launch skill <workspace/skill> \
  --agent claude-code --yolo
```

Add `--cloud --repo owner/name` to run it in the cloud instead of on your machine.

**What to expect**

The skill runs through your chosen agent with output streaming back. `--yolo` skips permission prompts, which is what makes it usable in CI. Use `--interactive` to watch it in the foreground instead, and `tessl launch list` to see recent runs.

</details>

<details>

<summary>Hooks, when the trigger is an agent event</summary>

* **Two tiers, declared in `.tessl-plugin/plugin.json`.** Use `hooks` for the portable path that works across agents, and `nativeHooks` to pass agent-specific config straight through when you need the escape hatch.
* **Five generic events.** `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `SessionStart`, and `Stop`. A `matcher` narrows a hook to specific tools; omit it to fire on all of them.
* **Anchor every path with `${TESSL_PLUGIN_DIR}`.** It's the only reference that survives a change of working directory. Bare relative paths resolve against wherever the event fired, not your plugin.

{% hint style="warning" %}
**Heads up:** hook scripts have to live under `hooks/` in the plugin to ship at all, and only the files you actually declare are made executable at install time. A `timeout` in the manifest is accepted but not currently enforced, so don't rely on it to stop a slow hook.
{% endhint %}

</details>

***

## 06 Measure your context

Context is the skills and rules your agents load. One that reads well can still fail to fire, or fire and change nothing. Reviews score the writing. Evals measure the difference it makes.

<details>

<summary>Score a skill's quality</summary>

USE WHEN You want to know how a skill reads against a rubric before anyone depends on it.

**Ask your agent**

"Review this skill."

**You provide**

The path to the skill or plugin, plus `--workspace`. Defaults to the current directory if you omit the path.

**Under the hood**

```
tessl review run ./my-skill \
  --workspace <name>
```

Runs server-side. Ctrl-C detaches from the output; the review keeps going.

**What to expect**

A score with a per-judge breakdown, so you can see which dimension pulled it down. In CI, `--threshold <percent>` exits non-zero below the bar you set; `0` never fails and skips score gating entirely.

For a security pass instead, run `tessl review run security` and gate it with `--fail-on low|medium|high|critical`.

</details>

<details>

<summary>Improve a skill automatically</summary>

USE WHEN The review found problems and you would rather the agent fixed them than do it by hand.

**Ask your agent**

"Optimize my skill."

**You provide**

The same path and workspace. It prompts before it edits unless you pass `--yes`.

**Under the hood**

`tessl review fix`

An improve-then-re-review loop. `--max-iterations` caps the passes (1 to 10) and `--threshold` stops it early once the score clears your bar.

**What to expect**

It prompts before it edits, then hands you a change summary of exactly what it improved. Pass `--yes` to skip the prompts and `--threshold` to stop once the score clears a bar.

</details>

<details>

<summary>Measure whether a skill helps</summary>

USE WHEN A skill reads well, but you have not proved it changes what an agent actually does.

**Ask your agent**

"Build an eval for this skill."

**You provide**

The plugin directory. `scenario generate` needs a source to generate from, and `scenario download` reads scenarios from `evals/`.

**Under the hood**

```
tessl scenario generate <plugin> --count 5
tessl scenario download --last
tessl eval run <path/to/plugin>
```

Keep counts low at first so runs finish while you're still watching.

**What to expect**

Each scenario runs twice, once with your skill injected and once without, so the result is a delta rather than a bare score. That's the number that tells you whether the skill earns its place. Add `--runs 3` to average out model variance.

Downloaded scenarios land as `evals/<hash>-<slug>/` with a `task.md`, a `criteria.json`, and a `scenario.json`.

{% hint style="info" %}
**Note:** `scenario download` writes to the plugin's `evals/` directory automatically when run from the plugin root or any subdirectory of it. The plugin root is the directory that contains `.tessl-plugin/plugin.json`. Evals need at least `publisher` access; `member` is enough to run a review but not an eval.
{% endhint %}

</details>


# Enhance your workflow with skills

Learn how to discover and install skills that enhance your agent's capabilities with procedural workflows

Skills provide step-by-step workflows that help your agent perform specific tasks correctly. This guide shows you how to find, install, and use skills to improve your development workflow.

## What are skills?

Skills are procedural guides that teach agents how to perform specific workflows. Unlike documentation (which explains what something is), skills explain how to do something step-by-step.

**Example skills:**

* Debug API endpoints systematically
* Review code for security issues
* Write comprehensive test suites
* Generate API documentation

When you work on tasks, your agent automatically loads relevant skills to follow best practices and team procedures.

## Prerequisites

Before installing skills, make sure you've initialized Tessl in your project:

```bash
# Initialize Tessl (auto-detects and configures your agent)
tessl init
```

See [Improve your first skill](/introduction-to-tessl/improve-your-first-skill) and [Custom agent setup](/reference/custom-agent-setup) for details.

## Discovering skills

### Browse the registry

Visit the [Tessl Registry](https://tessl.io/registry) to browse available skills. You can further filter the results by:

* plugin
* Skill

### Search from CLI

Search for skills directly from your terminal:

```bash
# Search for testing skills & plugins
tessl search testing

# Search for code review skills & plugins
tessl search "code review"

# Interactive search
tessl search
```

### Ask your agent

You can also ask your agent to find and install skills:

```
Find me a skill for debugging API endpoints
```

## Installing skills

{% hint style="info" %}
Note, if you're installing skills to your OpenClaw agent, you must install skills from your agent's workspace. Check out [OpenClaw](/reference/custom-agent-setup/openclaw) for more information.
{% endhint %}

By default, skills are installed into your current project (`.tessl/plugins/`) and tracked in `tessl.json`. Use `--global` to install a skill to `~/.tessl/` instead, making it available across all your projects without needing to reinstall it per project.

```bash
# Install into the current project (default)
tessl install tessl-labs/debug-api-endpoints

# Install globally, available in all projects
tessl install --global tessl-labs/debug-api-endpoints
```

### From the registry

Install skills by name from the Tessl registry:

```bash
# Install a specific skill
tessl install tessl-labs/debug-api-endpoints

# Install multiple skills
tessl install tessl-labs/code-review tessl-labs/testing-workflow
```

### From GitHub repositories

Many open source projects publish skills on GitHub:

```bash
# Install from GitHub with interactive skill selection
tessl install https://github.com/anthropics/skills

# Install specific skills by name
tessl install https://github.com/anthropics/skills --skill pdf --skill pptx

# Install from a specific branch or path
tessl install https://github.com/owner/repo/tree/main/skills/my-skill

```

The registry shows a commit-specific install command when security results are available for that version — use it to install the exact version that was scanned. See [Security scores](#security-scores) for details.

## Using installed skills

Once installed, skills work automatically. Your agent loads relevant skills based on:

* Your task description
* The files you're working with
* The context of your conversation

### How skills activate

Skills use "lazy push" - they're automatically loaded when relevant:

**Example: Debugging workflow**

1. You say: "Debug the failing API endpoint"
2. Agent recognizes this matches the debug-api-endpoints skill
3. Skill is loaded with its step-by-step workflow
4. Agent follows the procedure: check infrastructure → verify auth → validate I/O → test functionality

You don't need to explicitly invoke skills - they activate automatically when needed.

## Managing installed skills

### List installed skills

View all skills in your project:

```bash
tessl list
```

This shows all installed plugins, including those containing skills.

### Update skills

Check for newer versions and reinstall to update:

```bash
# Check which installed plugins have updates available
tessl outdated

# Reinstall to get latest version
tessl install tessl-labs/debug-api-endpoints
```

### Remove skills

Uninstall skills you no longer need:

```bash
# Remove from the current project
tessl uninstall tessl-labs/debug-api-endpoints

# Remove a globally installed skill
tessl uninstall --global tessl-labs/debug-api-endpoints
```

## Reviewing skill quality

Before installing skills, check their review scores and evaluations in the registry to understand their quality and effectiveness.

### Understanding review scores

Skills in the registry show multiple quality indicators:

**Review Score** (0-100%): Overall quality assessment

* 90%+: High-quality, production-ready
* 70-89%: Good quality, may have minor improvements needed
* Below 70%: Needs work before deployment

**Validation Score**: Checks for required fields, trigger hints, workflow structure, and metadata completeness

**Implementation Score**: Code quality in examples, security practices, error handling

**Activation Score**: How well agents discover and load the skill when relevant

### Viewing evaluations

Click on any skill in the registry to see:

* Detailed validation results (what passed/failed)
* Specific recommendations for improvement
* Example usage and trigger conditions
* Community feedback and ratings

Use these scores to choose skills that meet your quality standards before installing.

For more details on how skills are scored, see [Check a skill's quality using review](/improving-your-skills/reviewing-skills).

## Security scores

Skills installed from the Tessl Registry are scanned for security vulnerabilities using Snyk. Security scores are shown in the registry UI alongside quality scores so you can assess risk before installing.

Security scanning can produce false positives, and Tessl is designed to give you flexibility in how much weight you give the findings rather than blocking you outright.

### Security gating

When you run `tessl install` or `tessl update`, if a skill has critical or high security findings, the CLI will ask for your permission before continuing. You can always choose to proceed — installation is never blocked.

To skip the prompt automatically (for example in CI or automated workflows):

```bash
tessl install engteam/my-skill --accept-warnings
```

### Installing a pinned version

The registry shows a commit-specific install command alongside security results, so you can install the exact version that was scanned:

```bash
tessl install engteam/my-skill@abc1234
```

You can also install the latest version at any time by omitting the version, or run `tessl update` on an already-installed skill.

### Reporting false positives

Each security finding in the registry includes a **Report Incorrect Findings** link. Use it to flag suspected false positives — the Tessl team will investigate and feed confirmed corrections back to Snyk to improve the scanner.

## Best practices

**Start with popular skills**: Browse featured skills in the registry to find well-reviewed, widely-used workflows.

**Install skills for your tech stack**: Look for skills specific to your frameworks and tools (React, FastAPI, Docker, etc.).

**Create team skills**: Build custom skills for your team's specific workflows and procedures. See [Creating skills](/creating-skills-and-plugins/create-a-skill).

**Review before installing**: Check skill review scores in the registry to understand quality and completeness.

**Keep skills updated**: Reinstall periodically to get improvements and bug fixes.

## Troubleshooting

**Skill not activating?**

* Make sure the skill is installed: `tessl list`
* Try being more specific in your request to match skill triggers
* Check that your agent has MCP configured: `tessl init --agent <name>`

**Multiple skills conflicting?**

* Skills are designed to work together, but if you notice issues, uninstall less relevant ones
* More specific skills usually take precedence over general ones

**Need a skill that doesn't exist?**

* Create your own: [Creating skills](/creating-skills-and-plugins/create-a-skill)
* Request it in the Tessl community
* Check GitHub for open source skills in your domain

## Next steps

* [Creating skills](/creating-skills-and-plugins/create-a-skill) - Build custom workflows for your team
* [Check a skill's quality using review](/improving-your-skills/reviewing-skills) - Understand skill quality scores


# Migrating from tiles to plugins

Guide for migrating from the legacy tile.json format to the new plugin format

Tessl is transitioning from **tiles** to **plugins** as the packaging format for shareable agent rules and skills. This document explains why, what changed, and how to migrate your existing tile packages.

## Why the change?

Tessl started with tiles: prepackaged bundles of context for agents. Since then, the wider agent ecosystem is settling on plugins as the standard format for the same idea, so we're moving Tessl onto plugins.

For you, this means what you build in Tessl travels naturally to different coding agents, and the terminology lines up with what your teams are already seeing. For us, it's a stronger foundation to build on. As agents start handling richer kinds of context, plugins give us the base to layer versioning, distribution, evaluation, and security on top of these new context types.

***

## What changed

### Manifest location and filename

| Format | File          | Location                    |
| ------ | ------------- | --------------------------- |
| Tile   | `tile.json`   | Package root                |
| Plugin | `plugin.json` | `.tessl-plugin/plugin.json` |

The manifest moves into a dedicated `.tessl-plugin/` directory. This keeps the root clean and makes space for sibling agent manifests (`.claude-plugin/`, `.cursor-plugin/`) without naming collisions, if and when they are added.

### Field-by-field comparison

| Concept           | tile.json                                 | plugin.json               | Notes                                          |
| ----------------- | ----------------------------------------- | ------------------------- | ---------------------------------------------- |
| Package name      | `name`                                    | `name`                    | Same format: `workspace/package`               |
| Version           | `version`                                 | `version`                 | Required at publish in both                    |
| Short description | `summary`                                 | `description`             | Field renamed; trimmed and required at publish |
| Private flag      | `private`                                 | `private`                 | Identical                                      |
| Repository URL    | `repository`                              | `repository`              | Both accept `https://` URLs                    |
| Package author    | —                                         | `author`                  | New: `{ name, email, url }` object             |
| Homepage          | —                                         | `homepage`                | New: URL string                                |
| License           | —                                         | `license`                 | New: SPDX identifier, e.g. `"MIT"`             |
| Skills            | `skills: { "name": { path: "..." } }`     | `skills: "./skills/"`     | Changed from keyed object to path(s)           |
| Rules / Steering  | `rules: { "name": { rules: "..." } }`     | `rules: "./rules/"`       | Changed from keyed object to path(s)           |
| Commands          | `commands: { "name": { script: "..." } }` | `commands: "./commands/"` | Changed from keyed object to path(s)           |
| Docs file         | `docs: "./README.md"`                     | —                         | Removed; move content into skills or rules     |
| Describes (PURL)  | `describes: "pkg:..."`                    | —                         | Removed; not carried forward                   |

### Skills: from keyed registry to directory discovery

In `tile.json`, each skill had an explicit key and path:

```json
{
  "skills": {
    "my-skill": { "path": "./skills/my-skill/SKILL.md" },
    "other-skill": { "path": "./skills/other-skill/SKILL.md" }
  }
}
```

In `plugin.json`, you point at a directory and Tessl discovers skills automatically. A skill's identity comes from the `name:` field in its `SKILL.md` frontmatter (falling back to the directory basename if no frontmatter is present):

```json
{
  "skills": "./skills/"
}
```

You can also pass an explicit array of paths when you want precise control:

```json
{
  "skills": ["./skills/my-skill/SKILL.md", "./skills/other-skill/SKILL.md"]
}
```

### Rules: from nested objects to directory paths

In `tile.json`:

```json
{
  "rules": {
    "style-guide": { "rules": "./rules/style-guide.md" }
  }
}
```

Note: `steering` was accepted as an alias for `rules` in `tile.json` and normalised automatically. Both wrote to the same field internally.

In `plugin.json`:

```json
{
  "rules": "./rules/"
}
```

All `.md` files found in the declared directory are included as rules. There are no longer named keys; the rule name is derived from the filename.

### Docs and Describes: removed

The `docs` field (a standalone markdown file) and `describes` field (a PURL for a versioned dependency) have no equivalent in `plugin.json`.

* **`docs`**: Fold the content into a rule or skill. If the doc was user-facing guidance about a tool or library, it belongs as a skill.
* **`describes`**: This field declared that a tile described a specific versioned package (e.g. a Go library). It was used as part of Docs, as Docs have not been carried forward, neither has Describes.

If you publish with `TESSL_PLUGIN=1` and your `tile.json` still has `docs` or `describes` set, the CLI will warn you at pack time that these fields will not be included.

***

## Compatibility during the transition

Tiles and plugins coexist. The CLI continues to read and publish `tile.json` packages without any changes required. No existing tile breaks.

When both a `tile.json` and a `.tessl-plugin/plugin.json` are present in the same package `.tessl-plugin/plugin.json` takes precedence for metadata (name, version, description).

{% hint style="info" %}
For new work, use plugins: `.tessl-plugin/plugin.json` and `tessl plugin ...`. Use tiles only for legacy compatibility or migration. Publish legacy `tile.json` packages with `tessl tile ...`. Publish new plugin packages with `tessl plugin ...`.
{% endhint %}

***

## How to migrate

Using the migrate command

The CLI can generate `.tessl-plugin/plugin.json` from your existing tile.json automatically:

```
tessl tile migrate
```

Run this from your package root (the directory containing tile.json). The command:

* Creates `.tessl-plugin/` if it doesn't exist
* Translates all supported fields (name, version, summary → description, repository, private, skills, commands, rules)
* Warns if your tile.json contains docs or describes — these have no plugin equivalent and will not appear in the output

If you want to migrate a package that isn't in the current directory, pass the path:

```
tessl tile migrate ./path/to/my-package
```

What the command does not do

The migrate command handles the mechanical translation. A few steps still require your attention:

* docs and describes: If your tile.json uses these fields, the command will warn you and omit them. See Docs and Describes: removed for how to fold that content into skills or rules.
* SKILL.md frontmatter: The command copies your skill paths as-is. If your SKILL.md files are missing a name: field, Tessl will fall back to the directory basename — but adding frontmatter is recommended. See Step 4: Add SKILL.md frontmatter.
* tile.json is kept: The command does not delete your existing tile.json. Once you've verified the new manifest with `tessl plugin lint` and `tessl plugin publish --dry-run`, you can remove it manually.

***

### Alternatively:

### Step 1: Create `.tessl-plugin/plugin.json`

Create the directory and manifest:

```
mkdir .tessl-plugin
touch .tessl-plugin/plugin.json
```

Translate your `tile.json` fields:

**tile.json (before):**

```json
{
  "name": "myorg/my-package",
  "version": "1.2.0",
  "summary": "Linting rules for our TypeScript monorepo",
  "repository": "https://github.com/myorg/my-package",
  "skills": {
    "lint-fix": { "path": "./skills/lint-fix/SKILL.md" }
  },
  "rules": {
    "ts-style": { "rules": "./rules/ts-style.md" },
    "imports": { "rules": "./rules/imports.md" }
  }
}
```

**.tessl-plugin/plugin.json (after):**

```json
{
  "name": "myorg/my-package",
  "version": "1.2.0",
  "description": "Linting rules for our TypeScript monorepo",
  "repository": "https://github.com/myorg/my-package",
  "skills": "./skills/",
  "rules": "./rules/"
}
```

### Step 2: Check your directory layout

The path values in `plugin.json` are relative to the **package root** (i.e. the directory containing `.tessl-plugin/`). Verify that the paths you declared actually exist:

```
my-package/
├── .tessl-plugin/
│   └── plugin.json          ← manifest
├── skills/
│   └── lint-fix/
│       └── SKILL.md         ← discovered automatically
└── rules/
    ├── ts-style.md           ← discovered automatically
    └── imports.md            ← discovered automatically
```

### Step 3: Handle docs and describes

If your `tile.json` has a `docs` field:

1. Open the file it points to.
2. If the content is a step-by-step procedure for the agent to follow, convert it to a skill: create a `SKILL.md` in your skills directory with the content.
3. If the content is constraints or style guidance, move it into your rules directory as a `.md` file.
4. Remove `docs` from `tile.json` (or skip it entirely if you're dropping `tile.json`).

### Step 4: Add SKILL.md frontmatter (recommended)

With `tile.json`, skill identity was the key in the `skills` object. With `plugin.json`, identity comes from the `name:` field in `SKILL.md` frontmatter. Add frontmatter if it's missing:

```markdown
---
name: lint-fix
description: Fix lint errors in TypeScript files using the project's oxlint config
---

# Lint Fix

...
```

If there's no frontmatter, Tessl uses the directory basename as the skill name — so existing skills continue to work without changes.

### Step 5: Verify with the CLI

Run the linter to catch any issues:

```
tessl plugin lint
```

In plugin mode (`TESSL_PLUGIN=1`), the CLI will:

* Warn if `tile.json` is still present with `docs` or `describes` fields that have no plugin equivalent.
* Validate that every path declared in `plugin.json` resolves and contains recognisable content.

Do a dry-run pack to confirm what will be included in the tarball:

```
tessl plugin publish --dry-run
```

### Step 6: Publish

```
tessl plugin publish
```

Once you're confident in the new format, you can delete `tile.json` from your repository to remove the duplication.

***

## Running evaluations after migration

`tessl eval run` works the same way it did for tiles: point it at the package root. The only change is where that root is.

Before migration, you pointed at the tile directory (where `tile.json` lives):

```
tessl eval run <path-to-tile>
```

After migration, point at the plugin directory (where `.tessl-plugin/plugin.json` lives):

```
tessl eval run <path-to-plugin>
```

The plugin directory is the *parent* of `.tessl-plugin/`, not `.tessl-plugin/` itself. From a plugin root, this is usually just `.`:

```
tessl eval run .
```

***

## Quick reference

### tile.json → plugin.json field mapping

| tile.json                                  | plugin.json                                |
| ------------------------------------------ | ------------------------------------------ |
| `summary`                                  | `description`                              |
| `skills.X.path`                            | `skills` (directory or array)              |
| `rules.X.rules`                            | `rules` (directory or array)               |
| `steering.X.rules`                         | `rules` (directory or array)               |
| `commands.X.script`                        | `commands` (directory or array)            |
| `docs`                                     | No equivalent — move to skills or rules    |
| `describes`                                | No equivalent — contact us                 |
| `name`, `version`, `private`, `repository` | Identical                                  |
| —                                          | `description` (required at publish)        |
| —                                          | `author`, `homepage`, `license` (optional) |

### Minimum valid plugin.json

```json
{
  "name": "workspace/package-name",
  "version": "1.0.0",
  "description": "One sentence describing what this plugin does"
}
```

By convention, skills will be pulled from the `./skills` directory and rules from `./rules`. If they are not set, these conventions will be used.


# Understand your organization's context

How to connect an SCM account so Tessl scans your repositories and shows your Organization's Context and Findings.

Connect an SCM account and Tessl scans your repositories, indexes the skills and configuration it finds as **Context**, and surfaces **Findings** you can act on. This is the primary way to see what exists across your Organization.

***

SCM connections are managed at the Organization level. Connecting one needs the Organization admin role. Organization members and admins can view **Context** and **Findings**. See [Roles](/administrators/roles) for the full breakdown.

### Connect an SCM account

Tessl supports these source-control providers:

* **GitHub** (GitHub App)
* **GitLab** (access token)
* **Bitbucket**

**To connect:**

1. Open the Tessl web app.
2. Select your Organization from the dropdown at the top of the sidebar, then go to **Settings**, then **Integrations**.
3. Under **Source control**, select **Add** next to your provider and follow the prompts to authorize Tessl.

Connecting authorizes Tessl to read from the provider. Depending on provisioning availability, Tessl may then start a first scan of the repositories in scope. Scan provisioning is best-effort and separate from creating the connection, so a successful connection does not guarantee that a scan has started or finished. The connection page shows scan status. An Organization admin can retry a failed scan or set the **Scan scope** there.

### See your Context and Findings

Once a scan has run:

* Open **Context** in the sidebar to see the skills and configuration Tessl found across your connected repositories, including which repository each item lives in.
* Open **Findings** to see the issues Tessl surfaced, ranked by severity.
* **Home** gives you an overview across your Organization.

To control which repositories Tessl scans, open the connection under **Settings**, then **Integrations**, and set the **Scan scope**. For GitHub-specific setup, see [Connect Tessl to GitHub](/distribute/connect-tessl-to-github).


# Overview

Create a skill or a plugin - the packaged, versioned context you share with your team and your agents.

{% hint style="info" %}
**Takeaway** A skill teaches an agent how to do something. A plugin is how you package one or more skills, together with rules, commands, MCP servers, and hooks, so you can version and share them. Start with a skill; reach for a plugin when you need to bundle more than one thing.
{% endhint %}

A skill is a folder with a `SKILL.md` that teaches an agent how to do something. A plugin is the package that holds it: a `.tessl-plugin/plugin.json` manifest plus its content. Every skill you create with `tessl skill new` is already a minimal plugin, so "skill" and "single-skill plugin" are the same thing. You only think about plugins explicitly when you bundle more: several skills, a rule, a command, an MCP server, or a hook.

Whatever you create, you create it once. Tessl makes it work across every agent it supports, Claude Code, Cursor, Codex, and more, adapting it to each agent's own format on install. You never write a per-agent version.

The examples in this section use `engteam` as the workspace name. It is a stand-in: run `tessl workspace list` to see the workspaces you belong to, and use one of those in its place.

This section covers creating that context and getting it ready to share: writing a skill, bundling a plugin, adding an MCP server, testing locally, and publishing. It does not cover improving a skill's quality once written (see [Improving your skills](/improving-your-skills/overview-improving-skills-and-plugins)), enforcing a standard in CI (see [Codifying and enforcing your skill standards](/codifying-and-enforcing-your-skill-standards/overview)), or rolling context out across many repositories (see [Distribute](/distribute/distributing-via-registry)).

{% hint style="info" %}
All of this is also available through your agent. Install Tessl's plugin creator and it will do the steps below for you, from understanding the problem to building the plugin: `tessl install tessl/plugin-creator`
{% endhint %}

## In this section

|                                                                                   |                                                                              |
| --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| [Create a skill](/creating-skills-and-plugins/create-a-skill)                     | Write a `SKILL.md`, scaffold it, and validate it. The common starting point. |
| [Create a plugin](/creating-skills-and-plugins/create-a-plugin)                   | Bundle several skills, rules, and commands into one versioned package.       |
| [Add an MCP server](/creating-skills-and-plugins/add-an-mcp-server)               | Give a plugin live tools or data, not just instructions.                     |
| [Develop and test locally](/creating-skills-and-plugins/develop-and-test-locally) | Install a plugin from your filesystem and iterate before you publish.        |
| [Publish and update](/creating-skills-and-plugins/publish-and-update)             | Publish to the registry, control visibility, and ship new versions.          |

## Next

* [Create a skill](/creating-skills-and-plugins/create-a-skill) - the fastest way to package a single capability.
* [Create a plugin](/creating-skills-and-plugins/create-a-plugin) - when one skill is not enough.


# Create a skill

Scaffold a skill, write its SKILL.md, and validate it before you share it.

{% hint style="info" %}
**Takeaway** A skill is a folder with a `SKILL.md`, packaged as a minimal plugin so you can version and share it. Scaffold one with `tessl skill new`, write a clear `SKILL.md`, and validate it with `tessl skill lint` and `tessl review`.
{% endhint %}

A skill teaches an agent how to do one thing: a workflow, a convention, a piece of specialized knowledge. It is agent-agnostic, so the skill you write here works across every agent Tessl supports, adapted to each on install. This page covers scaffolding a skill, writing it, and checking it is well formed. Improving how well it performs is a separate step, covered in [Improving your skills](/improving-your-skills/overview-improving-skills-and-plugins), and sharing it is covered in [Publish and update](/creating-skills-and-plugins/publish-and-update).

{% hint style="info" %}
Your agent can do all of this for you. Install Tessl's plugin creator with `tessl install tessl/plugin-creator`, or use Tessl Agent, which ships with those skills built in.
{% endhint %}

## Prerequisites

* The Tessl CLI installed (see [Installation](/introduction-to-tessl/set-up-tessl/installation)).
* A logged-in session (`tessl login`), or a `TESSL_TOKEN` in the environment.
* A workspace to attribute your work to. Examples use `engteam` as a stand-in; run `tessl workspace list` to see yours and use one of those instead.

## Scaffold the skill

`tessl skill new` creates a new skill and its `.tessl-plugin/plugin.json` manifest. Run it with no flags for an interactive wizard:

```bash
tessl skill new
```

Or pass flags to scaffold it directly:

```bash
tessl skill new --name database-migration-helper --description "When you need to create and manage database migrations" --workspace engteam --path ./database-migration-helper
```

| Flag            | Purpose                                                                                                              |
| --------------- | -------------------------------------------------------------------------------------------------------------------- |
| `--name`        | Skill name, lowercase with hyphens.                                                                                  |
| `--description` | When the skill should trigger. This is what an agent reads to decide whether to load the skill, so make it specific. |
| `--workspace`   | Workspace for the skill. Defaults to local.                                                                          |
| `--path`        | Directory to create the skill in.                                                                                    |
| `--install`     | Install it into the current project immediately, so you can test it as you write.                                    |
| `--public`      | Make it public. Private by default.                                                                                  |

### Import an existing skill

If you already have a `SKILL.md`, `tessl skill import` generates the `.tessl-plugin/plugin.json` around it so it becomes an installable skill:

```bash
tessl skill import ./database-migration-helper --workspace engteam
```

Pass `--public` to make it public, or `--force` to overwrite an existing manifest.

## Write the SKILL.md

A skill follows the [Agent Skills specification](https://agentskills.io/specification). The file is named `SKILL.md`: YAML frontmatter, then a Markdown body.

```markdown
---
name: database-migration-helper
description: When you need to create and manage database migrations.
---

# Database migration helper

When creating a new migration:

1. Always include both `up` and `down` migrations.
2. Use a transaction where the engine supports it.
3. Test the migration on a copy of production data first.

## Example: adding a column

    -- Up
    ALTER TABLE users ADD COLUMN email_verified BOOLEAN DEFAULT FALSE;

    -- Down
    ALTER TABLE users DROP COLUMN email_verified;

## Best practices

- Never modify a migration that has already run in production.
- Keep migrations small and focused.
```

Two parts do the work:

* **`description`** (frontmatter) is how an agent discovers the skill. State what it does and when to use it. A vague description is the most common reason a skill never fires.
* **The body** is the instructions the agent follows once loaded. Keep it concise and actionable; push long reference material into a `references/` folder beside the `SKILL.md`.

## Understand the package structure

`tessl skill new` creates a Tessl package: your skill plus a manifest that makes it installable.

```
database-migration-helper/
├── .tessl-plugin/plugin.json
└── skills/database-migration-helper/SKILL.md
```

The manifest holds the package metadata:

```json
{
  "name": "engteam/database-migration-helper",
  "version": "1.0.0",
  "description": "Create and manage database migrations",
  "private": true
}
```

| Field         | Meaning                                                                                |
| ------------- | -------------------------------------------------------------------------------------- |
| `name`        | `workspace/name`. Its identity on the registry.                                        |
| `version`     | Semantic version ([semver](https://semver.org/)). Bump it on each release.             |
| `description` | Short summary shown on the registry.                                                   |
| `private`     | `true` keeps it to your workspace; public is irreversible once published.              |
| `skills`      | Path, or array of paths, to your skill directories. Defaults to `./skills` if omitted. |

You rarely edit this by hand, except to bump `version`. The full reference is in [Configuration files](/reference/configuration).

## Validate

Lint checks the structure and frontmatter against the Agent Skills specification:

```bash
tessl skill lint ./database-migration-helper
```

Fix any errors it reports before moving on. Then, for a scored review of how well the skill is written, run `tessl review` and read the result:

```bash
tessl review run ./database-migration-helper --workspace engteam
```

Reviewing and improving are covered in full in [Check a skill's quality using review](/improving-your-skills/reviewing-skills).

## Next

* [Create a plugin](/creating-skills-and-plugins/create-a-plugin) - bundle this skill with others, or add rules and an MCP server.
* [Prove a skill works using evaluation](/improving-your-skills/evaluate-skill-quality-using-scenarios) - measure whether it improves the agent's output.
* [Publish and update](/creating-skills-and-plugins/publish-and-update) - share it with your team or publicly.


# Create a plugin

Bundle skills, rules, and commands into one versioned plugin, and choose what each piece should be.

{% hint style="info" %}
**Takeaway** Reach for a plugin when a single skill is not enough: several related skills, an always-on rule, a command, or an MCP server that belong together. Scaffold it with `tessl plugin new`, add content under the plugin directory, and validate with `tessl plugin lint`.
{% endhint %}

A single skill is already a minimal plugin. You create a plugin explicitly when you want to bundle more than one thing and version and share it as a unit: a set of related skills, a rule that captures a convention, a command the user invokes, an MCP server, or a hook. Keep a plugin focused on one responsibility; unrelated work belongs in separate plugins. Whatever it contains, Tessl installs it into each agent's native format, so one plugin serves your whole team across every agent Tessl supports.

This page covers scaffolding a plugin and choosing what each part should be. Adding an MCP server has its own page, and publishing is covered in [Publish and update](/creating-skills-and-plugins/publish-and-update).

{% hint style="info" %}
Your agent can decide the right shape and build it for you. Install Tessl's plugin creator with `tessl install tessl/plugin-creator`, or use Tessl Agent, which ships with it built in.
{% endhint %}

## Scaffold the plugin

`tessl plugin new` creates the plugin directory and its `.tessl-plugin/plugin.json`. Pass `--skill` or `--rules` to seed initial content, and set the workspace explicitly:

```bash
tessl plugin new --name engteam/api-conventions --summary "Conventions and workflows for our REST APIs" --workspace engteam --skill --skill-name write-endpoint --skill-description "When adding a REST endpoint" --path ./api-conventions
```

`engteam` is a stand-in workspace. Run `tessl workspace list` and use one of yours in its place.

| Flag                                             | Purpose                                                                  |
| ------------------------------------------------ | ------------------------------------------------------------------------ |
| `--name`                                         | Plugin name, `workspace/plugin` format.                                  |
| `--summary`                                      | Short description of the plugin.                                         |
| `--workspace`                                    | Workspace for the plugin. Not inferred from `--name`, set it explicitly. |
| `--path`                                         | Directory to create the plugin in.                                       |
| `--skill`, `--skill-name`, `--skill-description` | Seed an initial skill.                                                   |
| `--rules`, `--rule-description`                  | Seed an initial rule file.                                               |
| `--install`                                      | Install into the current project immediately.                            |
| `--public`                                       | Make it public. Private by default.                                      |

## Anatomy of a plugin

A plugin is a directory with a manifest and one or more content types:

```
api-conventions/
├── .tessl-plugin/
│   └── plugin.json
├── skills/
│   └── write-endpoint/SKILL.md
├── rules/
│   └── api-style.md
├── commands/
│   └── new-migration.md
└── .mcp.json
```

The manifest declares what the plugin ships:

```json
{
  "name": "engteam/api-conventions",
  "version": "0.1.0",
  "description": "Conventions and workflows for our REST APIs",
  "private": true,
  "skills": "./skills/",
  "rules": "./rules/",
  "commands": "./commands/",
  "mcpServers": ".mcp.json"
}
```

`name`, `version`, and `description` are required. `skills` and `rules` each take a path or an array of paths and default to their conventional directory (`./skills/`, `./rules/`) when it exists; `commands` and `mcpServers` are declared explicitly. The full field reference, including `author`, `homepage`, `repository`, and `license`, is in [Configuration files](/reference/configuration).

## Choose what each piece should be

Match the behavior you want to the right content type. Add only what earns its place.

* **Skill** - a workflow the agent reaches for when a task matches its description. Most content is skills. See [Create a skill](/creating-skills-and-plugins/create-a-skill). To add a further skill to a plugin, author `skills/<name>/SKILL.md` directly.
* **Rule** - an always-on convention the agent follows without being asked. A plain Markdown file in `rules/`, no frontmatter. Use a rule when something should always hold ("errors return `problem+json`"), not just during one task.
* **Command** - a named action the user invokes on demand. A Markdown file in `commands/`. Use a command when the human wants a button to press rather than something the agent decides to do.
* **MCP server** - live tools or data, not just instructions. See [Add an MCP server](/creating-skills-and-plugins/add-an-mcp-server).

Hooks run a command automatically at an agent lifecycle event, such as after a tool runs or when a prompt is submitted. Declare them with a `hooks` (cross-agent) or `nativeHooks` (per-agent) field in `.tessl-plugin/plugin.json`; see [Configuration files](/reference/configuration) for the detail.

## Validate

Lint checks the plugin structure and manifest:

```bash
tessl plugin lint ./api-conventions
```

Confirm what will actually ship by packing it and inspecting the archive, not just linting:

```bash
tessl plugin pack ./api-conventions --output /tmp/check.tgz
tar tzf /tmp/check.tgz
```

## Next

* [Add an MCP server](/creating-skills-and-plugins/add-an-mcp-server) - give the plugin live tools or data.
* [Develop and test locally](/creating-skills-and-plugins/develop-and-test-locally) - install from your filesystem and iterate.
* [Publish and update](/creating-skills-and-plugins/publish-and-update) - share the plugin with your team or publicly.


# Add an MCP server

Ship an MCP server inside a plugin to give the agent live tools or data, not just instructions.

{% hint style="info" %}
**Takeaway** Bundle an MCP server in a plugin when the agent needs a live capability, a tool or a data source, rather than instructions. Declare it in a `.mcp.json` at the plugin root; on install, Tessl materializes it into each agent's native MCP config.
{% endhint %}

A skill gives an agent instructions. An MCP (Model Context Protocol) server gives it a tool or data at runtime: query an internal feature-flag service, look up your component library, fetch a live API schema from staging. Add one only when the plugin genuinely needs a capability the agent does not already have. If the job is "tell the agent how to do X", that is a skill, not an MCP server.

{% hint style="info" %}
This is also available through your agent: `tessl install tessl/plugin-creator`
{% endhint %}

## Declare the server

Point the manifest at a bundled `.mcp.json` file:

```json
{
  "mcpServers": ".mcp.json"
}
```

The value is the literal `".mcp.json"` or `"./.mcp.json"`. Declaring it is optional: the file is discovered by convention at the plugin root either way, so the field just lets you be explicit. The file declares the servers the plugin ships, in two transports:

```json
{
  "mcpServers": {
    "feature-flags": {
      "type": "http",
      "url": "https://flags.internal.engteam.example/mcp"
    },
    "schema-linter": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@engteam/schema-linter-mcp"]
    }
  }
}
```

* **`http`** - a remote server at a URL (http or https), with optional `headers`.
* **`stdio`** - a local server Tessl launches, with a `command` and optional `args` and `env`.

Do not hard-code secrets in `.mcp.json`. If a server needs credentials, reference them from the environment rather than committing them.

## What happens on install

Each agent configures MCP servers in its own way, and that is exactly the work Tessl does for you. When someone installs the plugin, Tessl reads the bundled `.mcp.json` and writes the servers into each agent's own MCP configuration, adapting the shape to that agent. A server declared once is available in Claude Code, Cursor, and other supported agents, without the author writing per-agent config.

## Validate

Lint the plugin, then confirm the servers materialize by installing into a throwaway project and checking the agent's MCP config:

```bash
tessl plugin lint ./api-conventions
tessl install file:./api-conventions
```

For the local development loop, see [Develop and test locally](/creating-skills-and-plugins/develop-and-test-locally).

## Next

* [Create a plugin](/creating-skills-and-plugins/create-a-plugin) - the plugin that holds the server.
* [Develop and test locally](/creating-skills-and-plugins/develop-and-test-locally) - install and confirm the server appears.
* [Publish and update](/creating-skills-and-plugins/publish-and-update) - share the plugin once the server works.


# Develop and test locally

Install a plugin straight from your filesystem so you can test and iterate before publishing.

{% hint style="info" %}
**Takeaway** Install a plugin from your filesystem with `tessl install file:./my-plugin`, test it with your agent, then edit and reinstall to iterate. Nothing is published until you choose to.
{% endhint %}

You usually want to test a plugin before it reaches the registry. Tessl installs plugins directly from your filesystem, so you can iterate on the content and confirm it works with your agent first. This is also how you build context that stays with one codebase, or plugins for different parts of a monorepo.

{% hint style="info" %}
This whole loop is also handled by your agent: `tessl install tessl/plugin-creator`
{% endhint %}

## The loop

### 1. Create

Scaffold a plugin, seeding a first skill:

```bash
tessl plugin new --name engteam/my-plugin --workspace engteam --skill --skill-name my-skill --skill-description "When ..." --path ./my-plugin
```

`engteam` is a stand-in workspace; run `tessl workspace list` and use one of yours.

Add any further skills by authoring `my-plugin/skills/<name>/SKILL.md` directly. Use `tessl skill new` only for a standalone skill, not inside a plugin directory, where it would nest a separate package.

### 2. Install from the filesystem

```bash
tessl install file:./my-plugin
```

The plugin is now available to your agent, exactly as an installed one would be.

### 3. Test with your agent

* **Skills**: trigger the skill and confirm it runs and produces what you expect.
* **Rules**: ask the agent to write some code and check it follows the rule.
* **MCP servers**: confirm the server appears in the agent's tools.

### 4. Validate

Lint the structure, then pack it and inspect the archive to confirm every intended file ships. Lint alone can pass on a plugin that would drop content when packaged, so check both:

```bash
tessl plugin lint ./my-plugin
tessl plugin pack ./my-plugin --output /tmp/check.tgz
tar tzf /tmp/check.tgz
```

### 5. Iterate

Edit the content, reinstall, and test again. Repeat until it behaves.

## Plugin structure

```
<plugin-dir>/
├── .tessl-plugin/
│   └── plugin.json
├── skills/<skill-dir>/SKILL.md
├── rules/
│   └── standards.md
└── evals/
    └── scenario-1/
        ├── task.md
        ├── criteria.json
        └── scenario.json
```

* `.tessl-plugin/plugin.json` points at your content. If a skill sits at the plugin root use `"skills": ["."]`; if it sits under `skills/<name>` use `"skills": ["./skills/<name>"]`, or rely on the `./skills/` convention.
* `evals/` is present only once you have generated [scenarios](/improving-your-skills/overview-improving-skills-and-plugins).

The complete `.tessl-plugin/plugin.json` reference is in [Configuration files](/reference/configuration).

## Next

* [Publish and update](/creating-skills-and-plugins/publish-and-update) - share the plugin once it works locally.
* [Improving your skills](/improving-your-skills/overview-improving-skills-and-plugins) - prove and improve quality before you rely on it.


# Publish and update

Publish a skill or plugin to the registry, control who can install it, and ship new versions.

{% hint style="info" %}
**Takeaway** Publish a skill or plugin with `tessl skill publish` or `tessl plugin publish`. It goes to your workspace as private by default. Bump the version to ship an update, and request public visibility only when you want anyone to install it.
{% endhint %}

Publishing puts a skill or plugin on the [Tessl Registry](https://tessl.io/registry) so it can be installed by name. This page covers the publish command, visibility, and versioning. Rolling context out across many repositories, and the GitHub-based publishing flow, are covered in [Distribute](/distribute/distributing-via-registry).

{% hint style="info" %}
Your agent can run this step too: `tessl install tessl/plugin-creator`
{% endhint %}

## Publish

From the skill or plugin directory, or with a path:

```bash
tessl plugin publish ./api-conventions --workspace engteam
```

`engteam` is a stand-in workspace. Run `tessl workspace list` and use one of yours.

A single skill uses the same flow through `tessl skill publish`. Both publish to your workspace as **private**: only workspace members can install it.

| Flag                         | Purpose                                                                           |
| ---------------------------- | --------------------------------------------------------------------------------- |
| `--workspace`                | Workspace to publish under. Required when non-interactive.                        |
| `--dry-run`                  | Run every pre-publish check without publishing. Use it first.                     |
| `--bump patch\|minor\|major` | Bump the version automatically if the current one already exists on the registry. |
| `--skip-evals`               | Skip publishing the eval scenarios in `evals/`.                                   |
| `--version`                  | Publish a specific version (plugin publish).                                      |

On publish, Tessl lints and reviews the content automatically, and the review score appears on the registry. See [Check a skill's quality using review](/improving-your-skills/reviewing-skills).

## Publication checks and blocked versions

An accepted upload is not necessarily available to install yet. Tessl runs publication checks asynchronously after the upload. The publish command tells you whether the version is available, blocked, still being checked, or could not be checked.

Check the exact version that you uploaded:

```bash
$ tessl plugin info engteam/api-conventions@1.2.0
```

If your organization or workspace enables a minimum skill review score policy, every skill in the plugin version must meet that score. Tessl does not average scores across the skills. For example, with a minimum score of 80, scores of 91 and 78 block the version, while scores of 91 and 85 pass this check. A score equal to the minimum passes.

The version must also pass every other publication check that applies to it. A version is unavailable while blocked and cannot be overwritten. Use `tessl plugin info` to check its publication status and the reported reason. For a score below the threshold, open the exact version in the Tessl Registry, select **Quality**, and use the per-skill findings to improve the failing skill. Then bump the version and publish the higher version.

If the publication or moderation status is pending and the version is unavailable, wait and check the same version again. An available version can still have quality or security reviews running in the background; those reviews do not delay its use.

If a required review did not run because credits were unavailable, restore credits or ask your organization administrator for help, then publish a higher version. Restoring credits does not automatically retry the review for the existing version. If a review completed without a score, contact Tessl support before publishing a higher version. These outcomes do not mean that the skill itself needs improvement. For other system errors, contact Tessl support.

Versions blocked by policy are not available for distribution, including Claude marketplace sync. The policy provides a publish-time quality gate without requiring a CI step.

## Install it

Once published, you or your workspace can install it in any project:

```bash
tessl install engteam/api-conventions
tessl install engteam/api-conventions@1.2.0
```

To let teammates install a private plugin, add them to the workspace with `tessl workspace add-member`. See [Workspaces](/reference/workspaces).

## Ship an update

1. Make your changes.
2. Bump `version` in `.tessl-plugin/plugin.json` following [semantic versioning](https://semver.org/): patch for fixes, minor for backward-compatible additions, major for breaking changes.
3. Validate with `tessl plugin lint`.
4. Publish again.

```bash
tessl plugin publish ./api-conventions --workspace engteam
```

Anyone who installed it updates by running `tessl install engteam/api-conventions` again, which fetches the latest version.

## Make it public

Public means anyone can discover and install it. It is irreversible, so it needs approval first:

1. Open your plugin or skill in the [Tessl Registry](https://tessl.io/registry) and sign in.
2. Select **Actions** then **Make Public**, and confirm the request.

Once approved, set the visibility and publish again:

* **A plugin** takes its visibility from the `private` field in `.tessl-plugin/plugin.json`. Set `"private": false` and publish.
* **A skill** can be published public directly with `--public`:

```bash
tessl skill publish ./my-skill --workspace engteam --public
```

See [Sharing with your team and publicly](/distribute/sharing-plugins-publicly) for more.

## Remove or deprecate

Published something you should not have, or no longer want it installed? There is no hard delete, but two options cover it. Both use `tessl plugin` whether you published a skill or a plugin.

To drop one skill from a plugin that contains others, delete the skill file and publish a new version with `tessl plugin publish`. The old version remains on the registry; the new version simply omits the skill.

* **Unpublish** removes a version as if it was never published. It has a short time limit, a couple of days after publishing:

```bash
tessl plugin unpublish --plugin engteam/api-conventions@0.1.0
```

* **Archive** hides it from search but keeps it working for anyone who already references it, so existing installs do not break:

```bash
tessl plugin archive --plugin engteam/api-conventions --reason "superseded"
```

To simply stop new people finding it, you can also make it private again: set `"private": true` in the manifest and republish, or change visibility from the registry UI.

## Next

* [Distributing via registry](/distribute/distributing-via-registry) - archive, unpublish, and monitor usage.
* [Distribute plugins across multiple repositories](/distribute/distribute-plugins-across-multiple-repositories) - push plugins across many repos.
* [Improving your skills](/improving-your-skills/overview-improving-skills-and-plugins) - raise quality before you publish widely.


# Overview

{% hint style="info" %}
**Takeaway** Know whether a skill is any good, and make it better. Use a review for fast feedback on the skill itself. Run an evaluation when you need proof it changes how an agent behaves, for example before you share it with your team.
{% endhint %}

This page is the place to start if you want to improve a skill. It walks through the two ways Tessl assesses skill quality, review and evaluation, and how you use each to make a skill better.

A review and an evaluation answer different questions.

|                |                                                                                                                                                                                                                                                                     |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Review**     | Reads the skill itself and scores how well it is written: whether the description is clear enough for an agent to load it at the right time, and whether the content is specific, actionable, and complete. Runs in under a minute and needs nothing but the skill. |
| **Evaluation** | Runs an agent on real tasks with and without the skill, then scores the difference. Tells you whether the skill actually changes what the agent does, not just whether it reads well. Takes longer and runs against a set of scenarios.                             |

## Building trust in a skill

Both reviews and evaluations help you assess different aspects of skill quality and guide improvement. In practice it helps to treat them as different levels of rigour, applied according to how much trust you need to build in the skill.

For example, if you are building a skill for yourself to use day to day, and you make quick ad hoc improvements as you go, a review may be enough. You will notice when a skill misfires and fix it, using review to guide your improvements.

If you are putting the skill into a shared repository to shape how other people's agents behave, you may decide this needs an extra level of rigour. An evaluation gets ahead of that: it proves the skill does what it claims, and the scenarios you generate are saved with the skill, so anyone who edits it later can re-run them and confirm it still works. They become a regression test for the skill.

Over the following pages we walk through both review and evaluation in depth.

## Quick start: Skill Optimizer

Alternatively, Tessl's [Skill Optimizer plugin](https://tessl.io/registry/tessl/skill-optimizer) runs this whole progression for you. It includes both review and evaluation and applies them as needed. Install it, point your agent at a skill, and it reviews the skill, applies quick win improvements, and escalates into evaluation when the skill is worth testing harder, generating evaluation scenarios, diagnosing failures, and re-running until the scores improve.

```bash
tessl install tessl/skill-optimizer
```

Then ask your agent to optimise the skill. Use Skill Optimizer when you want the full cycle handled for you. Use the commands directly, covered in the next pages, when you want control over each step.

## Next

* [Check a skill's quality using review](/improving-your-skills/reviewing-skills) - run a review, read the score, and apply fixes with `tessl review`.
* [Prove a skill works using evaluation](/improving-your-skills/evaluate-skill-quality-using-scenarios) - generate scenarios and measure a skill's impact with `tessl eval`.
* To enforce a quality bar across your organisation, for example gating pull requests on a review score or scoring against your own standards, see [Codifying and enforcing your skill standards](/codifying-and-enforcing-your-skill-standards/overview).


# Check a skill's quality using review

Run a review to score how well a skill is written, read the result, and apply fixes.

{% hint style="info" %}
**Takeaway** Run a review to score how well a skill is written, read the result, and apply fixes. A review checks the skill itself in under a minute. It does not run the skill against any task.
{% endhint %}

`tessl review` reads a skill and scores it against Tessl's standard rubric. Use it for fast feedback while you are writing a skill, or to check one before you rely on it. At a high level it tells you whether the skill is well structured and formatted, and whether its description and content are clear. It does not tell you whether the skill improves what the agent produces; for that, see [Prove a skill works using evaluation](/improving-your-skills/evaluate-skill-quality-using-scenarios).

A review runs through an agent that reads your whole skill bundle, not just `SKILL.md`. References, scripts, and assets are all in scope. It runs server-side: `tessl review run` polls until it finishes and prints the score, so a single command feels synchronous.

{% hint style="info" %}
All the steps on this page are also available through your agent. Install Tessl's Skill Optimizer to run reviews, apply fixes, and check scores without leaving your editor: `tessl install tessl/skill-optimizer`
{% endhint %}

{% hint style="info" %}
`tessl skill review` has been removed; `tessl review` replaces it. See [Migrate from skill review](/improving-your-skills/migrate-from-skill-review).
{% endhint %}

## Run a review

```bash
tessl review run ./my-skill --workspace engteam
```

The command packs the skill, uploads it, runs the review pipeline, and prints the score when it finishes. Pressing Ctrl+C detaches from the run; the review continues server-side and you can check on it later.

`tessl review run` scores quality. To scan a skill for security issues instead, run `tessl review run security ./my-skill --workspace engteam` — see [Protect against insecure skills](/tutorials/protecting-against-insecure-skills).

```bash
# View the most recent run
tessl review view --last

# View a specific run by ID
tessl review view <run-id>

# List recent runs
tessl review list

# Re-run a failed review
tessl review retry --last
```

## Read the score

A review produces an overall score from 0 to 100, a weighted average of three components:

|                 |                                                                                                                                                                                                                 |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Validation**  | Deterministic checks that the skill follows the [Agent Skills specification](https://agentskills.io/specification): line count, frontmatter, schema, licence, and metadata. Each check passes, warns, or fails. |
| **Description** | An LLM judge on the description, scoring how likely an agent is to load the skill at the right time. Graded on specificity, completeness, trigger term quality, and distinctiveness.                            |
| **Content**     | An LLM judge on the `SKILL.md` body, scoring how well it guides the agent once loaded. Graded on conciseness, actionability, workflow clarity, and progressive disclosure.                                      |

Each component comes with specific recommendations, for example tightening a vague description or adding trigger terms a user would naturally say. As a rough guide:

* **90% and above**: the skill conforms well to best practices.
* **70% to 89%**: a good skill, with minor improvements available.
* **Below 70%**: likely needs work before you rely on it or share it.

Example output:

```
Validation Checks

  ✔ skill_md_line_count - SKILL.md line count is 9 (<= 500)
  ✔ frontmatter_valid - YAML frontmatter is valid
  ✔ name_field - 'name' field is valid: 'format-commit-messages'
  ✔ description_field - 'description' field is valid (70 chars)
  ✔ body_present - SKILL.md body is present

Overall: PASSED (0 errors, 0 warnings)

Judge Evaluation

  Content: 85%
    A tightly written, token-efficient body that gives clear and unambiguous
    guidance for a simple task. Adding one complete worked commit-message
    example would lift actionability from good to fully concrete.

    Suggestions:
      - Add a short worked example showing a complete commit message to
        make the guidance copy-paste concrete.

  Description: 90%
    A concise, well-triggered description that clearly states both capability
    and usage conditions with natural user-facing language.

Review Score: 90%
```

## Apply fixes

`tessl review fix` runs an improve-and-review loop: it reviews the skill, applies improvements, and re-reviews. The loop runs up to `--max-iterations` times (default 3, maximum 10). It stops early once the score reaches the `--threshold` target (default 90). The highest-scoring version wins, so if no iteration improves on the original skill, no changes are applied.

```bash
tessl review fix ./my-skill --workspace engteam
```

It prompts before applying changes, since the loop edits the skill content directly. Pass `--yes` (or `-y`) to apply without a prompt, which is useful in CI or batch workflows. Review the changes before you publish.

## Score against your own standards

By default a review scores against Tessl's standard rubric, based on Anthropic's best practices. To score against your organisation's own bar instead, with your own judges and weights, run the review against a custom reviewer plugin. Pass a local directory or a `workspace/plugin` registry reference with `--review-plugin`:

```bash
tessl review run ./my-skill --workspace engteam --review-plugin <reviewer-plugin>
```

The same `--review-plugin` flag works with `tessl review fix`. To build a reviewer, see [Define your own quality standards](/codifying-and-enforcing-your-skill-standards/define-your-own-quality-standards).

## Gate a pull request on a review score

You can run the same review in CI and fail the build when the score is too low. Combine `--json` with `--threshold`:

```bash
tessl review run ./my-skill --workspace engteam --json --threshold 80
```

The command exits non-zero when the score is below the threshold, so the CI step fails. For a complete GitHub Actions workflow, see [Gate skill quality in CI](/codifying-and-enforcing-your-skill-standards/gate-skill-quality-in-ci).

## Reviews when you publish

You do not have to run a review by hand. When you publish a plugin to the registry, Tessl lints and reviews it automatically, and the score appears on the registry.

An organization or workspace can also require a minimum review score before a plugin version is available. Every skill in the version must meet the threshold; scores are not averaged. If a version is blocked, inspect that exact version, use the review findings to improve the failing skill, then publish a higher version. See [Publication checks and blocked versions](/creating-skills-and-plugins/publish-and-update#publication-checks-and-blocked-versions) and [Codifying and enforcing your company's skill standards](/tutorials/codifying-and-enforcing-skill-standards).

## Prerequisites

`tessl review run` and `tessl review fix` need authentication and a workspace. The companion commands `view`, `list`, and `retry` need authentication only.

* Authentication, either a logged-in session, confirmed with `tessl whoami` and started with `tessl login`, or a `TESSL_TOKEN` API key in the environment. Any workspace member can run a review.
* A workspace to attribute the run to, passed with `--workspace`. It is required when `--json` is set, because a non-interactive run cannot prompt for one.

## Next

* [Prove a skill works using evaluation](/improving-your-skills/evaluate-skill-quality-using-scenarios) - measure whether a skill changes an agent's output, not just how it reads.
* [Codifying and enforcing your skill standards](/codifying-and-enforcing-your-skill-standards/overview) - build a custom rubric, set an organisation-wide quality bar, and gate pull requests on it.


# Migrate from skill review to Tessl Review

How to switch from the removed tessl skill review to Tessl Review, and what to expect

Tessl Skill Review let you quickly evaluate the quality of a skill against best practices, providing scores on validation, content and description quality. It has been superseded by Tessl Review.

This page explains what changed, the impact on how skill quality is scored, and how to switch.

{% hint style="warning" %}
**`tessl skill review` has been removed.** Running it now fails with a message pointing here. Use `tessl review run` instead. The mapping below covers every flag, including the one flag that has no replacement yet.
{% endhint %}

## What changed, and why

Tessl Review brings skill review into Tessl's wider agentic evaluation architecture.

Tessl Skill Review evaluated individual `SKILL.md` files in a single pass against a fixed rubric. With Tessl Review, the review runs through an agent that works across the whole skill and its connected files, and reasons over multiple steps to reach its result.

This allows us to introduce several improvements:

* **Full-bundle review.** The review reads your entire skill, not just `SKILL.md`. References, scripts, and assets are all in scope, so nothing in your skill is missed.
* **Customisable rubric.** Fork the default quality scoring rubric and define what "good" means for your organisation. Set it as your workspace default and every review uses your standard.
* **Review history.** Reviews are stored and queryable with `tessl review list` and `tessl review view`, so you have a persistent record of quality over time.
* **Built for CI.** `--json` and `--threshold` gate pull requests on a review score, exactly as before.

{% hint style="info" %}
**Scores returned by Tessl Review may differ slightly from skill review.** Because the review now runs as an agent over your full skill bundle rather than a single pass, Tessl Review scores are more accurate and more reflective of real skill quality. And because the agent reasons over multiple steps, it can reach a clearer, better-founded conclusion than skill review's single pass.
{% endhint %}

The shift to Tessl Review also makes it easier for us to expand what reviews can do. You can now run a security scan directly from the CLI with `tessl review run security` — see [Protect against insecure skills](/tutorials/protecting-against-insecure-skills). Coming soon:

* Choose the model a review runs on.

## What changes on the CLI

{% hint style="info" %}
**Reviews require authentication.** `tessl review run` requires you to be signed in. Sign in with `tessl login`, or set a `TESSL_TOKEN` API key in CI.
{% endhint %}

Most workflows map across directly:

| Workflow                           | Removed (`tessl skill review`)             | New (Tessl Review)                                |
| ---------------------------------- | ------------------------------------------ | ------------------------------------------------- |
| Run a review                       | `tessl skill review <path>`                | `tessl review run <path>`                         |
| Review one skill from a plugin     | `tessl skill review --skill <name> <path>` | Point `tessl review run` at the skill's directory |
| Improve a skill automatically      | `tessl skill review --optimize`            | `tessl review fix`                                |
| Gate CI on a score                 | `--threshold <score>`                      | `--threshold <score>` (unchanged)                 |
| Output machine-readable results    | `--json`                                   | `--json` (unchanged)                              |
| Limit improvement iterations       | `--max-iterations` (with `--optimize`)     | `--max-iterations` (on `tessl review fix`)        |
| Auto-apply fixes without prompting | `--yes` (with `--optimize`)                | `--yes` (on `tessl review fix`)                   |
| Attribute a run to a workspace     | -                                          | `--workspace` (new)                               |
| Score against a custom rubric      | -                                          | `--review-plugin` (new)                           |
| Label a run for history            | -                                          | `--label` (new)                                   |
| Force a fresh run                  | -                                          | `--force` (new)                                   |
| Choose the review model            | `--model <name>`                           | No replacement yet, see Coming soon above         |

What stays the same:

* The default rubric (Anthropic best practices) is unchanged. Reviews still score the same three components out of the box: validation, description, and content.
* Reviews still produce a 0-100 score.
* `--json` with `--threshold` still gates CI through the exit code.
* The GitHub Action still drives reviews in CI.

What's new to know:

* **Authentication.** `tessl review run` and `tessl review fix` run against your workspace, so you need to be signed in (`tessl login`, or a `TESSL_TOKEN` API key in CI) and run against a workspace (pass `--workspace`, or pick one when prompted). Any workspace member can run reviews.
* **Asynchronous by default.** A review runs server-side. `tessl review run` polls until it finishes and then prints the score, so a single command still feels the same. Press Ctrl+C to detach and pick the run back up later with `tessl review view --last`.

## Migrating your CI

Replace the `tessl skill review` step with `tessl review run`, add a workspace, and keep your threshold:

```sh
tessl review run ./my-skill --workspace <workspace> --json --threshold 80
```

`--workspace` is required when `--json` is set, because a non-interactive run cannot prompt for one. In CI, authenticate with a `TESSL_TOKEN` repository secret. For a complete GitHub Actions workflow, see [Review, lint & publish with GitHub Actions](/distribute/review-and-publish-with-github-actions).

## Next steps

* [Check a skill's quality using review](/improving-your-skills/reviewing-skills) - the full guide to running reviews, customising the rubric, and gating CI
* [`tessl review run` reference](/reference/cli-commands#tessl-review-run) - all flags
* [Review, lint & publish with GitHub Actions](/distribute/review-and-publish-with-github-actions) - run reviews on pull requests


# Prove a skill works using evaluation

Generate scenarios, run an evaluation, and read the score difference to see whether a skill actually changes what an agent produces.

{% hint style="info" %}
**Takeaway** Generate scenarios from a skill, run an evaluation, and read the score difference to see whether the skill actually changes what an agent produces. Scenarios are saved with the skill, so they become a regression test you re-run after every change.
{% endhint %}

By default, an evaluation runs an agent on real tasks twice, once without the skill and once with it, then scores the difference. You can also declare your own variants to compare several configurations on the same tasks. That difference is the skill's value: proof it changes what the agent does, not just that it reads well. Where a review checks the skill itself, an evaluation measures its effect.

Evaluation is more rigorous than review and takes longer to set up and run. Reach for it when you need confidence a skill works, for example before you publish it or share it into a repository where you will not see how others' agents use it.

You can evaluate a single skill against generated scenarios, or evaluate context files against real tasks taken from your codebase's commit history. Both run through the same `tessl eval` commands.

{% hint style="info" %}
All the steps on this page are also available through your agent. Install Tessl's Skill Optimizer to generate scenarios, run evaluations, and improve skills without leaving your editor: `tessl install tessl/skill-optimizer`
{% endhint %}

## Skills are evaluated inside Tessl plugins

A Tessl plugin is a structure that bundles the skills, rules, and documentation an agent needs for an area of context, packaged so it can be versioned and shared. An evaluation generates scenarios and produces results, and a plugin is the natural place to keep them with the skill they belong to.

For this reason `tessl eval run` expects the generated scenarios to live inside the plugin, and the shorthand `tessl eval run ./my-skill` expects a directory containing a `.tessl-plugin/plugin.json`. If you have a standalone skill, import it into a plugin first:

```bash
tessl skill import ./my-skill
```

## Generate scenarios

Scenarios are the tasks an agent is assessed against. Generate them from the plugin:

```bash
tessl scenario generate ./my-skill
```

Each generated scenario is put through a feasibility check on the server. Only scenarios that pass are saved, so the generator gives you tests that actually exercise the skill rather than filler.

`--count` sets how many scenarios to target, and defaults to 3. Pass a higher number to cover more of what the skill claims to do. For a narrowly scoped skill the generator may return fewer than requested, because it will not save scenarios that fail the feasibility check.

```bash
tessl scenario generate ./my-skill --count 5
```

Generation runs server-side. Check progress with `tessl scenario list --mine`, then download the scenarios once it completes:

```bash
tessl scenario download --last
```

When run from inside a plugin directory (or any subdirectory of one), `tessl scenario download` writes scenarios directly into the plugin's `evals/` directory, so they are immediately runnable by `tessl eval run`. When run outside a plugin, it writes to an `evals/` directory relative to your current directory. Override the location with `--output`, and choose whether new scenarios merge with or replace existing ones with `--strategy merge` (the default) or `--strategy replace`. The command always prints where the files landed.

{% hint style="warning" %}
**The default destination is the plugin's `evals/`, wherever you run from inside the plugin.** Two consequences follow from this:

* `--strategy replace` clears the plugin's `evals/` directory before writing, even when you run from a subdirectory such as `src/`. It does not target `src/evals`. Pass `--output <dir>` to replace a different directory.
* `--strategy merge` (the default) overwrites existing scenario files with the same name. Scenario names are canonical (`scenario-1/`, `scenario-2/`, …), so a re-download replaces `scenario-1/` and any hand-edits in it. Copy edited scenarios you want to keep to a directory the download will not touch, or download with `--output` to a separate directory.
  {% endhint %}

The scenarios are saved with the skill, so anyone who changes it later can re-run the same scenarios and confirm it still behaves as expected.

You do not have to generate scenarios. You or your agent can write them by hand instead, as long as they sit in the plugin's `evals/` directory, one scenario per subdirectory. See [File formats](#file-formats) for the structure.

### Generate scenarios from your codebase

Instead of generating scenarios from a skill, you can build them from real changes in your repository, so the tasks reflect work your team actually does. Point `tessl scenario generate` at a repository with commit hashes or PR numbers:

```bash
# From commits
tessl scenario generate org/repo --commits abc123,def456 --workspace engteam

# From pull requests
tessl scenario generate org/repo --prs 42,107 --workspace engteam
```

Tessl analyses each diff and reconstructs the intent of the change as a task brief and a scoring rubric. The starting state has your context files stripped out, so the agent solves the task without them.

`--context` tells Tessl which files are context files. The patterns are saved in each scenario as the codebase fixture's exclude list. When omitted, Tessl excludes `*.mdc`, `*.md`, `tile.json`, `.tessl-plugin/plugin.json`, `tessl.json`, and `.tessl/` by default.

To browse recent commits and choose which to turn into scenarios, use `tessl repo select-commits org/repo`. It accepts `--keyword`, `--author`, `--since` / `--until`, and `--count`, and prints a table of commit hashes to pass to `generate`.

{% hint style="info" %}
Your GitHub or GitLab account must be connected in workspace settings.
{% endhint %}

Because these scenarios start from a state with your context files stripped out, pass `--context` again at run time with a glob matching those files. This adds a with-context variant on top of the baseline so you can measure the delta:

```bash
tessl eval run ./my-skill/evals/ --context 'src/**/*.knowledge.md'
```

Match the `--context` glob to the files each scenario excludes. With no `--context`, only the baseline runs.

## Run an evaluation

```bash
tessl eval run ./my-skill
```

By default, the agent solves each scenario twice, without the skill and with it, and a judge scores both against a per-scenario rubric. A run with explicit variants solves the same scenarios for each configuration instead. The run reports each score and any applicable difference. Runs can take a while; pressing Ctrl+C detaches and the run continues server-side.

Label a run to find it again later:

```bash
tessl eval run ./my-skill --label "baseline"
```

```bash
# List your recent runs
tessl eval list --mine

# View a run, including the per-scenario breakdown
tessl eval view --last
tessl eval view abc123

# Retry a failed run
tessl eval retry abc123
```

## Read the result

A run produces output like this:

```
Eval Results: baseline

Agent:     claude:deepseek-v4.1-flash
Variants:  Without this plugin, With this plugin
Status:    Completed

Scenario: Commit message formatting for complex refactor

  Without this plugin
  Check                         Score
  ------------------------------------------
  Conventional Commits format   30/30  (100%)
  Valid type                    0/20   (0%)
  Subject line <= 72 chars      0/30   (0%)
  Blank line separator          20/20  (100%)
  Total                         50/100  (50%)

  With this plugin
  Check                         Score
  ------------------------------------------
  Conventional Commits format   30/30  (100%)
  Valid type                    20/20  (100%)
  Subject line <= 72 chars      30/30  (100%)
  Blank line separator          20/20  (100%)
  Total                         100/100  (100%)

Scenario: Type selection for a small fix

  Without this plugin
  Total                         90/100  (90%)

  With this plugin
  Total                         95/100  (95%)

Summary
  Scenarios: 3 completed
  Without this plugin avg:  71%
  With this plugin avg:     92%
```

The score difference tells you what the skill is worth:

* **A large positive difference** means the skill reliably moves the agent toward the right behaviour.
* **Little or no difference** means the agent already did the task well without the skill, or the skill is not landing.
* **A small negative difference** is usually normal model variance, not evidence the skill is harmful.
* **A large or consistent negative difference** is a real sign the skill is confusing the agent or pushing it toward unhelpful behaviour, and the content needs work.

Read the per-scenario breakdown rather than just the headline average, since a strong result on one scenario can hide a weak one on another.

### Outcome and activation

A standard evaluation measures **outcome**: with the context forced on, does the agent produce better work against the rubric. It does not test whether the agent would have loaded the skill on its own.

To measure **activation**, whether the agent picks up the skill itself without being told to, add `--skip-forced-context-activation`. A skill can score well on outcome but fail to activate, which means it would not help in practice because the agent never reaches for it.

```bash
# Measure activation only: don't force the context, and skip outcome scoring
tessl eval run ./my-skill --skip-forced-context-activation --skip-scoring
```

The two flags are independent: `--skip-forced-context-activation` stops forcing the context so activation can be observed, and `--skip-scoring` skips rubric scoring. Used together they give a pure activation check.

Example activation output:

```
Activation Results

Agent:     claude:deepseek-v4.1-flash
Variants:  baseline, with context
Status:    Completed

Injected context
Plugin: format-commit-messages@0.1.0

Scenario                                            Activated skills
--------------------------------------------------------------------
1. Conventional Commits format and type selection   format-commit-messages
2. Commit message formatting for complex refactor   format-commit-messages
3. Conventional Commits with multi-line message     —
```

`—` in the Activated skills column means the skill was not selected by the agent on its own. A skill that does not activate is a description problem, best fixed with a [review](/improving-your-skills/reviewing-skills). A skill that activates but does not improve the outcome is a content problem to fix in the skill body.

## Comparing test variants

A test variant is one configuration in an evaluation. Every variant solves the same scenarios and is scored against the same rubric, so you can compare models, agents, fixture combinations, or revisions of a plugin.

Declare variants with `--variant-json`. Give the flag a JSON array or the path to a JSON file:

```bash
$ tessl eval run ./my-skill --variant-json ./variants.json
```

The run solves only the variants you list. It does not add a baseline for you, so include one in the list when you want to compare against it (see [Compare a plugin with no plugin](#compare-a-plugin-with-no-plugin)). `--variant-json` cannot be combined with `--skip-baseline` or `--arms-json`. A JSON file can be up to 1 MiB.

`--arms-json` is deprecated but still works. Use `--variant-json` for new runs.

Each variant needs a unique label. A label uses lowercase letters, digits, hyphens, and underscores, starts with a letter or digit, and is at most 64 characters, so a model name such as `kimi-k2.6` needs a label without the dot. Fields you omit use the run-level agent, model, and fixture setup. The bare string `"baseline"` is the exception: it removes the run-level subject fixture. An object variant, including `{ "label": "baseline" }`, uses the ordinary object rules and includes that fixture.

```json
[
  { "label": "opus", "model": "claude-opus-5" },
  { "label": "sonnet", "model": "claude-sonnet-5" },
  { "label": "haiku", "model": "claude-haiku-4-5" }
]
```

### Variant spec reference

| Field           | Type         | Default         | What it does                                                                                                                                  |
| --------------- | ------------ | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `label`         | string       | required        | Names the variant. Labels are unique within a run and appear with the results.                                                                |
| `agent`         | string       | the run's agent | Uses a different agent for this variant.                                                                                                      |
| `model`         | string       | the run's model | Uses a different model for this variant.                                                                                                      |
| `fixtures`      | object       | inherits        | Replaces, adds, or removes named fixtures for this variant. A value replaces or adds a fixture, `null` removes it, and omitted names inherit. |
| `agentMustRead` | string array | none            | Names fixtures whose installed locations the agent must read before starting the task.                                                        |

`agentMustRead` contains fixture names, not paths or file content. For a commit or directory fixture, the agent is told that fixture's `installPath`. For a plugin fixture, the agent is told the locations of the installed skills. A name must identify a fixture available to that variant after its overlay has been applied.

For example, this scenario supplies a commit fixture at the repository root and excludes draft instructions:

```json
{
  "fixtures": {
    "repository": {
      "type": "commit",
      "repoUrl": "https://github.com/org/repo.git",
      "ref": "abc1234",
      "installPath": ".",
      "exclude": ["docs/drafts/**"]
    }
  }
}
```

The variant adds a directory fixture named `guidance` and names both fixtures in `agentMustRead`. The agent is told where the repository and guidance fixtures were installed.

The CLI packages the contents of a `localPath` fixture and uploads them for the evaluation. The evaluation agent can access that uploaded content, so point `localPath` only at files you intend to share.

```json
[
  {
    "label": "with-guidance",
    "fixtures": {
      "guidance": { "localPath": "./variants/guidance" }
    },
    "agentMustRead": ["repository", "guidance"]
  }
]
```

### Fixture values in a variant

A fixture value in a variant takes one of two forms:

* `{ "localPath": "./path" }` for content on your disk. The path is relative to the directory you run the command from. A plugin directory is installed as a plugin. Any other directory is copied into the solution at its path relative to your repository root. `localPath` must be the only key in the object. The CLI uploads each distinct path once.
* A published plugin, declared by name and version. The run resolves it from the registry, so nothing is uploaded:

```json
{
  "label": "published",
  "fixtures": {
    "context": {
      "definition": { "type": "plugin", "name": "engteam/my-skill", "version": "1.2.0" }
    }
  }
}
```

Other fixture types need uploaded content, so declare them with `localPath` and let the CLI upload them. A `localPath` that resolves outside your Git repository (or outside the source directory, when it is not in a repository) is refused unless you pass `--allow-unsafe-fixture-paths`.

### Compare a plugin with no plugin

Passing `./my-skill` already supplies the run-level subject fixture. The bare string `"baseline"` is a special case that removes that fixture. Every object variant, including `{ "label": "baseline" }`, inherits it, so use an object with no `fixtures` to run the plugin once.

```json
[
  "baseline",
  {
    "label": "with-plugin"
  }
]
```

### Repeats and limits

Repeat each variant with `--runs` (or `-n`) when a decision depends on a small score difference. One attempt per variant cannot show how much agent behavior varies between runs. If two variants' score spreads overlap heavily, the run does not show a real difference between them. It does not show that they are equivalent either. Add repeats before you decide.

* `--runs` accepts 1 to 10.
* A run can plan at most 2,000 solves. The count is scenarios × variants × runs. Reduce one of them if the product is higher.
* A variant can set at most 100 fixtures.
* The variant list can contain at most 50 `localPath` references, which resolve to at most 20 different local directories.

## Choose how rigorous to be

Evaluation is a toolkit, not a single fixed test. Scale the rigour to how much confidence you need.

* **More scenarios.** Generate a higher `--count` to cover more of what the skill claims to do.
* **Repeated runs.** An evaluation runs a real agent, and agents are non-deterministic, so a single run can mislead. Pass `--runs` (or `-n`) to run each scenario several times and average out the variance. Worth doing before you publish.
* **Different agents.** Run the evaluation against more than one agent with `--agent` to check the skill holds up across the models your team uses.
* **Quality filter.** Pass `--quality-check` to exclude scenarios the platform judges low-feasibility, rubric-leaking, or low-signal at run time, a stricter filter than the one applied at generation.
* **Skip the baseline.** Pass `--skip-baseline` to run only the with-context variant when you do not need the comparison.
* **Override the scorer.** Pass `--scorer-agent` to score with a different agent from the one solving the tasks.

## Passing secrets to an evaluation

Some scenarios need external credentials, for example an API key or a database URL. The `--env-file` flag encrypts a local `.env` file client-side and injects the variables into the eval sandbox, where setup scripts and the agent inherit them:

```bash
tessl eval run ./my-skill --env-file ./secrets.env
```

The file uses standard `KEY=VALUE` lines; blank lines and `#` comments are ignored. The CLI rejects reserved keys at submit time, including `LITELLM_ADMIN_KEY`, `LITELLM_MASTER_KEY`, `UPLOAD_WORKSPACE`, `DOWNLOAD_WORKSPACE`, and any key prefixed `AWS_`, `S3_`, `DAYTONA_`, or `RECIPE_RUN_`. Rename conflicting keys before running.

## Choosing an agent

By default an evaluation runs against agent `claude`, and in the US region model `deepseek-v4.1-flash`. Choose a different agent and model with `--agent` and `--model`. To compare models or agents in one run, set `model` or `agent` on explicit [test variants](#comparing-test-variants).

The default model and the available models both depend on the region you are connected to: the open-weight models below (DeepSeek, GLM, Kimi, Qwen, MiniMax, GPT OSS, and Nemotron) are not offered in the EU region, which defaults to a Claude model instead. Run `tessl eval run --list-agents` to print the models and the default for your region.

```bash
$ tessl eval run ./my-skill --agent claude --model claude-opus-4-8
```

`--agent` values: `claude`, `codex`, `tessl`. Pair each with a `--model` value from its tab below.

{% tabs %}
{% tab title="Claude" %}

| Model             | `--model` value     |
| ----------------- | ------------------- |
| Claude Fable 5    | `claude-fable-5`    |
| Claude Opus 5     | `claude-opus-5`     |
| Claude Opus 4.8   | `claude-opus-4-8`   |
| Claude Opus 4.7   | `claude-opus-4-7`   |
| Claude Opus 4.6   | `claude-opus-4-6`   |
| Claude Sonnet 5   | `claude-sonnet-5`   |
| Claude Sonnet 4.6 | `claude-sonnet-4-6` |
| Claude Opus 4.5   | `claude-opus-4-5`   |
| Claude Sonnet 4.5 | `claude-sonnet-4-5` |
| Claude Haiku 4.5  | `claude-haiku-4-5`  |
| {% endtab %}      |                     |

{% tab title="Other hosted models" %}
Also served through `--agent claude`.

| Model                         | `--model` value          |
| ----------------------------- | ------------------------ |
| DeepSeek V4.1 Flash (default) | `deepseek-v4.1-flash`    |
| GLM 5.3                       | `glm-5.3`                |
| MiniMax M3                    | `minimax-m3`             |
| GPT OSS 120B                  | `gpt-oss-120b`           |
| Nemotron 3 Ultra              | `nemotron-3-ultra-nvfp4` |
| {% endtab %}                  |                          |

{% tab title="Codex" %}

| Model         | `--model` value |
| ------------- | --------------- |
| GPT 6 Astra   | `gpt-6-astra`   |
| GPT 6.1 Sol   | `gpt-6.1-sol`   |
| GPT 6 Sol     | `gpt-6-sol`     |
| GPT 6 Luna    | `gpt-6-luna`    |
| GPT 5.6 Sol   | `gpt-5.6-sol`   |
| GPT 5.6 Terra | `gpt-5.6-terra` |
| GPT 5.6 Luna  | `gpt-5.6-luna`  |
| GPT 5.5       | `gpt-5.5`       |
| GPT 5.4       | `gpt-5.4`       |
| GPT 5.4 Mini  | `gpt-5.4-mini`  |
| GPT 5.4 Nano  | `gpt-5.4-nano`  |
| GPT 5.3 Codex | `gpt-5.3-codex` |
| GPT 4.1       | `gpt-4.1`       |
| GPT 4.1 Mini  | `gpt-4.1-mini`  |
| {% endtab %}  |                 |

{% tab title="Tessl" %}

| Model               | `--model` value          |
| ------------------- | ------------------------ |
| Claude Fable 5      | `claude-fable-5`         |
| Claude Opus 5       | `claude-opus-5`          |
| Claude Opus 4.8     | `claude-opus-4-8`        |
| Claude Opus 4.7     | `claude-opus-4-7`        |
| Claude Opus 4.6     | `claude-opus-4-6`        |
| Claude Sonnet 5     | `claude-sonnet-5`        |
| Claude Sonnet 4.6   | `claude-sonnet-4-6`      |
| Claude Opus 4.5     | `claude-opus-4-5`        |
| Claude Sonnet 4.5   | `claude-sonnet-4-5`      |
| Claude Haiku 4.5    | `claude-haiku-4-5`       |
| DeepSeek V4.1 Flash | `deepseek-v4.1-flash`    |
| GLM 5.3             | `glm-5.3`                |
| MiniMax M3          | `minimax-m3`             |
| GPT OSS 120B        | `gpt-oss-120b`           |
| Nemotron 3 Ultra    | `nemotron-3-ultra-nvfp4` |
| {% endtab %}        |                          |
| {% endtabs %}       |                          |

## Evaluations when you publish

Publishing a plugin to the registry runs an evaluation as part of publishing, and the results appear on the registry. The evaluation is optional: pass `--skip-evals` to publish without it, or `--with-scenario-quality-check` to apply the quality filter to the publish-time run. See [Distributing via registry](/distribute/distributing-via-registry).

## Prerequisites

* Authentication, either a logged-in session, confirmed with `tessl whoami` and started with `tessl login`, or a `TESSL_TOKEN` API key in the environment.
* Workspace access at Publisher level or above.
* A Tessl project, so Tessl knows which repository an evaluation belongs to and your results stay tied to your codebase over time. Create one with `tessl project create <name> --workspace engteam`, link an existing one with `tessl project link`, and repair a broken link with `tessl project repair`. See [Projects overview](/projects/overview).

A plugin that holds evaluations looks like this on disk:

```
my-plugin/
├── .tessl-plugin/
│   └── plugin.json      workspace, name, version
├── skills/<name>/SKILL.md
├── tessl.json           links the directory to a Tessl project
└── evals/
    └── scenario-name/
        ├── task.md          the task brief the agent sees
        ├── criteria.json    the scoring rubric
        └── scenario.json    fixture and include declarations
```

## File formats

### task.md

Free-form markdown, and the only file the agent sees. Typically structured with problem, expected behaviour, and acceptance criteria sections. Edit it freely before running.

### criteria.json

Defines how a solution is scored, as a weighted checklist:

```json
{
  "context": "Brief description of what is being evaluated.",
  "type": "weighted_checklist",
  "checklist": [
    {
      "name": "fixes_syntax_error",
      "description": "The solution adds the missing closing parenthesis.",
      "max_score": 1,
      "category": "INTENT"
    }
  ]
}
```

Each checklist item carries a `category`: `INTENT`, `DESIGN`, `MUST_NOT`, `MINIMALITY`, `REUSE`, `INTEGRATION`, or `EDGE_CASE`. The score is the sum of awarded scores over the sum of maximum scores, as a percentage, and the grader can award partial credit.

### scenario.json

Optional. Declares fixtures, included files, and setup scripts. A skill scenario can simply list `include` paths to copy into the working directory. A codebase scenario carries a commit fixture with the repository URL, the starting commit, and the context exclude patterns. See the [configuration reference](/reference/configuration) for the full schema.

## Next

* [Codifying and enforcing your skill standards](/codifying-and-enforcing-your-skill-standards/overview) - set an organisation-wide quality bar and enforce it.
* [Troubleshooting reviews and evaluations](/improving-your-skills/troubleshooting-reviews-and-evals) - what to do when a run fails or scores look wrong.


# Compare with test variants

Compare models, agents, skill revisions, and context using test variants, with examples of how to interpret the results.

Use test variants to try different configurations on the same tasks. You can compare models, choose between skill rewrites, or check whether an agent uses a skill without being prompted.

A **test variant** specifies the model, agent, and files available to the agent. Each variant solves the same scenarios and is scored against the same rubric. A run can include as many variants as fit in its [solve limit](#plan-the-run-size).

If you are new to evaluations, start with [Prove a skill works using evaluation](/improving-your-skills/evaluate-skill-quality-using-scenarios). The examples below build on that workflow.

## Set up a comparison

Declare your variants with `--variant-json`, as a JSON array or the path to a JSON file:

```bash
$ tessl eval run ./my-skill --variant-json ./variants.json
```

The examples below show the JSON array for each comparison. Replace placeholder models, agents, and paths with your own values. The run solves only the variants you list, so add a baseline yourself when you need one. The full field reference is in [Comparing test variants](/improving-your-skills/evaluate-skill-quality-using-scenarios#comparing-test-variants).

A fixture is a set of files supplied to the agent. The plugin you evaluate is the fixture named `context`. The `fixtures` field changes fixtures by name for one variant:

* A value replaces the fixture, or adds it when the name is new. Set `context` to another directory to test a different version of the plugin.
* `null` removes the fixture. Set `context` to `null` to measure the agent without the plugin. Other fixtures remain available.
* A name you leave out keeps the fixture the scenario and run supply.

Refer to a local directory with `{ "localPath": "./path" }`. Paths are relative to the directory where you run the command. The CLI uploads each distinct path once.

## Make the results useful

Run each scenario more than once. Agents can produce different results from the same input, so a single attempt tells you little about consistency. Start with three repeats (`-n 3`) and increase the count when results vary widely or the decision needs more confidence.

When you want to measure how much a skill helps, include a **baseline** without it:

```json
{ "label": "no-skill", "fixtures": { "context": null } }
```

This lets you distinguish improvement from a task the agent could already do well. You can omit this variant when you only need to compare versions against each other.

The results below come from example runs. Scores are percentages of the rubric's maximum; differences between scores are percentage points. **SD** means standard deviation, a measure of how much scores vary. A larger SD calls for a closer look at individual results before choosing a variant. See [Read an evaluation's results](/improving-your-skills/reading-evaluation-results) for the commands and guidance.

## 1. Compare models

Keep the skill, agent, and judge fixed, and change only the model. This helps you choose a model based on its quality, cost, and speed on your tasks.

In this example, four models used the same skill to work with an admin artifact API. The skill contained about 500 lines of procedure. Each scenario ran three times per model.

```json
[
  { "label": "frontier", "model": "<model>" },
  { "label": "mid-tier", "model": "<model>" },
  { "label": "small-fast", "model": "<model>" },
  { "label": "cheaper-hosted", "model": "<model>" }
]
```

| Model                  | Score | SD   | Cost / solve | Duration | Turns | Input tokens |
| ---------------------- | ----- | ---- | ------------ | -------- | ----- | ------------ |
| A cheaper hosted model | 85.0  | 15.8 | $1.572       | 313.1s   | 61.3  | 985,422      |
| A frontier model       | 84.7  | 20.0 | $0.940       | 149.6s   | 17.4  | 701,643      |
| A mid-tier model       | 73.0  | 34.6 | $0.683       | 95.9s    | 14.8  | 869,633      |
| A small fast model     | 72.9  | 21.8 | $0.107       | 42.7s    | 10.3  | 385,330      |

The small fast model averaged 72.9, compared with the frontier model's 84.7, at about 11% of the cost and under a third of the time. That may be a useful tradeoff for tasks where its quality is sufficient.

The model labeled “cheaper hosted” had a lower list price but cost 67% more per solve than the frontier model. It also took about twice as long and used 61.3 turns on average, compared with 17.4. Use the measured cost of completing a task when comparing prices.

The score differences need more care. Means ranged from 72.9 to 85.0, while standard deviations ranged from 15.8 to 34.6. The mid-tier model's individual scores covered the full range from 0 to 100. Inspect the per-scenario results and failed attempts before treating the average scores as a reliable model ranking.

## 2. Compare agents

Run the same skill with different coding agents to compare how they complete the work. Keep the model fixed where possible. If two agents do not support the same model, the comparison measures the agent and model together.

This example used a prose editing skill to change files in place. Two variants used Agent A with different models; the other two used separate agents.

```json
[
  { "label": "agent-a-larger-model", "agent": "<agent>", "model": "<model>" },
  { "label": "agent-a-smaller-model", "agent": "<agent>", "model": "<model>" },
  { "label": "agent-b", "agent": "<agent>", "model": "<model>" },
  { "label": "agent-c", "agent": "<agent>", "model": "<model>" }
]
```

| Test variant           | Score | SD  | Duration | Turns | Input tokens |
| ---------------------- | ----- | --- | -------- | ----- | ------------ |
| Agent A, larger model  | 99.0  | 1.1 | 44.6s    | 17.7  | 133,118      |
| Agent A, smaller model | 95.3  | 2.4 | 31.5s    | 19.5  | 137,065      |
| Agent B                | 92.3  | 3.6 | 35.5s    | 5.0   | 207,486      |
| Agent C                | 88.7  | 1.0 | 12.7s    | 3.5   | 57,456       |

Agent A with the larger model had the highest mean score, 99.0, and a low SD of 1.1. Agent C was the fastest at 12.7 seconds and used the fewest input tokens, but had the lowest mean score at 88.7. The choice depends on the quality your editing task needs and how long you can wait for it.

## 3. Compare skill rewrites

Mutation testing compares edited versions of a skill on the same scenarios. It helps you check whether a proposed rewrite improves the agent's work.

Here, a skill for filing and updating tickets gave incorrect CLI guidance. For example, it taught `--labels`, which replaces the label set, for a task that needed `--add-labels`, which preserves existing labels. Three other flags also needed correction. Mechanics that were missing or incorrect accounted for 56 of the target scenario's 100 rubric points.

The evaluation compared nine skill versions and a no-skill baseline across three scenarios, with three repeats. Each rewrite supplied its own `context` directory. This excerpt shows five of the ten variants; the others use the same structure.

```json
[
  { "label": "no-skill", "fixtures": { "context": null } },
  { "label": "v0-shipped" },
  { "label": "v1-outcome-recipe", "fixtures": { "context": { "localPath": "./variants/v1-outcome-recipe" } } },
  { "label": "v2-unteach-labels", "fixtures": { "context": { "localPath": "./variants/v2-unteach-labels" } } },
  { "label": "v8-gutted", "fixtures": { "context": { "localPath": "./variants/v8-gutted" } } }
]
```

The target scenario produced these results:

| Test variant              | Score | SD   |
| ------------------------- | ----- | ---- |
| v7-combined               | 97.2  | 2.5  |
| v4-label-semantics-only   | 91.6  | 12.1 |
| v5-comment-mechanics-only | 89.1  | 13.5 |
| v1-outcome-recipe         | 87.0  | 32.7 |
| v0-shipped                | 86.4  | 21.2 |
| v3-route-to-cli           | 85.2  | 20.4 |
| v2-unteach-labels         | 83.0  | 32.0 |
| v6-correct-pagination     | 64.7  | 40.4 |
| no-skill                  | 39.4  | 24.1 |
| v8-gutted                 | 39.3  | 29.6 |

The shipped skill scored 47 points above the no-skill baseline. The highest-scoring rewrite, `v7-combined`, added another 10.8 points and had the lowest SD, 2.5. It is a candidate to test further across the full scenario set before shipping.

Several smaller edits helped explain what to investigate:

* `v4-label-semantics-only` corrected label handling, including adding labels and removing a stale label. It scored 91.6 on a rubric with 29 points for label management.
* `v5-comment-mechanics-only` added `--body-file` and the idempotency-key format. It scored 89.1, with 19 rubric points covering comment mechanics.
* `v1-outcome-recipe` combined those changes with a discovery section and scored 87.0. Combining the same advice in a different form did not produce the same mean score as `v7-combined`.
* `v2-unteach-labels` removed incorrect guidance without adding the correct flags and scored 83.0. `v3-route-to-cli` added a scoping paragraph without flag details and scored 85.2, close to the shipped version.

Three repeats do not support ranking these rewrites against each other. Most of the standard deviations in this table are large. A variant's range often overlaps the rows above and below it. An overlapping range does not test whether two variants really differ. The score gap between v7-combined and the no-skill floor is 57.8 points, but this run does not tell you the order of the rewrites in between. Run more repeats before treating any single pair in the middle of the table as different.

The run also exposed a rubric problem. `v6-correct-pagination` documented the CLI's actual pagination behavior, but an eight-point rubric item required an `--offset` flag that the CLI did not have. Fix that item before using the results to judge pagination guidance. Its SD of 40.4 also makes the mean score a poor basis for a decision on its own.

The `v8-gutted` variant removed the skill's substantive guidance. It scored 39.3, almost the same as the no-skill baseline's 39.4. A deliberately reduced version like this helps check whether a scenario detects the loss of useful instructions. Keep the other scenarios in the evaluation too, so an improvement on the target task does not hide a regression elsewhere.

## 4. Test whether a skill activates

Change a skill's frontmatter description while keeping its body identical. This tests whether the description helps the agent recognize when to use the skill.

This example compared five descriptions: the shipped text, internal jargon, wording a user might type, the skill name alone, and a deliberately broad description. It used three scenarios with three repeats each. One task clearly needed the skill, one was ambiguous, and one used similar vocabulary but belonged to another skill. That last scenario checks for unwanted activation.

```json
[
  { "label": "d0-shipped" },
  { "label": "d1-jargon", "fixtures": { "context": { "localPath": "./variants/d1-jargon" } } },
  { "label": "d2-user-words", "fixtures": { "context": { "localPath": "./variants/d2-user-words" } } },
  { "label": "d3-name-only", "fixtures": { "context": { "localPath": "./variants/d3-name-only" } } },
  { "label": "d4-overbroad", "fixtures": { "context": { "localPath": "./variants/d4-overbroad" } } }
]
```

Run this comparison with `--skip-forced-context-activation`:

```bash
$ tessl eval run ./my-skill --variant-json ./descriptions.json --skip-forced-context-activation
```

Without that flag, the agent is told to use the context, so you cannot measure whether it would choose the skill itself. Do not set `agentMustRead` on these variants for the same reason.

Each variant had nine observations: three scenarios multiplied by three repeats.

| Test variant  | Activated | Score |
| ------------- | --------- | ----- |
| d2-user-words | 6/9       | 96.0  |
| d4-overbroad  | 5/9       | 98.3  |
| d1-jargon     | 2/9       | 95.9  |
| d0-shipped    | 0/9       | 86.9  |
| d3-name-only  | 0/9       | 85.2  |

The shipped and name-only descriptions recorded no activations. The description written in user language recorded six, making it a useful candidate for further testing.

Check which scenarios activated before choosing a description. More activations are only helpful when they occur on appropriate tasks. The overbroad description scored highest despite activating less often than the user-language version; these nine observations per variant do not explain why, or establish that either description is best.

### How activation is measured

For the `claude` agent, Tessl reads the session transcript and records the skill name when the agent calls the `Skill` tool. This measures explicit invocation. The agent may read or use the content another way without producing that event, so a zero count does not prove the content had no effect.

Other agents do not report activation yet. Their activation is recorded as not measured, which does not mean the skill failed to activate.

`tessl eval view` lists activated skill names per scenario, combining names across all variants and repeats. It does not show activation counts. The table above was calculated from the underlying per-solution records.

You can add `--skip-scoring` to check activation without rubric scoring.

## 5. Compare a skill with access to the codebase

Test whether an agent needs the skill, the source code, or both. Use four variants so you can measure each combination.

This example paired a CLI skill with a portion of the CLI's source. Some guidance was available in the code. Other guidance covered a server-side route and a required preliminary check that the source did not describe. Two scenarios tested those different needs.

The `codebase-only` variant removes the plugin and adds the source as a separate fixture:

```json
[
  { "label": "neither", "fixtures": { "context": null } },
  { "label": "codebase-only", "fixtures": { "context": null, "codebase": { "localPath": "./cli-source" } } },
  { "label": "skill-only" },
  { "label": "both", "fixtures": { "codebase": { "localPath": "./cli-source" } } }
]
```

| Test variant     | Score | Cost / solve |
| ---------------- | ----- | ------------ |
| skill + codebase | 100.0 | $0.654       |
| skill only       | 98.0  | $0.558       |
| neither          | 92.0  | $1.784       |
| codebase only    | 87.8  | $2.155       |

The skill-only variant scored 98.0 and cost $0.558 per solve. Adding the source increased the score to 100.0 and the cost to $0.654. Source alone scored 87.8 and cost $2.155, nearly four times as much as the skill alone.

For these tasks, the skill-only setup offered a lower-cost option with a score close to the full setup. Check whether the two-point difference represents an important missed requirement before removing the source. These results apply to the tested tasks; they do not establish that the codebase is unnecessary for every task the skill supports.

## 6. Remove skills from a plugin

**Ablation** means removing one part at a time to measure how the result changes. For a plugin with several skills, it can help identify which skills contribute to the tasks you care about.

This example used a dbt review plugin with five skills: incremental correctness, SQL performance, dimensional modeling, test coverage, and SQL style. The scenario contained five planted defects, one for each skill, and the rubric penalized invented findings.

Seven variants ran the scenario three times each: the full plugin, five versions with one skill removed, and a no-plugin baseline. Each reduced version needs its own prepared plugin directory, supplied through the `context` fixture. Setting a fixture to `null` removes the entire fixture, so it cannot remove just one skill from a plugin.

| Test variant            | Mean | SD  |
| ----------------------- | ---- | --- |
| full plugin             | 80.0 | 3.6 |
| `minus-modelling`       | 77.3 | 6.5 |
| `minus-sql-performance` | 77.3 | 6.1 |
| `minus-testing`         | 73.0 | 1.7 |
| `minus-incremental`     | 72.0 | 5.2 |
| `minus-style`           | 64.7 | 2.9 |
| no plugin               | 39.3 | 4.2 |

The full plugin had the highest mean score, 80.0, compared with 39.3 without it. Removing the style skill produced the largest drop, 15.3 points. That makes style guidance worth investigating before any attempt to reduce the plugin.

The smaller differences, such as the 2.7-point drop after removing modeling or SQL performance, need more evidence before they justify a change. Skills can also interact, so the drops should not be added together as separate contributions to the full plugin's score. Measure cost and duration as well if the goal is to make reviews cheaper or faster.

## 7. Check for regressions after an edit

Compare a skill before and after a change to check whether the edit affects behavior.

This example used two commits that differed by a version bump and one restored sentence. The sentence explained that a CLI update command changes only three fields and cannot change instruction text.

The run compared the two versions and a no-skill baseline across two scenarios, with three repeats:

```json
[
  { "label": "no-skill", "fixtures": { "context": null } },
  { "label": "pre-edit", "fixtures": { "context": { "localPath": "./plugins/pre-edit" } } },
  { "label": "post-edit" }
]
```

One scenario assigned 45 of its 100 points to requirements affected by the restored sentence. The other tested guidance present word for word in both versions. This control helps check whether a difference is specific to the edited guidance.

The post-edit version scored 100.0, the pre-edit version 95.7, and the no-skill baseline 42.5. The observed gain from restoring the sentence was 4.3 points.

Inspect the relevant rubric items and repeat the comparison before attributing the whole difference to that sentence. Keep a regression scenario when it tests behavior you need to preserve. A tie in one run does not show that the guidance or the test is unnecessary.

## View the results

Use `view` to inspect a run and `compare` to compare separate runs:

```bash
# One run, with a summary for each test variant
$ tessl eval view <run-id>

# Detailed statistics in the per-scenario tables
$ tessl eval view <run-id> --full

# Compare two to ten separate runs
$ tessl eval compare <run-id> <run-id> --full
```

Alongside scores, check cost, duration, tokens, and turns where they are available. [Read an evaluation's results](/improving-your-skills/reading-evaluation-results) explains these fields, missing data, and the differences between the two commands.

## Plan the run size

Each combination of a variant, scenario, and repeat creates one solution:

```
solutions = test variants x scenarios x repeats
```

The rewrite comparison above uses 90 attempts, the activation comparison 45, and the ablation comparison 21. Before the run starts, the CLI prints its estimated credit cost and, where available, your remaining daily quota. See [pricing](https://tessl.io/pricing/) for current rates.

A run supports:

* At most 2,000 attempts.
* Unique variant labels matching `^[a-z0-9][a-z0-9_-]{0,63}$`. Dots are not allowed, so a model name such as `kimi-k2.6` needs a different label.
* 1 to 10 repeats, set with `--runs` or `-n`.
* Up to 50 `localPath` fixture references, resolving to at most 20 distinct local sources.

An organization can create 1,000 attempts per day, resetting at 00:00 UTC. A run with more than 50 scenarios requires `--yes` to confirm. Calculate the solution count before submitting so you have room for repeats and follow-up runs.

## Next steps

* [Read an evaluation's results](/improving-your-skills/reading-evaluation-results) explains the output and how to interpret it.
* [Prove a skill works using evaluation](/improving-your-skills/evaluate-skill-quality-using-scenarios) covers scenario generation and running an evaluation.
* [Troubleshooting reviews and evaluations](/improving-your-skills/troubleshooting-reviews-and-evals) covers failed runs and unexpected scores.


# Read evaluation results

View evaluation results, compare scores and costs, and understand variation and missing data.

An evaluation shows how well an agent completed your tasks and, where available, how much time and money it spent. Start with the summary, then check individual scenarios for failures or differences that the average may hide.

If you have not run an evaluation yet, see [Prove a skill works using evaluation](/improving-your-skills/evaluate-skill-quality-using-scenarios).

## View a run

Open your most recent run or choose one by ID:

```bash
$ tessl eval view --last
$ tessl eval view <run-id>
```

The output includes a comparison of the test variants, followed by a table of results for each variant broken down by scenario.

This example is real output from a run that compared four models on the same plugin, with three repeats each:

```
Eval Results: tile-analysis — model comparison

Repo:      tesslio/monorepo
Agent:     claude
Model:     claude-sonnet-4-6
Variants:  Opus 5, Sonnet 4 6, Haiku 4 5, Deepseek V4 Flash
Status:    ✔ Completed

Test variant comparison
  Test variant          Score           Cost / solve        Duration          Tokens / solve       Turns / solve
  opus-5                100% ± 0.0pp    $0.9835 ± $0.1550   183.3s ± 17.5s    730,012              26.67 ± 1.63
  sonnet-4-6            98% ± 2.8pp     $0.3698 ± $0.0935   126.6s ± 27.0s    533,055              24 ± 3.02
  haiku-4-5             92% ± 7.9pp     $0.1258 ± $0.0635   50.3s ± 42.2s     451,771              15.22 ± 13.45
  deepseek-v4-flash     98% ± 2.7pp     $1.3125 ± $0.3342   245.8s ± 84.0s    801,544              43 ± 9.94
```

Score, cost, duration, and turns show a **mean** (average) followed by a **standard deviation** (SD), which describes how much the measurements vary. For example, `92% ± 7.9pp` means an average score of 92% and an SD of 7.9 percentage points. It is not a guaranteed range for future scores.

The score mean is truncated to a whole number in this view: 84.7 appears as `84%`. Tokens appear as a rounded mean without an SD. When fewer repeats have results than expected, a cell includes a count such as `(n=2 of 3)`.

## View a run in the web app

`tessl eval view` prints a link to the run. The page has these sections, from top to bottom:

* **Result**: one sentence that states the outcome, for example which variant scored highest.
* **Metrics**: score, cost per solve, time per solve, and tokens per solve for each variant. Each value is a mean across repeats, drawn with its spread.
* **Did the agent actually use the skill?**: the share of repeats in which the agent loaded the skill, for each variant that has it. See [How activation is measured](/improving-your-skills/comparing-with-test-variants#how-activation-is-measured).
* **By scenario**: one row for each scenario. Select a row to open its criteria and its repeats. Each repeat shows its score, cost, time, tokens, and whether the skill activated, and members of the workspace can download the solution.
* **Setup**: what each variant did differently and what stayed the same, the total cost of the run, and how many attempts were reused from earlier runs and not billed again.

### When a difference is within noise

The page treats two means as different only when the gap between them is larger than their pooled standard deviation, the square root of the sum of the two variances. A grey band on each metric shows that threshold. In a default run, the band sits around the result without the skill, and a change inside it reads **Unchanged**. In a run with your own variants, the band sits around the best variant, and variants that trail it by more than the band are faded.

A difference inside the band is not proof that the variants are equivalent. Add repeats to narrow the spread.

### Activation states

* A percentage, such as `62%`, is the share of measured repeats in which the agent loaded the skill.
* **Not measured** means the agent does not report activation. It does not count as a zero.
* **Not loaded** on a repeat means the skill was not installed for that variant, or it was installed and the agent did not load it.

## Understand what is being counted

A run contains test variants, scenarios, and repeats:

```
Evaluation run
└── Test variant      A configuration, such as a model or skill version
    └── Scenario      A task and its scoring rubric
        └── Repeat    Another attempt at the same task
```

Every variant attempts every scenario. The rubric defines what a successful response must do, and the judge scores the response against it. `-n`, or `--runs`, sets how many times each variant attempts each scenario.

Each attempt is a **solution**, whether or not scoring completes. The number of attempts is:

```
solutions = test variants x scenarios x repeats
```

For example, four models on three scenarios with three repeats create 36 attempts. This is also the number counted toward the daily quota.

A default run has two test variants: without the plugin and with it. A run declared with `--variant-json` has the variants you list. Each can change the model, agent, plugin, or other fixtures.

## What each measurement means

| Measurement      | What it records                                                   |
| ---------------- | ----------------------------------------------------------------- |
| Score            | Points awarded as a percentage of the rubric's maximum            |
| Cost             | Cost of solving the task, in dollars, where recorded              |
| Duration         | Wall-clock duration and the agent's own time, recorded separately |
| Tokens           | Input, output, cache read, and cache creation token counts        |
| Turns            | Number of agent turns used to solve the task                      |
| Skill activation | Names of skills the agent invoked, where supported                |

The judge's time, cost, and tokens are recorded separately. The cost in the variant comparison table covers solving the task; it does not include scoring it.

Numeric measurements store a count, missing count, minimum, quartiles, median, mean, maximum, and standard deviation. Activation is a separate record of skill names. See [How activation is measured](/improving-your-skills/comparing-with-test-variants#how-activation-is-measured) for its limitations.

## Inspect variation within a result

Use `--full` to see more than the average:

```bash
$ tessl eval view --last --full
```

This expands the per-scenario cells in each variant's table. The top-level comparison of variants stays compact.

An expanded score cell from the run above, for one scenario solved three times by `haiku-4-5`, looks like this:

```
min 81
q1 81.5
med 82
mean 87.7
q3 91
max 100
sd 8.73
```

`min` and `max` are the lowest and highest values. `med` is the median, or middle value. `q1` and `q3` mark the lower and upper quartiles, with the middle half of the results between them. `sd` is the standard deviation.

Use these statistics to decide which individual attempts to inspect. A low minimum may point to a failed attempt, while a wide range may reflect differences between tasks or between repeats of the same task. The summary alone does not explain the cause.

### Avoid ranking variants by the average alone

The model comparison in [What you can compare with an evaluation](/improving-your-skills/comparing-with-test-variants) had these unrounded scores:

| Test variant           | Mean score | SD   |
| ---------------------- | ---------- | ---- |
| A cheaper hosted model | 85.0       | 15.8 |
| A frontier model       | 84.7       | 20.0 |
| A mid-tier model       | 73.0       | 34.6 |
| A small fast model     | 72.9       | 21.8 |

The means differ by about 12 points from highest to lowest, while the standard deviations range from about 16 to 35. With that much variation, the averages alone are a weak basis for choosing a model.

For example, the mid-tier model's individual scores ranged from 0 to 100. Its average of 73.0 hides that variation. Look at the per-scenario results to see which tasks failed and whether the failures recur across repeats.

Start with at least three repeats when you intend to share a conclusion. Add more when the result is variable or the decision needs greater confidence. Comparing standard deviations is useful for spotting uncertainty, but it does not replace a statistical test of the difference.

## Check cost and time alongside quality

That run also shows why the highest score may not be the best choice for your workload:

| Test variant           | Mean score | Cost / solve | Duration | Turns |
| ---------------------- | ---------- | ------------ | -------- | ----- |
| A cheaper hosted model | 85.0       | $1.572       | 313s     | 61.3  |
| A frontier model       | 84.7       | $0.940       | 150s     | 17.4  |
| A small fast model     | 72.9       | $0.107       | 43s      | 10.3  |

The small fast model scored lower, but used about 11% of the frontier model's cost and less than a third of its time. It may be suitable for tasks where that quality is enough.

The cheaper hosted model had a lower list price than the frontier model, yet cost 67% more per completed task and took about twice as long. Its higher turn count helps explain why list price alone is not enough to estimate the cost of a task.

## Measure the improvement over a baseline

To measure how much a skill helps, include a variant without it:

```json
{ "label": "no-skill", "fixtures": { "context": null } }
```

Setting the `context` fixture to `null` removes the plugin under test. Other fixtures, such as a codebase, remain available. This gives you a baseline for the same tasks without the plugin you are testing.

In one evaluation, five skill rewrites scored between 95 and 98, but the no-skill variant already scored 90.2. The measured improvements were therefore between 4.8 and 7.8 points. Those scenarios left little room to distinguish successful rewrites because the agent could already do most of the work without the skill.

On a different skill, the shipped version scored 86.4 against a baseline of 39.4. The best rewrite scored 97.2. The rewrite improved on the shipped version by 10.8 points; the baseline showed that both versions also helped substantially compared with having no skill.

A baseline provides context for a skill's contribution. You do not need it merely to calculate the difference between two versions.

## Compare separate runs

Use `compare` to put two to ten runs side by side:

```bash
$ tessl eval compare <run-id> <run-id>
$ tessl eval compare <run-id> <run-id> --full
```

The output has one column per run and shows the configuration dimensions that differ between them. Check those differences before attributing a score change to your edit.

The compact statistical cells in `compare` and `view` both show the **mean** with the standard deviation.

With `compare --full`, every statistical cell in the comparison table expands to show the full distribution. With `view --full`, only the per-scenario tables expand.

### Confirm results with another run

The seven examples in [What you can compare with an evaluation](/improving-your-skills/comparing-with-test-variants) were each run twice with the same configuration. Two conclusions changed on the second run: the order of the ablation variants reversed, and the improvement from a rewrite changed by an order of magnitude.

Repeats help reveal variation within a run. A separate run checks whether your conclusion holds again. If it does not, investigate the individual attempts and the rubric before making a change based on the first result.

## Handle missing data

A missing measurement is different from a measured zero. Missing cells display a dash, and partially missing results include a count such as `n=1 missing`. Check these counts before comparing averages.

| Missing field                            | What to check                                                                                                                                                           |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Activation                               | Only the `claude` agent reports activation. For other agents it is recorded as not measured. This does not mean the skill failed to activate.                           |
| Cost                                     | Some agents do not use the billing path that records per-solution cost. An empty cell does not mean the task was free.                                                  |
| Cost or judge statistics on a shared run | These fields are visible only to members of the workspace that owns the run, even when the run is public. Check workspace access before assuming data was not recorded. |
| Score                                    | A solution may complete without receiving a score. Treat it as missing data, not a zero or a no-skill baseline.                                                         |
| Per-turn or tool-usage statistics        | These require an agent log. Attempts without one are reported as unavailable.                                                                                           |

## Run limits

| Limit                                             | Value                         |
| ------------------------------------------------- | ----------------------------- |
| Attempts per run (variants × scenarios × repeats) | 2,000                         |
| Repeats (`-n`, `--runs`)                          | 1 to 10                       |
| `localPath` fixture references per run            | 50                            |
| Distinct local sources                            | 20                            |
| Scenarios per run                                 | More than 50 requires `--yes` |
| Attempts per organization per day                 | 1,000, resetting at 00:00 UTC |

The daily cap counts attempts, not runs. Four variants on three scenarios with three repeats use 36 of the 1,000 available attempts.

## Next steps

* [What you can compare with an evaluation](/improving-your-skills/comparing-with-test-variants) gives examples of model, agent, and skill comparisons.
* [Prove a skill works using evaluation](/improving-your-skills/evaluate-skill-quality-using-scenarios) covers generating scenarios and running evaluations.
* [Troubleshooting reviews and evaluations](/improving-your-skills/troubleshooting-reviews-and-evals) helps with failed runs and unexpected scores.
* [Check a skill's quality using review](/improving-your-skills/reviewing-skills) explains how to review a skill before evaluating it.


# Troubleshooting reviews and evaluations

Most problems with reviews and evaluations come down to a few causes. Work through these when something does not behave as expected.

{% hint style="info" %}
**Takeaway** Most problems with reviews and evaluations come down to a few causes: scenarios in the wrong place, missing permissions, a skill that never activated, or normal model variance read as a real result. Work through the questions below when something does not behave as expected.
{% endhint %}

## Why does my evaluation say "no scenarios found"?

`tessl scenario download` writes scenarios into the plugin's `evals/` directory when run from the plugin root or any subdirectory of it. The plugin root is the directory that contains `.tessl-plugin/plugin.json`. If you ran it from outside the plugin tree, scenarios landed somewhere else and `tessl eval run <path>` cannot find them.

Re-download from inside the plugin (any subdirectory works):

```bash
cd <plugin-root>          # or any subdirectory under it
tessl scenario download --last
```

Or pass the location explicitly:

```bash
tessl scenario download --last --output <plugin-root>/evals
```

## Why do I only see baseline results, with no "with context" column?

The evaluation completed, but every scenario shows a baseline score and nothing for the skill. This means the run could not find the skill to inject, usually because the plugin layout is wrong.

Check that `evals/`, `.tessl-plugin/`, and the skill directory all sit under the same plugin root, and that `skills[]` in `plugin.json` points at the skill directory correctly, relative to the plugin root.

## Why does my run fail on permissions?

Evaluations require at least **publisher** access to the workspace. Member access is not enough to run `tessl eval run` or `tessl scenario generate`. A review needs only member access, so a skill you can review may still fail to evaluate.

Confirm who you are logged in as with `tessl whoami`, and check your access to the workspace the plugin is attributed to in `.tessl-plugin/plugin.json`.

## Why do scores look worse with the skill than without?

A small negative difference is usually normal model variance, not evidence the skill is harmful. An evaluation runs a real agent, so results vary from run to run. Run each scenario several times with `--runs` and read the overall pattern rather than a single result. A large or consistent negative difference is a real signal that the skill is confusing the agent, and the content needs work.

## Why does the skill make no difference at all?

If the "with context" score sits at zero improvement across repeated runs, the skill most likely never activated, so the agent never used it. That is a different problem from variance. Check activation: run `tessl review run` and look at the description score, and make sure the skill's `description` states clear trigger conditions for when it should be used. See [Check a skill's quality using review](/improving-your-skills/reviewing-skills).

## A scenario keeps failing to complete

It is normal for an agent not to solve every scenario on every attempt. Retry the run, or adjust the scenario if it is consistently failing for the wrong reason.

```bash
tessl eval retry abc123
tessl eval retry --last
```

## Still using `tessl skill review`?

`tessl skill review` has been removed and replaced by `tessl review run`, which requires authentication. If a script or workflow still calls the old command, see [Migrate from skill review](/improving-your-skills/migrate-from-skill-review) for what changed and how to move across.

## Getting help

If none of the above surfaces the cause, share the output of `tessl doctor --json` when you reach out, since it reports your authentication status. See [Giving feedback](/support/giving-feedback).


# Overview

Gate skill quality automatically and define your organisation's standard for what a good skill looks like.

{% hint style="info" %}
**Takeaway** Keep poor-quality skills out of your repository. Set the bar for what "good" means, then gate it in CI so skills that fall short do not get merged.
{% endhint %}

When a team shares skills, quality stops being a personal matter. A weak skill that one person tolerates becomes everyone's problem once it is in the shared repository, shaping how every agent on the team behaves. This section is about holding a quality line automatically, so you do not depend on everyone remembering to check.

There are two parts to it:

* **Enforce code invariants.** Use verifiers for binary, observable facts about committed files that a linter cannot express. See [Verifiers overview](/codifying-and-enforcing-your-skill-standards/verifiers-overview) and [Verify agent changes in CI](/codifying-and-enforcing-your-skill-standards/verify-agent-changes-in-ci).
* **Gate on a review score.** Run a review in CI and fail the build when a skill scores below your bar, so substandard skills never merge. See [Gate skill quality in CI](/codifying-and-enforcing-your-skill-standards/gate-skill-quality-in-ci).
* **Define your own bar.** The default rubric scores against general best practices. Fork it to encode what your organisation cares about, and apply it to every review. See [Define your own quality standards](/codifying-and-enforcing-your-skill-standards/define-your-own-quality-standards).

This section covers protecting a repository from low-quality skills. It does not cover managing your wider skill estate, surveying what is installed across your organisation, or org-level policy; those are separate capabilities.

## Next

* [Verifiers overview](/codifying-and-enforcing-your-skill-standards/verifiers-overview) - decide whether a verifier is the right way to enforce a standard.
* [Verify agent changes in CI](/codifying-and-enforcing-your-skill-standards/verify-agent-changes-in-ci) - write, calibrate, and run a verifier on pull requests.
* [Gate skill quality in CI](/codifying-and-enforcing-your-skill-standards/gate-skill-quality-in-ci) - run reviews on pull requests and block merges below your threshold.
* [Define your own quality standards](/codifying-and-enforcing-your-skill-standards/define-your-own-quality-standards) - build a custom rubric and apply it to every review.


# Verifiers overview

Use verifiers to check committed files against repository-specific invariants that a linter cannot express.

{% hint style="info" %}
**Takeaway** Use a verifier for a binary, observable fact about committed files when a deterministic check cannot express the standard.
{% endhint %}

{% hint style="info" %}
You can select and fix a specific judge with `tessl change verify --model`; use `openai/gpt-6-luna` to prioritize reasoning or **Jev** (`--model jev`) for cost and speed. Jev returns one pass/fail verdict and a confidence per rule with no written reasoning, so it is cheaper and faster to run than a reasoning judge. As with any judge, a run sends the files it checks to your configured LLM gateway. Jev's availability depends on that gateway's configuration and region, and it is not offered for bring-your-own-model organizations.
{% endhint %}

A verifier is an LLM-as-judge check that compares committed files with an invariant stored as JSON. For example, a verifier can check that every user-facing error includes a next step or that every FastAPI route uses your typed response helpers.

Verifiers belong in your repository. A verifier file describes what to judge, while the `verify` block in `tessl.json` selects the files it covers and sets the severity of its findings. This separation lets teams reuse the same verifier with different scopes and enforcement levels.

{% hint style="success" %}
Want a coding agent to set up and tune verifiers with you? Install the `tessl/tessl-verify` plugin with `tessl install tessl/tessl-verify`. Its skill walks an agent through authoring verifier JSON, scoping the `verify` block, and adding a CI gate.
{% endhint %}

## When to use a verifier

Use a verifier when all of these are true:

* The standard has a pass or fail answer.
* The evidence is visible in committed files or a diff.
* A reviewer can apply the standard consistently from the supplied context.
* A deterministic tool cannot check it reliably.

Do not use a verifier for subjective preferences such as whether code is elegant. Narrow the invariant until its checklist has observable evidence.

## Choose the right mechanism

| Mechanism      | Use it for                                                | Example                                          |
| -------------- | --------------------------------------------------------- | ------------------------------------------------ |
| Rule           | Guidance an agent must follow on every relevant task      | Use the shared response helper for API routes    |
| Skill          | A workflow an agent should activate for a specific task   | Create and register a new API route              |
| Linter or test | A fact code can check deterministically                   | Every route file imports an approved symbol      |
| Verifier       | A binary invariant that needs judgment over code or prose | Every user-facing error explains the next action |

Prefer a linter or test when one can enforce the invariant. A rule or skill guides an agent before and during a change; a verifier checks the result after the change. You can pair them: a rule teaches the expected pattern, and a verifier catches changes that do not follow it.

Verifiers are separate from Tessl Code Review. Code Review looks for general problems across a pull request. Verifiers enforce the specific invariants your repository names and configures.

## How verification works

Each verifier JSON file contains the instruction, applicability criteria, evaluation scope, and a checklist of binary rules. The repository's `tessl.json` groups verifier files, targets source files with include and exclude globs, and assigns an `info`, `warn`, or `error` level.

`tessl change verify` resolves the configured scope and judges applicable targets. In CI, `warn` findings remain advisory and `error` findings exit non-zero, so they can block a pull request.

By default, `tessl change verify` is change-aware: it checks the files a change touches against every applicable verifier, and re-checks a changed verifier or `tessl.json` rule against its full configured scope. Run `tessl change verify --all` to audit every file in the current project's configured scopes (include and exclude filters still apply); add `--recursive` to include linked child manifests in a monorepo.

For the complete configuration fields, see the [`verify` block reference](/reference/configuration#verify-block).

## Next

* [Verify agent changes in CI](/codifying-and-enforcing-your-skill-standards/verify-agent-changes-in-ci) - create a verifier, inspect its scope, and add a pull request gate.
* [Improving agent code quality](/tutorials/improving-agent-code-quality) - find recurring review feedback worth turning into a rule, skill, test, or verifier.


# Verify agent changes in CI

Write and calibrate a verifier, then run it as an advisory or blocking check on pull requests.

{% hint style="info" %}
**Takeaway** Start a verifier as a warning, inspect the files it covers, and promote it to an error only after it produces reliable findings on real changes.
{% endhint %}

This guide creates a verifier for a repository invariant, connects it to `tessl.json`, and runs it against pull request changes. If you are deciding whether a verifier fits your standard, start with [Verifiers overview](/codifying-and-enforcing-your-skill-standards/verifiers-overview).

## 1. Write the verifier

Create `verifiers/actionable-errors.json`:

```json
{
  "name": "User-facing errors include a next step",
  "instruction": "When code returns a user-facing error, it should tell the user what to do next.",
  "relevant_when": "TypeScript code that constructs a UserFacingError or prints an error for a CLI command.",
  "context": "CLI errors should be actionable for users running commands in a terminal.",
  "scope": "file",
  "scope_reason": "Each command file contains the error and its recovery guidance.",
  "checklist": [
    {
      "name": "actionable-error",
      "rule": "Each user-facing error includes the command to run next, the flag to change, or the file to inspect."
    }
  ]
}
```

Keep `relevant_when` specific. It should name the observable trait that makes a target applicable, rather than a broad category such as "TypeScript file."

Validate the file before running it:

```bash
$ tessl change verify lint verifiers/actionable-errors.json
```

## 2. Configure its scope as a warning

Add a group to the `verify` block in `tessl.json`:

```json
{
  "name": "engteam/my-project",
  "verify": {
    "groups": {
      "error-ux": {
        "path": "verifiers/actionable-errors.json",
        "level": "warn",
        "include": ["src/**/*.ts"],
        "exclude": ["**/*.test.ts"]
      }
    }
  }
}
```

Severity belongs in `tessl.json`, not in the verifier file. Starting at `warn` reports findings without blocking the pull request.

## 3. Inspect the matched files

Preview a full-repository run before spending judge calls:

```bash
$ tessl change verify --dry-run --all --show-files
```

Check that the output contains the files you intend to cover and excludes unrelated files. Adjust the group's `include` and `exclude` globs or the verifier's `relevant_when` if the scope is too broad or narrow.

Then judge a small sample:

```bash
$ tessl change verify --all --sample 5
```

This sends the selected source content to Tessl's LLM judge. Limit the verifier's scope to content your organization is authorized to send, and exclude credentials, personal data, and other sensitive material.

Review each result against the invariant. Refine the verifier until the same evidence produces consistent pass or fail decisions.

## 4. Run it on pull requests

CI needs a Tessl API key in the `TESSL_TOKEN` environment variable. For key creation and storage guidance, see [Authenticating in CI](/codifying-and-enforcing-your-skill-standards/gate-skill-quality-in-ci#authenticating-in-ci). A pull request can change its workflow, verifier, and manifest configuration, so its branch location does not establish trust. Use a `pull_request` workflow, not `pull_request_target`, and protect the credentialed job with a GitHub environment that requires approval from reviewers who are authorized to approve the configuration and source changes.

The following job and steps assume the workflow runs on `pull_request`, has checked out the repository, and installed Tessl with `tesslio/setup-tessl@v2`. The job skips an external head repository by comparing repository identities, so it still runs for same-repository pull requests when the repository itself is a fork. Create the `tessl-verify` environment in GitHub with required reviewers, store `TESSL_TOKEN` as an environment secret, and do not approve it until the pull request's workflow, verifier, and manifest changes have been reviewed:

```yaml
verify:
  runs-on: ubuntu-latest
  if: github.event.pull_request.head.repo.full_name == github.repository
  environment: tessl-verify
  env:
    TESSL_TOKEN: ${{ secrets.TESSL_TOKEN }}
  steps:
    - name: Fetch base branch
      env:
        BASE_REF: ${{ github.event.pull_request.base.ref }}
      run: git fetch --no-tags origin "$BASE_REF:refs/remotes/origin/$BASE_REF"

    - name: Inspect verifier scope
      env:
        BASE_REF: ${{ github.event.pull_request.base.ref }}
      run: tessl change verify --base "origin/$BASE_REF" --dry-run --show-files --max-calls 200

    - name: Verify changed files
      env:
        BASE_REF: ${{ github.event.pull_request.base.ref }}
      run: tessl change verify --base "origin/$BASE_REF" --github --max-calls 200
```

`--base "origin/$BASE_REF"` compares against the pull request's actual target branch, which the fetch step records as a remote-tracking ref. For source-only changes, the command verifies the pull request's changed files. When the pull request changes a verifier JSON file or the `verify` configuration, the affected verifier runs across its full configured scope. `--github` emits annotations for findings. A `warn` finding is advisory, while an `error` finding makes the command exit non-zero.

`--max-calls 200` is the gate's call budget. The command stops before judging if its planned scope exceeds that limit; narrow the group's scope or choose and document a larger limit before changing the workflow.

If you run the command from a monorepo root with linked child manifests, add `--recursive` to each invocation.

## 5. Promote the verifier to a blocking check

Leave the verifier at `warn` while it runs on real pull requests. Track false positives, missed applicable files, and unclear findings. When the verifier is reliable, change the group level:

```json
{
  "verify": {
    "groups": {
      "error-ux": {
        "path": "verifiers/actionable-errors.json",
        "level": "error",
        "include": ["src/**/*.ts"],
        "exclude": ["**/*.test.ts"]
      }
    }
  }
}
```

The existing CI command now blocks pull requests with failing findings. You do not need to change the workflow or add `--fail-on-warn`.

## Next

* Read the [`verify` block reference](/reference/configuration#verify-block) for group filters and per-verifier overrides.
* Add [Tessl Code Review](/tutorials/setting-up-agentic-code-review) when you also want general pull request review beyond named repository invariants.


# Gate skill quality in CI

Run a review on every pull request and fail the build when a skill scores below your threshold.

{% hint style="info" %}
**Takeaway** Run a review on every pull request and fail the build when a skill scores below your threshold. Skills that fall short of the bar never merge.
{% endhint %}

A review tells one person whether a skill is good. Gating in CI applies that check to everyone, automatically, so a substandard skill cannot reach your shared repository in the first place.

## Fail a build below a threshold

Pass `--threshold` to exit non-zero when a review score falls below your bar:

```bash
tessl review run ./my-skill --workspace engteam --threshold 80
```

`--threshold` takes a 0-100 integer. The default is 0, which never fails. Any value above 0 causes the command to exit non-zero - and fail the CI step - when the score falls below it.

Add `--json` when you need machine-readable output, for example in a script that reads the score programmatically:

```bash
tessl review run ./my-skill --workspace engteam --json --threshold 80
```

`--workspace` is required when `--json` is set, because a non-interactive run cannot prompt for one. In practice, always pass `--workspace` in CI regardless.

You can also re-check an existing run against a threshold without starting a new review:

```bash
tessl review view <run-id> --json --threshold 80
```

## Authenticating in CI

A review runs against your workspace, so CI needs to authenticate without an interactive login. Create an API key for your workspace:

```bash
tessl api-key create --workspace engteam
```

Set the key as a repository secret named `TESSL_TOKEN`. The CLI and the Tessl GitHub Action both read it from that variable.

### Keys expire after 30 days

Every API key expires. Unless you pass `--expiry-date` or `--ttl`, the key you just created is valid for **30 days from creation**, and the CLI prints the exact date when it issues the key.

This matters most in CI, because nothing fails at the moment the key expires. Your pipeline stays green for a month and then starts failing authentication on a run that changed nothing. Put a reminder in your calendar, or pass a longer `--expiry-date` when you create the key:

```bash
tessl api-key create --workspace engteam --name ci --role member --expiry-date 2027-12-31T00:00:00Z
```

You can also set a duration using the local clock just before the creation request, such as `--ttl 4w` for four weeks, `--ttl 2mo` for 60 days, or `--ttl 1y` for 365 days. Use one expiry flag at a time.

### Rotating a CI key

Rotation is always something you run from your own machine, never from inside the pipeline. An API key cannot create another API key, which means a leaked CI credential cannot renew itself. That is deliberate.

Two paths, depending on whether the key has already expired.

**The key has already expired.** Your pipeline is broken, so there is no window to protect. Replace it under the same name:

```bash
tessl api-key delete --workspace engteam ci
tessl api-key create --workspace engteam --name ci --role member
```

**The key is still valid.** Create the replacement under a new name first, confirm a run passes with it, and only then remove the old key. Your pipeline is never without a working credential:

```bash
tessl api-key create --workspace engteam --name ci-2 --role member
# update the secret, confirm a run passes, then:
tessl api-key delete --workspace engteam ci
```

Key names are unique per workspace, which is why the second path needs a different name rather than reusing `ci`.

Both paths involve reading your pipeline to find which secret feeds `TESSL_TOKEN`, and that secret can be named anything. `tessl agent` can do that part for you: ask it to rotate your Tessl CI key and it finds the secret, shows you a plan naming where each fact came from, and waits for a go-ahead before it changes anything.

```bash
tessl agent "rotate my Tessl CI key"
```

Nothing to install. The skill behind this ships with the CLI.

Use `tessl api-key list --workspace engteam --json` when you want key ids, roles, or expiry dates in a script.

### Where the key lives on each CI platform

Whatever your platform, the CLI reads the key from the `TESSL_TOKEN` environment variable. What changes is where the secret behind that variable is stored. The secret itself can be named anything, so check your pipeline definition to see which one it maps to `TESSL_TOKEN`.

| Platform            | Where the secret lives                                 | CLI                            |
| ------------------- | ------------------------------------------------------ | ------------------------------ |
| GitHub Actions      | Settings > Secrets and variables > Actions             | `gh secret set <secret>`       |
| GitLab CI           | Settings > CI/CD > Variables                           | `glab variable set <variable>` |
| CircleCI            | Project Settings > Environment Variables               |                                |
| Buildkite           | Pipeline Settings > Secrets                            | `bk secret set <secret>`       |
| Azure Pipelines     | Pipelines > Library                                    | `az pipelines variable update` |
| Jenkins             | Manage Jenkins > Credentials                           |                                |
| Bitbucket Pipelines | Repository settings > Pipelines > Repository variables |                                |
| TeamCity            | Build Configuration > Parameters                       |                                |

When a key expires, the CLI names your platform and its secret location in the error, so you do not have to come back to this table.

## Run it on pull requests

Tessl publishes a GitHub Action that installs the CLI and runs this gate on pull requests, so you do not have to wire up the CLI by hand. For the complete workflow, see [Review, lint & publish with GitHub Actions](/distribute/review-and-publish-with-github-actions).

## Next

* [Define your own quality standards](/codifying-and-enforcing-your-skill-standards/define-your-own-quality-standards) - gate against your organisation's own rubric, not just the default.
* [Check a skill's quality using review](/improving-your-skills/reviewing-skills) - full reference for `tessl review run`, score interpretation, and applying fixes.


# Define your own quality standards

Fork the default review rubric to encode your organisation's quality bar, then apply it to every review.

{% hint style="info" %}
**Takeaway** The default review rubric scores against general best practices. Fork it to encode what your organisation cares about, then apply it everywhere: local reviews, automated fixes, and your CI gate.
{% endhint %}

The default rubric is a good baseline, but it does not know your conventions. If your team has its own bar for what a skill must do, a custom reviewer plugin lets you define it once and apply it consistently.

## Build a reviewer

The `create-review-plugin` skill from [`tessl/review-plugin-creator`](https://tessl.io/registry/tessl/review-plugin-creator) scaffolds a reviewer plugin for you. Install it, then ask your agent to create a reviewer:

```bash
tessl install tessl/review-plugin-creator
```

A reviewer plugin is a standard Tessl plugin with this layout:

```
<plugin-name>/
├── .tessl-plugin/
│   └── plugin.json
└── skills/skill-reviewer/
    ├── SKILL.md
    ├── references/
    │   ├── config.json
    │   └── rubrics/
    │       └── <judge-name>.json   (one per judge)
```

`config.json` sets the weight each judge contributes to the final score, plus a `validation_weight` for the deterministic validation pass. All weights must sum to 1.0:

```
validation_weight + sum of judge weights = 1.0
```

Set `validation_weight` to `0.0` to exclude validation from scoring. Validation still runs as a gate but does not affect the numeric score.

Each rubric file defines one judge: what it evaluates (`evaluation_target`), a scoring scale, and a set of dimensions. Dimension weights within a single rubric also sum to 1.0. The `create-review-plugin` skill validates the finished plugin by running `tessl review run` against a test skill, so you catch misconfigured weights before you use it.

The [default rubric](https://github.com/tesslio/product-plugins/tree/main/default-skill-review) is worth forking rather than building from scratch. It scores a skill's description on specificity, completeness, trigger term quality, and distinctiveness, and the content on conciseness, actionability, workflow clarity, and progressive disclosure. Forking keeps those dimensions and lets you adjust weights, remove ones that do not apply, or add judges of your own.

## Use a reviewer

Point a review at your reviewer with `--review-plugin`, passing either a local directory or a `workspace/plugin` registry reference:

```bash
tessl review run ./my-skill --workspace engteam --review-plugin <reviewer-plugin>
```

The same flag works with `tessl review fix` and in your CI gate:

```bash
tessl review run ./my-skill --workspace engteam --review-plugin <reviewer-plugin> --threshold 80
```

Pass `--review-plugin` explicitly on each run, or configure a workspace default, so the flag is not required at all.

## Setting workspace policy

Rather than passing `--review-plugin` on every run, you can set your reviewer plugin as the default for an entire workspace. Once set, any review run that does not name its own reviewer uses your plugin automatically, so contributors do not need to remember the flag. A run that passes an explicit `--review-plugin` (or an upload override) still takes precedence, so the workspace default is the automatic choice rather than a non-bypassable rule.

To configure this, open the workspace settings in the Tessl web app and navigate to the **Reviews** section. Select **Manage**, enter your reviewer plugin reference (for example `myorg/my-reviewer` or a pinned version such as `myorg/my-reviewer@1.2.3`), and save. The reviewer plugin you choose becomes the workspace default, applied to every review run that does not override it.

{% hint style="info" %}
You still need the `customReviewPlugins` entitlement to use this feature. Contact your workspace owner if the Reviews section is not visible in your settings.
{% endhint %}

## Next

* [Gate skill quality in CI](/codifying-and-enforcing-your-skill-standards/gate-skill-quality-in-ci) - enforce your rubric automatically on pull requests.


# Distributing via registry

Distribute, share, and maintain plugins through the Tessl Registry

Once you've created and published a plugin, you'll need to share it with your team, keep it updated, and manage its lifecycle. This guide covers the common tasks for managing published plugins.

## Sharing with your team

Add members to your workspace so they can access your private plugins:

```bash
tessl workspace add-member --username <tessl-username> --role member --workspace myworkspace
```

The `member` role gives read-only access to plugins. See [Roles](/administrators/roles) for more information about workspace roles and permissions.

To verify members have been added:

```bash
tessl workspace list-members myworkspace
```

## Installing plugins in projects

Team members can install your plugin in their projects:

```bash
tessl install myworkspace/your-plugin
```

Or search for it:

```bash
tessl search your-plugin
```

Private plugins appear in search results for workspace members just like public plugins.

To install a specific version:

```bash
tessl install myworkspace/your-plugin@1.0.0
```

## Updating documentation

When your library or service changes, update the plugin to keep documentation current.

### For automated plugins

If you used automated generation from a repository:

1. Merge code changes to your repository
2. Navigate to the Tessl interface and regenerate the plugin
3. Review new evaluation results
4. Publish the updated version

### For manual plugins

If you created the plugin manually:

1. Edit your documentation files
2. Increment the version in `.tessl-plugin/plugin.json` (following [semantic versioning](https://semver.org/))
3. Validate with `tessl plugin lint`
4. Publish with `tessl plugin publish`

**Version guidelines:**

* **Patch** (1.0.0 → 1.0.1): Bug fixes, typo corrections
* **Minor** (1.0.0 → 1.1.0): New content, backward-compatible changes
* **Major** (1.0.0 → 2.0.0): Restructuring, breaking changes

Users can update to the latest version:

```bash
tessl install myworkspace/your-plugin
```

## Archiving plugins

To remove a plugin from search results without deleting it:

```bash
tessl plugin archive --plugin myworkspace/your-plugin@1.0.0 --reason "replaced by new library"
```

Archived plugins:

* No longer appear in search results
* Cannot be installed by new users
* Remain accessible to projects that already have them installed
* Can be restored by contacting support

Use archiving when:

* A library is deprecated but still used by some projects
* You're migrating to a new plugin and want to phase out the old one
* The plugin is outdated but may still be needed for reference

## Unpublishing plugins

To completely remove a plugin within ***2 days*** of upload:

```bash
tessl plugin unpublish --plugin myworkspace/your-plugin@1.0.0
```

**Important:** Unpublishing is only available within 2 days of the original publish. After that, use archiving instead.

Unpublished plugins are completely removed and cannot be recovered.

## Monitoring plugin usage

Track how your plugins are being used:

1. Navigate to the [Tessl Registry](https://tessl.io/registry)
2. Log in and select your workspace
3. View your plugins to see usage statistics and install counts

This helps you understand which plugins are most valuable to your team.

## Best practices

* **Update regularly** - Keep documentation in sync with code changes
* **Use semantic versioning** - Help users understand the impact of updates
* **Archive deprecated plugins** - Guide users to replacement plugins
* **Monitor usage** - Focus maintenance effort on widely-used plugins
* **Document breaking changes** - Communicate major version updates to your team

## Related

* [Workspaces](/reference/workspaces) - Understanding workspaces and permissions
* [Configuration files](/reference/configuration) - Complete `.tessl-plugin/plugin.json` reference


# Sharing with your team and publicly

Making your plugins publicly discoverable in the Tessl Registry

Plugins can be shared with your team or with the entire internet. This is controlled by the `private` field in your plugin's [`.tessl-plugin/plugin.json`](https://docs.tessl.io/distribute/pages/wpdogFLQtCeEnXfbelvK#plugin.json) file.

## Understanding public vs private plugins

### Private plugins (`"private": true`) - Default

**Shared only with your team:**

* Only accessible to workspace members
* Not discoverable in public searches
* Useful for proprietary code, internal standards, company-specific content
* **Team sharing:** [Add members to your workspace](/reference/workspaces#add-member) to give them access

If you don't specify the `private` field in your `.tessl-plugin/plugin.json`, it defaults to `true` (private).

### Public plugins (`"private": false`)

**Shared with the entire internet:**

* Anyone can discover and install them via the Tessl Registry
* Appear in search results and on your workspace's public profile
* Useful for open source libraries, shared standards, community resources
* **Cannot be made private again once published**

## Making a plugin public

**Warning:** Once you make a plugin public, you cannot make it private again. Use this with caution.

To make a plugin publicly discoverable, set `"private": false` in your [`.tessl-plugin/plugin.json`](https://docs.tessl.io/distribute/pages/wpdogFLQtCeEnXfbelvK#plugin.json):

```json
{
  "name": "myworkspace/my-plugin",
  "version": "1.0.0",
  "description": "My public plugin",
  "private": false,
  "skills": "skills"
}
```

Then publish (or republish) your plugin:

```sh
tessl plugin publish
```

Your plugin is now publicly discoverable in the [Tessl Registry](https://tessl.io/registry) and accessible to anyone on the internet.

## Best practices

* **Start private** - Develop and test plugins privately before making them public
* **Use semantic versioning** - Especially important for public plugins that others depend on
* **Document thoroughly** - Include clear README files and usage examples
* **Validate before publishing** - Run `tessl plugin lint` to check for issues

## Related documentation

* [Distributing via registry](/distribute/distributing-via-registry) - How to publish plugins
* [Configuration files](/reference/configuration) - plugin.json field reference
* [Creating plugins](/creating-skills-and-plugins/create-a-plugin) - How to create your own plugins


# Permissions

The following permissions are required to configure and use Tessl with your repositories.

## **GitHub permissions**

You must have **`write` access**, so Tessl can create branches and open PRs.

## GitLab permissions

* A token with the scope of **`api`** is required.\* Project or Group tokens is required, webhooks will be set.
  * For Group tokens, the **`Owner`** permission is required.
  * Project tokens require **`Maintainer`** or above.

## **Tessl permissions**

* To enable GitHub or GitLab integration: **`Manager` or `Owner`.**
* To generate a plugin: **`Member`**.
* To distribute guidance across repositories: **`Publisher` or above** for the workspace.

If these permissions are missing, specific actions won’t appear in the interface.


# Connect Tessl to GitHub

Connect a GitHub account or organization so Tessl can scan its repositories and report Context and Findings in your Organization.

## Enable the SCM connection

You need the Organization admin role to manage SCM connections.

* Select your Organization from the dropdown at the top of the sidebar.
* Open **Settings**, then **Integrations**.
* Under **Source control**, select **Add** next to GitHub.<br>

  <div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-3092c9fe6a083932f6046f6e71c9b08c743406d0%2Fgithub-integration-add.png?alt=media" alt="" width="563"><figcaption></figcaption></figure></div>
* Select the GitHub organization or account to add the Tessl App to. Accounts that already have Tessl will not have the option.

  <div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-c8d91f35d8ad85ba600dca98b56e9fc3313b159c%2Fgithub-app-install-location.png?alt=media" alt="" width="563"><figcaption></figcaption></figure></div>
* Decide if you want to authorize Tessl on specific repositories or provide access to everything. Once selected, click *Install & Authorize*

  <div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-ba3ad5c0a980e9df705d45c241b23c4b2b3414f6%2Fgithub-app-install-authorize.png?alt=media" alt="" width="375"><figcaption></figcaption></figure></div>
* If the required permissions are available, the Tessl integrations screen shows **Connected**.

  <div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-dcd22b7164c99f538cf57dfda7fa3ebff57b8e7e%2Fgithub-integration-connected.png?alt=media" alt="" width="375"><figcaption></figcaption></figure></div>

After the connection is created, Tessl may start a first scan of the repositories in scope. The connection can show **Connected** before a scan starts or finishes, or when scan provisioning is unavailable. Open the connection to check its scan status, retry a failed scan, or set its **Scan scope**.

## Review Context and Findings

When scan analysis succeeds and your Organization has an active workspace, Tessl starts indexing the repository skills it found. Without an active workspace, the scan still completes and its Context remains available, but indexing does not start. There is no private-indexing opt-in or manual first-party approval step.

Indexing, review, and security scoring run as separate background work. A skill can appear in **Context** before its scores are available, and scoring can fail without removing the Context item. Some advanced Finding details depend on your Organization's plan.

Select your Organization and open **Context** to browse the skills and configuration Tessl observed. Open **Findings** to inspect issues from the latest account scans. Organization members can view both pages. Organization admins can ignore or restore Findings and manage scan settings. See [Roles](/administrators/roles).

## Add or remove Repositories when *Only Select Repositories* are in use.

If you wish to later add repositories, you will navigate to the Tessl app in GitHub:

* Click *Configure* next to the Tessl App.

  <div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-00490074f54c2655e7deb6e093a7ffb04dbfaf55%2Fimage%20(21)%20(1).png?alt=media" alt=""><figcaption></figcaption></figure></div>
* Select or remove repositories as needed.

  <div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-6953646d89a0786f8a3d215559d9b70a6de72deb%2Fimage%20(22)%20(1).png?alt=media" alt="" width="375"><figcaption></figcaption></figure></div>
* Click *Save*.<br>

  <div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-ed944a27dbd5f678c59ec6cb3a4edfa40141af5b%2Fimage%20(23)%20(1).png?alt=media" alt="" width="81"><figcaption></figcaption></figure></div>

## Troubleshooting

What if the option to **Add** the connection is not available? Confirm that you are an Organization admin, or ask an Organization admin to set up the connection. See [Roles](/administrators/roles).

<div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-e688ae64daf52e8e2fe477f301c0662f72781849%2Fimage%20(25)%20(1).png?alt=media" alt="" width="375"><figcaption></figcaption></figure></div>


# Connect Tessl to GitLab

To enable Tessl on your repositories, you will first navigate to the [Tessl Registry](https://tessl.io/registry) and login, if required.

## Enabling the Integration

* Select your workspace from the workspace switcher in the sidebar, below the organization dropdown.\
  ![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-cab7586e4e5abd3681c884f7680661d6aba51b2a%2Fimage%20\(115\).png?alt=media)
* Open **Settings** in the sidebar, then select the **Integrations** section\
  ![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-4e450775a29cf594dde6ed48e7e24c40dbc8b827%2Fimage%20\(116\).png?alt=media)

  .

  If the integration belongs to your organization rather than this workspace, the workspace Integrations section only summarizes it. Manage the connection from the organization dropdown at the top of the sidebar → **Settings** → **Integrations** → **Manage**. This route needs the organization integration-management permission.
* Select *Add* next to the GitLab integration.

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-9e563299c7f6d5769f53ea4d54e09256b6d1d550%2Fgitlab-integration-add.png?alt=media" alt=""><figcaption></figcaption></figure>

* Provide an access token with the correct [permissions](/distribute/permissions#gitlab-permissions) for a gitlab.com instance. \* If you are using a self hosted or customer-url for your instance, check the Use a custom instance URL box, and provide the necessary URL.<br>

  ```
  <div align="left"><figure><img src="../../../.gitbook/assets/image (2).png" alt="" width="375"><figcaption></figcaption></figure></div>
  ```

## Troubleshooting

What if the option to *Add Integration* isn't available? In the following example Tessl is not enabled and the user does not have the correct [Role](/administrators/roles) within Tessl (manager or owner) so the interface does not provide the option to integrate GitLab. To address this, ensure you have the correct [Role](/administrators/roles) within Tessl or request a user with these permissions to setup the integration.

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-2e2e4b9ade9ab3cc24a822e738a0eba995e8f9b7%2Fimage%20(4).png?alt=media" alt=""><figcaption></figcaption></figure>


# Initialize a repository

Using *Setup* on the Repositories view in [Tessl Registry](https://tessl.io/registry) creates a `tessl.json` file and adds any plugins Tessl has for the dependencies your repository uses. It also creates a `.mcp.json` file so people working in the repository can use Tessl as an MCP server for their coding agent.

## Setup Tessl

* Select your workspace from the workspace switcher in the sidebar, below the organization dropdown.\
  ![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-cab7586e4e5abd3681c884f7680661d6aba51b2a%2Fimage%20\(115\).png?alt=media)
* Click on Repositories from the menu.\
  ![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-a81843b3f3bec2d5c47d2e01a6817e3cbb87cb19%2Fimage%20\(113\).png?alt=media)
* Select *Setup Tessl*.

  <figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-4956af33b9e07bb78a4b244b3777266a214f84d3%2Fimage%20(13)%20(1).png?alt=media" alt=""><figcaption></figcaption></figure>
* Select the agents you wish to use and click *Setup*.

  <div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-4325da86b795f68c25f1b8f2d0d0a0347de59780%2Fimage%20(15)%20(1).png?alt=media" alt="" width="375"><figcaption></figcaption></figure></div>
* Once clicked, a pull request will be created and a popup will be displayed to bring you to the pull request.

  <div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-9751739874f11ea7215fc8dc3d6f3a3492f53e54%2Fimage%20(10)%20(1).png?alt=media" alt="" width="357"><figcaption></figcaption></figure></div>

### The Pull request created

Pull requests may contain updates to `tessl.json` and `.mcp.json` based on your selections and the dependencies the repository contains.

* You or your pipeline automation would merge the request(s).\
  ![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-8a3f097358dad37c46ffd34d4c9646fe5998e6e1%2Fimage%20\(16\)%20\(1\).png?alt=media)

## Troubleshooting

If you do not see your repositories listed, see [Connect Tessl to GitHub](/distribute/connect-tessl-to-github) or [Connect Tessl to GitLab](/distribute/connect-tessl-to-gitlab).

* It's possible you are only allowing specific repositories, in which case you may need to select the additional repositories you wish to display in the GitHub settings or provide a group token with access in GitLab.


# Distribute plugins across multiple repositories

The Tessl web interface allows users, with the correct permissions, to create pull requests adding plugins to selected repositories. The following workflow demonstrates distributing a plugin that has guidelines that must be followed by the agents that company is using. This could include style guides, architecture and other guidelines that you now want to push across your organization.

Before you start, make sure your repositories are connected to Tessl and you have the required [permissions](/distribute/permissions):

* [Connect Tessl to GitHub](/distribute/connect-tessl-to-github) or [Connect Tessl to GitLab](/distribute/connect-tessl-to-gitlab)
* [Initialize a repository](/distribute/initialize-a-repository) if it has not been set up with Tessl yet

To distribute a plugin, follow these steps:

* Navigate to the workspace your plugin resides in.
* Click *Rollout* next to the plugin you wish to push.<br>

  <figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-b99d67b8ca22264ae493fb856b1636d878e62cf3%2Fimage%20(123).png?alt=media" alt=""><figcaption></figcaption></figure>
* Indicate the Tessl workspace to roll the plugin out to.\ <img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-90993390eb6fdc80448ca0a0a33f0d691755bad4%2Fimage%20(7)%20(1).png?alt=media" alt="" data-size="original">
* Select which repositories the pull request will be created in.

  <div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-abafa4a3f30300a5564d7c3a3cf847ab4b5e1310%2Fimage%20(8)%20(1).png?alt=media" alt="" width="375"><figcaption></figcaption></figure></div>
* To complete the action, click *Create a rollout PR*.\
  ![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-5bcfbcba331e28325462156186f332c5b4c0e897%2Fimage%20\(9\)%20\(1\).png?alt=media)
* By doing this it will create a pull request adding the plugin to the tessl.json. A `.tessl-plugin/plugin.json` will be created if Tessl has not been initialized for that repository. An option to view the PR will be displayed:

  <div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-9751739874f11ea7215fc8dc3d6f3a3492f53e54%2Fimage%20(10)%20(1).png?alt=media" alt="" width="357"><figcaption></figcaption></figure></div>
* The PR will then be merged by you or your pipeline automations.


# Removing the integration

## GitHub Integration

To remove the GitHub integration, two steps are required:

\
1\) GitHub: Remove the Tessl App from within your GitHub Organization settings.

2\) Tessl: Click the delete icon next to the integration and confirm.

### GitHub removal

* Navigate to *Applications* and click on *Configure*<br>

  <figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-222fb050f48ed5c26034dcc2a437442b3562a0a4%2Fimage%20(19)%20(1).png?alt=media" alt=""><figcaption></figcaption></figure>
* Scroll and click the option to Uninstall, completing the steps to Uninstall when requested.<br>

  <figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-c576aa2bff5f46545076a65e9145438a47f59d13%2Fimage%20(20)%20(1).png?alt=media" alt=""><figcaption></figcaption></figure>

### Tessl Removal

* Select your workspace from the workspace switcher in the sidebar, below the organization dropdown.\
  ![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-cab7586e4e5abd3681c884f7680661d6aba51b2a%2Fimage%20\(115\).png?alt=media)
* Open **Settings** in the sidebar, then select the **Integrations** section.

  .\
  ![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-ac6951c8331482229a736ba9dfc5bf52cede94be%2Fimage%20\(125\).png?alt=media)

  If the integration belongs to your organization rather than this workspace, the workspace Integrations section only summarizes it. Remove the connection from the organization dropdown at the top of the sidebar → **Settings** → **Integrations** → **Manage**. This route needs the organization integration-management permission.
* Click the delete icon next to the GitHub integration, then click *Uninstall* in the confirmation dialog.

  <div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-dcce412ea9ffef78776df945e9918becc6cb4e16%2Fgithub-remove-confirm.png?alt=media" alt="" width="563"><figcaption></figcaption></figure></div>

***

## Gitlab integration

To remove the GitLab integration, two steps are required:

1\) Tessl: Click the delete icon next to the integration and confirm.

2\) GitLab, delete the integration token if it is no longer in use.

### Tessl & GitLab removal

* Select your workspace from the workspace switcher in the sidebar, below the organization dropdown.\
  ![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-cab7586e4e5abd3681c884f7680661d6aba51b2a%2Fimage%20\(115\).png?alt=media)
* Open **Settings** in the sidebar, then select the **Integrations** section.\
  ![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-f95952143a3aa19b1074aacfbca8165789cf1656%2Fimage%20\(128\).png?alt=media)

  .

  If the integration belongs to your organization rather than this workspace, the workspace Integrations section only summarizes it. Remove the connection from the organization dropdown at the top of the sidebar → **Settings** → **Integrations** → **Manage**. This route needs the organization integration-management permission.
* Click the delete icon next to the GitLab integration.
* Enter in the requested value\
  ![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-14f456a09b25b01cf5a1a9f690322af0e2bd9a05%2Fimage%20\(4\)%20\(1\).png?alt=media)

### Gitlab removal

For security purposes, it is generally good practice to delete old tokens that are no longer used. Navigate to the correct token area in Gitlab and delete the token used for the integration

<div align="left"><figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-5cfad8449391d2850c4af53de620268a1880c8da%2Fimage%20(5).png?alt=media" alt="" width="98"><figcaption></figcaption></figure></div>


# Using plugins directly from your repo

Use plugins directly from your repository for codebase-specific context

Instead of publishing plugins to the Tessl Registry, you can also check them directly into your repository. This is a simpler way to distribute plugins together with your code.

## When to check plugins into your repo vs using the registry

**Check plugins into your repo when:**

* You want a simple distribution method - plugins travel automatically with your code
* Context is tightly coupled to this specific codebase
* You don't need version management across multiple projects

**Use the Tessl Registry when:**

* You need centralized management and updates
* You want evaluation and review workflows
* You need automated distribution across multiple repositories
* You want to keep plugins up to date independently from code changes

## How it works

Repository-specific plugins are committed to your codebase and referenced in `tessl.json` with a `file:` source. Team members automatically get the plugin when they clone the repo and run `tessl install`.

## Setup

### 1. Create the plugin in your repository

<pre class="language-bash"><code class="lang-bash">cd my-repo
<strong>tessl plugin new --name myworkspace/my-plugin --path ./plugins/my-plugin
</strong></code></pre>

This creates the plugin structure for skills or rules:

```
my-repo/
├── plugins/my-plugin/
│   ├── .tessl-plugin/plugin.json
│   └── skills
│       └── my-plugin/
│          └── SKILL.md
│   └── rules/                 # This repo's conventions
│       └── standards.md
└── tessl.json
```

### 2. Install it locally

Add the plugin to your project by referencing it in `tessl.json` with a `file:` source:

```bash
tessl install file:./plugins/my-plugin
```

This updates your `tessl.json`:

```json
{
  "name": "my-project",
  "dependencies": {
    "myworkspace/my-plugin": {
      "version": "1.0.0",
      "source": "file:./plugins/my-plugin"
    }
  }
}
```

### 3. Commit both to version control

```bash
git add plugins/my-plugin/ tessl.json
git commit -m "Add repository-specific context plugin"
```

## Team workflow

When team members clone the repository:

```bash
git clone <repo>
cd <repo>
tessl install
```

The plugin installs automatically from the `file:` source in `tessl.json`. Everyone gets the same codebase-specific context.

## Updating the plugin

Changes to the plugin go through your normal code review process:

1. Edit plugin content (rules, skills)
2. Commit changes: `git commit -m "Update architecture docs"`
3. Create pull request
4. Team reviews context changes like code changes
5. Merge to main

Team members get updates when they pull and run `tessl install`.

## Benefits

* **Versioned with code** - plugin evolves alongside the codebase
* **Automatic distribution** - Team gets context when they clone
* **Code review** - Changes to patterns/architecture reviewed via PR
* **Private** - Context stays internal, no publishing needed
* **Self-documenting** - Codebase carries its own context

## Example: Monorepo workspaces

In monorepos, create workspace-specific plugins:

```
my-monorepo/
├── frontend/
│   ├── plugins/my-plugin/           # Frontend patterns
│   │   ├── .tessl-plugin/plugin.json
│   │   ├── skills/            # Frontend Testing methodology
│   │   └── rules/             # UI standards
│   └── tessl.json             # References frontend plugin
├── backend/
│   ├── plugins/my-plugin/           # Backend patterns
│   │   ├── .tessl-plugin/plugin.json
│   │   ├── skills/            # Database migration process
│   │   └── rules/             # Backend standards
│   └── tessl.json             # References backend plugin
└── shared/
    └── plugins/my-plugin/           # Cross-cutting concerns
        └── skills/            # Unified Code Review
```

Each workspace's `tessl.json` references its local plugin and any shared plugins:

```json
{
  "dependencies": {
    "myworkspace/frontend-context": {
      "source": "file:./plugins/my-plugin"
    },
    "myworkspace/shared-context": {
      "source": "file:../shared/plugins/my-plugin"
    }
  }
}
```

## Related

* [Developing plugins locally](/creating-skills-and-plugins/develop-and-test-locally) - Create and test plugins with local development workflow before checking them into your repo
* [Configuration files](/reference/configuration#plugin-configuration) - Complete plugin.json reference


# Promote or claim a skill you have created

Have you authored a skill in GitHub and want to promote it using Tessl? Tessl allows you to take a skill from a local repository or GitHub repository, and generate the plugin.json, which is essential for Tessl plugins and Skills. Once imported, you will publish the plugin to make it available, publicly or privately, to your teams. The following section discusses organization of your skills and how to publish them. Additionally if you have authored a plugin on GitHub that appears in Tessl, there is a section on [claiming that skill in Tessl](#claim-a-tessl-skill).

## Prerequisites

* Tessl [installed](/introduction-to-tessl/set-up-tessl/installation)
* Tessl logged in
* Skill code available locally
* Access to a [workspace](/reference/workspaces)

## Mapping and importing your skills into Tessl plugins

If you've authored a number of skills and documents to help your agents, you can logically group the related files together and create individual skill plugins in Tessl. For example:

* If you're developing a "data migration" skill, place all the files related to that skill and documentation in a single folder or repository. This would be a single plugin.

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-dad51a1546972f9536fddabab6c461502f5c39df%2Fimage%20(137).png?alt=media" alt="" width="375"><figcaption></figcaption></figure>

* Similarly if you've built a skill for "building a front end", that might contain best practices and design skills you would group them together into its own plugin, create the plugin.json, by running the import command, then lint, review, eval and publish the skill to your workplace.

<div align="center"><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-85e5ff00d0e727ce20c3ae0616c84f06fceed912%2Fimage%20(132).png?alt=media" alt=""></div>

### Anti-patterns

To avoid performance issues and confusion of your agents, avoid throwing in the kitchen sink! Make your skill purpose built, providing the information for specific tasks.<br>

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-480f0d7367cc19522ab8aa7d660c00fedf2d8cc3%2Fimage%20(135).png?alt=media" alt="" width="375"><figcaption></figcaption></figure>

## Clone the Repository

Clone your repository locally.

## Create a .tessl-plugin/plugin.json

* Create a [.tessl-plugin/plugin.json](/reference/configuration#plugin-configuration) (metadata describing it) by running the following command, with your workspace and folder location values:<br>

  ```
  tessl skill import ./<directory with SKILL.md> --workspace <myworkspace>
  ```
* You can create an eval file in `/eval` or request one through the web interface. More on this coming soon!

## Improve your skill with Lint, Review, and Evaluate your Skill

### Review your Skill against best practices

Before publishing, validate your skill structure and contents:

```sh
tessl skill lint ./my-skill
```

This command checks:

* `SKILL.md` format and structure
* Required frontmatter fields (name, description)
* Conformance to the Agent Skills specification
* Markdown validity

To optimize your skill, run the following:

```sh
tessl review fix ./my-skill
```

Fix any errors before proceeding.

### Evaluate quality using Scenarios

Once you've built your skill the final step before publishing is to create scenarios your skill can be evaluated against. The following document illustrates how you would accomplish this:

* [Evaluate skill quality using scenarios](/improving-your-skills/evaluate-skill-quality-using-scenarios)

## Set up GitHub Action to publish to Tessl's Registry

The following steps introduce the [GitHub action](https://github.com/tesslio/setup-tessl) to your repository to publish your plugin automatically.

Using the GitHub action will automatically link your plugin to the repository, and all skills we previously indexed in the repository will be hidden and redirected to the plugins.\
\
See Tessl's [setup-tessl GitHub action](https://github.com/tesslio/setup-tessl) for additional options and information.

{% stepper %}
{% step %}
**Create your workflow**

Create your workflow file `.github/workflows/tessl-publish.yml`. [Here's an example action for auto-publishing](https://github.com/tesslio/setup-tessl?tab=readme-ov-file#publish-a-plugin-on-push).
{% endstep %}

{% step %}
**Create an API Key**

Log into [Tessl](https://tessl.io) on web UI. Open your workspace, navigate to **Settings**, then **API keys**. Create a new API key. Copy the value as it will only be shown once.

![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-1e50df505de3847db2140f74e35cb8454edcdb30%2Fimage.png?alt=media)
{% endstep %}

{% step %}
**Add your API Key as a Repository Secret**

In GitHub, navigate to your repo **Settings**, select **Secrets and variables**, under actions find **New Repository Secret**. Provide the name: `TESSL_TOKEN` and the value is the Tessl API key you procured in the earlier step.
{% endstep %}

{% step %}
**Run your workflow**

Kick off your workflow by running the relevant trigger, or manually triggering from the UI if you enabled that in your workflow definition.
{% endstep %}
{% endstepper %}

## Claim a Tessl Skill

Found a skill in Tessl that is created/indexed from your Skill/GitHub repository of skills? You'll want to claim ownership and disable indexing by Tessl. To do this, you will:

* Recreate the skill in your workspace by importing it locally, update the workspace in `.tessl-plugin/plugin.json`.
* Use the [GitHub action to publish](https://github.com/tesslio/setup-tessl?tab=readme-ov-file#publish-a-plugin-on-push) , which will manage the Skill and ensure changes to the repo are reflected in your Tessl workspace and not reindexed by Tessl.

{% hint style="info" %}
If you accidentally use the CLI to publish a skill to your workspace, and the skill was already indexed by Tessl in the Tessl workspace, you would find two versions, in two different workspaces, when searching for that skill. You can [contact Tessl support](/support/giving-feedback) to claim your plugin and address the duplicate. Be sure to set up the GitHub action as part of this.
{% endhint %}


# Review, lint & publish with GitHub Actions

The following steps introduce the [GitHub action](https://github.com/tesslio/setup-tessl) to your repository. This action sets up `tessl` so it can be used for any CI/CD use case, such as auto-publishing plugins, linting or running skill reviews on PRs, etc.

If you're migrating your skill into Tessl's registry, using the GitHub action will automatically link your plugin to the repository, and all skills we previously indexed in the repository will be hidden and redirected to the plugins.\
\
See Tessl's [setup-tessl GitHub action](https://github.com/tesslio/setup-tessl) for additional options and information.

{% stepper %}
{% step %}
**Create your workflow**

Create your workflow file in `.github/workflows` for your desired action. For example, [here's how you'd set up an auto-publish action](https://github.com/tesslio/setup-tessl?tab=readme-ov-file#publish-a-plugin-on-push).
{% endstep %}

{% step %}
**Create an API Key**

Log into [Tessl](https://tessl.io) on web UI. Open your workspace, navigate to **Settings**, then **API keys**. Create a new API key. Copy the value as it will only be shown once.

![](https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-1e50df505de3847db2140f74e35cb8454edcdb30%2Fimage.png?alt=media)
{% endstep %}

{% step %}
**Add your API Key as a Repository Secret**

In GitHub, navigate to your repo **Settings**, select **Secrets and variables**, under actions find **New Repository Secret**. Provide the name: `TESSL_TOKEN` and the value is the Tessl API key you procured in the earlier step.
{% endstep %}

{% step %}
**Run your workflow**

Kick off your workflow by running the relevant trigger, or manually triggering from the UI if you enabled that in your workflow definition.
{% endstep %}
{% endstepper %}

## Gating pull requests with Tessl Review

Once `TESSL_TOKEN` is set as a repository secret, you can gate a pull request on a skill's review score using `tessl review run`. This is the authenticated, asynchronous review path: it requires auth and a workspace, kicks off a review pipeline server-side, and polls until the review completes. With `--json --threshold`, the command exits non-zero when the score falls below the threshold, failing the check.

The job below reviews a skill and fails if its score is under 80%:

```yaml
name: Skill review

on:
  pull_request:

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: tesslio/setup-tessl@v2
        with:
          token: ${{ secrets.TESSL_TOKEN }}
      - name: Review skill
        run: tessl review run ./my-skill --workspace engteam --json --threshold 80
```

`--workspace` is required whenever `--json` is set, since CI runs non-interactively and cannot prompt for a workspace. Point the path at the skill file or skill directory you want to review.

For the full flag reference, see [`tessl review run`](/reference/cli-commands#tessl-review-run). For more on running reviews and the companion commands, see [Check a skill's quality using review](/improving-your-skills/reviewing-skills).


# Overview

Understand what Tessl projects are and why we need them.

A Tessl project gives Tessl a stable place to attach eval runs and other repository-connected data, so your results stay tied to your codebase over time.

### Why projects exist

Tessl needs a project so it knows where an eval belongs. That way your results aren’t just a one-off run — they stay linked to the repository, so you can find them later, compare runs, and keep improving the same codebase.

### Common workflows

#### Create a new project

Use `tessl project create` when this repository is not linked yet and you want to create a new Tessl project. The command creates the project, then records the project link in `tessl.json`, creating the file if it does not exist yet. `tessl init` sets up local agent configuration and bootstraps `tessl.json` if it does not exist, but it does not create or link a remote project on its own; run `tessl project create` or `tessl project link` for that.

#### Link to an existing project

Use `tessl project link` when the Tessl project already exists for that repository.

#### Repair a project link

Use `tessl project repair` when the project link is missing, broken, outdated, or linked to the wrong project.

### Current limitations

Projects currently have limited fallback when a directory is not a Git repository.

### See also

* [Manage projects from the CLI](/projects/manage-projects-from-the-cli)
* [Projects in the app](/projects/projects-in-the-app)


# Manage projects from the CLI

Set up and repair project links from the Tessl CLI.

Start with `tessl init` or `tessl project create`. Both commands recognize a Tessl project that already matches the current repository and link it automatically when there is one clear match.

If you're blocked when starting an eval, this is usually the page you need.

Creating a Tessl project does not create a new Git repository. It creates the Tessl project for the repository you are already working in.

### When you are asked to create a project

Tessl asks for a project when it needs to identify the repository behind the current directory.

This happens most often when you run an eval from a repository that is not linked yet.

### Automatic setup

Run `tessl init` to set up Tessl and your coding agents. If the repository already has a project that Tessl discovered from GitHub, `tessl init` records that link in a new `tessl.json`. It does not create a duplicate project.

Run `tessl project create` when you only need to set up the project link. The command creates `tessl.json` if it is missing, then:

* Links the project automatically when exactly one project matches the repository and package path
* Creates a project when none match
* Shows the matching projects and the resolving command when more than one match is available

Both commands show the repository, matched project, manifest link, and next action after an automatic match.

### Which explicit command should I use?

| Command                | Use it when                                      | What it does                                                                    |
| ---------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------- |
| `tessl project create` | This directory is not linked yet                 | Links one matching project automatically, or creates a project when none match  |
| `tessl project link`   | Several projects match and you need to choose    | Links this directory to the selected existing project                           |
| `tessl project repair` | An existing local link is missing, wrong, or old | Inspects the link and applies an explicit relink, source update, or name update |

### `tessl project create`

Sets up the Tessl project link for the current repository checkout.

Use this when the current repository is not linked yet. The command checks the projects across your organization before it creates anything.

If no `tessl.json` is found in this directory or any parent directory, the command creates one before proceeding. You do not need to run `tessl init` first.

If a `tessl.json` already exists in a parent directory (but not in the current directory), the command stops and asks you to pass `--nested`. Run it again with `--nested` to create a new `tessl.json` in the current directory and a separate project for it, rather than reusing the parent project.

When one existing project matches, the command records its full `workspace/project` link in `tessl.json`. When no project matches, it creates one and records the new link. Use `--new` with an explicit workspace and project name only when you intentionally need another project for the same repository.

### `tessl project link`

Links the current repository to an existing Tessl project.

Use this when several projects match and you need to choose one explicitly.

This command only links to an existing project. It does not create a new one.

If you want to create a brand new project for this repository, use `tessl project create` instead.

### `tessl project repair`

Inspects or repairs the Tessl project link for the current repository.

Use this when the repository should already be linked to a project, but the link is missing, broken, outdated, or no longer matches the expected repository.

Use `--relink` to connect the repository to a different existing project, `--update-source` to keep the linked project but approve this repository as its source, and `--update-name` to rename the linked project to match `tessl.json`.

### What happens after linking

Once a repository is linked:

* Evals attach to that Tessl project
* The CLI and app can show one shared evaluation history


# Repairing projects

Understand how to diagnose and fix issues with projects.

Use `tessl project repair` when Tessl detects that the project identity for your current repository no longer matches the project it is linked to.

A Tessl project has two parts of identity:

* its **locator** — the project name in `tessl.json`
* its **source** — the repository Tessl sees for the current directory

If those drift apart, Tessl can stop evals from running until you repair the association.

### What `tessl project repair` does

`tessl project repair` helps you diagnose and fix **project identity drift** for the current repository.

Depending on the problem, you can use it to:

* inspect the current mismatch
* **relink** this repository to a different existing Tessl project
* **update the source** recorded for the currently matched Tessl project
* **update the project name** to match a local rename

### Start by diagnosing

Run this first:

```bash
tessl project repair
```

This checks the current repository and shows whether the problem is:

* a **locator drift**
* a **source drift**
* a **rename drift**
* a more complete mismatch that needs relinking or project creation

If you just want machine-readable output, use:

```bash
tessl project repair --json
```

### When to use each repair action

### Relink to a different project

Use this when your current repository is linked to the **wrong Tessl project**.

```bash
tessl project repair --relink --workspace <workspace> --project <project> --yes
```

Use `--relink` when:

* Tessl matched the wrong project
* your repository should belong to a different existing project
* you are in a monorepo and the current package or subpath was associated with the wrong project
* the locator in `tessl.json` is missing, stale, or points at the wrong project

#### Example: locator drift

Your current repository source matches an existing Tessl project, but the project named in `tessl.json` does not.

For example:

* local `tessl.json` does **not** point to the right project
* Tessl can see that the current repo actually matches `team-two/real-project`

In that case, relink the current repository to the correct project:

```bash
tessl project repair --relink --workspace team-two --project real-project --yes
```

### Update the source for the current project

Use this when the **project is correct**, but Tessl has the wrong **repository source** recorded for it.

```bash
tessl project repair --update-source --yes
```

Use `--update-source` when:

* the repo remote changed
* the repo was renamed or transferred
* the repo subpath changed in a way Tessl should now treat as the project’s canonical source
* Tessl found the right project by name, but the current repository source no longer matches the stored source

#### Example: repoistory name change

Your `tessl.json` still points to the correct project, but the underlying repository source has changed.

For example:

* locator: `team-one/old-name`
* recorded source: `https://github.com/acme/old-name`
* current local source: `https://github.com/acme/new-name`

In that case, keep the same project and update its recorded source:

```bash
tessl project repair --update-source --yes
```

Use this when the project identity is still right and only the source has drifted.

### Update the project name

Use this when the **repository is still linked correctly**, but the **project name has changed** locally.

```bash
tessl project repair --update-name --yes
```

Use `--update-name` when:

* you renamed the project in `tessl.json`
* the repo still matches the existing project source
* you want the Tessl project name to match the new local name

#### Example: rename drift

Your repository still matches the same project source, but the name in `tessl.json` changed.

For example:

* old name: `team-one/old-project`
* new local name: `team-one/renamed-project`

In that case, repair the name without relinking or recreating the project:

```bash
tessl project repair --update-name --yes
```

### How to decide which one to use

Use:

* `--relink` when the repository should point to a **different existing project**
* `--update-source` when the **project is right** but its **recorded source is outdated**
* `--update-name` when the **project is right** but its **name changed**

A simple rule:

* wrong project → **relink**
* wrong repo source → **update source**
* wrong project name → **update name**

### When repair is not the right command

You usually do **not** need repair when:

* you are creating a Tessl project for the first time
* there is no existing project association yet
* Tessl is asking you to create a project before your first eval

In those cases, use the normal create or link flow instead.

## Project visible in the web app but missing after cloning

**Symptom.** The project appears in the Tessl web app. A fresh clone has no `tessl.json`, and `tessl install` or `tessl init` behaves as if no project exists.

**Cause.** A teammate created the project but did not commit `tessl.json` to the repository. Without it the CLI has no stored project link and cannot connect automatically. Running `tessl install` blocks when the backend confirms a match, and `tessl init` offers to link inline when the session is interactive and exactly one project matches.

### The `tessl install` block

When the CLI confirms the backend already has a project for this repository, `tessl install <plugin>` exits with an error rather than creating a second, disconnected project:

```
A Tessl project already exists for this repository but no tessl.json was found.
To link this directory to the existing project, run:

  tessl init
  tessl project repair --relink --workspace engteam --project my-project --yes

Ask the teammate who created the project to commit tessl.json, so the next clone links to it.
```

When more than one project matches, the relink command uses placeholders and a `Matching projects:` list follows, one `workspace/project` per line, so you can choose the right one and fill in the values.

If the backend check cannot run (authentication failure, network error, or timeout), `tessl install` falls through to normal initialization instead of blocking.

### The `tessl init` prompt

#### Interactive session with one matching project

When the CLI confirms a single matching project and the session is interactive, `tessl init` asks:

```
Link this directory to engteam/my-project? (Y/n)
```

* **Yes**: writes `tessl.json` already linked to the existing project. No relink step is needed.
* **No**: writes an unlinked `tessl.json` and prints the relink command after `Init complete!`.

#### Non-interactive session, declined prompt, or multiple matching projects

When init cannot link inline, it writes an unlinked `tessl.json` and prints the relink command after `Init complete!`.

**Single matching project**: the printed command includes the project's workspace and name:

```bash
$ tessl project repair --relink --workspace engteam --project my-project --yes
```

**Multiple matching projects**: the printed command uses placeholders, followed by a list of candidates:

```
tessl project repair --relink --workspace <name-or-id> --project <name> --yes

Matching projects:
  engteam/project-a
  engteam/project-b
```

Choose the correct project from the list and substitute its workspace and name into the command.

### Running from a subdirectory

When the CLI confirms the project is registered at the repository root but the command runs from a subdirectory:

* `tessl install` exits with an error that names the repository root and the recovery commands to run from there.
* `tessl init` stops without writing anything and tells you to run `tessl init` from the repository root.

Move to the repository root and run the recovery from there. If the backend check cannot run, both commands fall through to normal initialization instead.

### The commit nudge

When `tessl init` creates a new `tessl.json` that is not yet tracked by git, the CLI prints:

```
Commit tessl.json so teammates connect to the same project. Leaving it uncommitted is the most common cause of project mismatch errors.
```

This nudge fires when the manifest is linked, either for a fresh project or after the user accepts the link prompt. When init writes an unlinked manifest (user declined or non-interactive path), the nudge is suppressed; run the relink command first, then commit.

### Prevention

Commit `tessl.json` immediately after running `tessl init` or `tessl project create`. Every teammate who clones the repository will then connect to the same project without needing to relink.


# Projects in the app

Browse organization-level projects and review project-level activity in the Tessl app.

Projects are visible at the organization level and in project-specific views.

Each Tessl project represents a repository, so the app can show repository-level eval history in one place.

Projects are stored in a workspace for permissions and compatibility, but appear in the organization that owns that workspace.

### Organization Projects list

Use the Projects list to:

* Browse projects across your organization
* Filter projects by their backing workspace
* Open a project's detail view

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-849fe97b13cff0ea91ab4abc0da3b5eb969bc8b3%2Forganization-projects-list-2026-09-14.png?alt=media" alt=""><figcaption></figcaption></figure>

### Project eval views

Each project has app views that show project-level evaluation history.

Use these views to:

* Inspect evaluation history over time
* Review results connected to the same repository
* Understand the state of evaluations for a codebase

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-d59d435b6054993d5e36a5db2b366eb47b4c393b%2Fproject-eval-views-2026-08-10.png?alt=media" alt=""><figcaption></figcaption></figure>


# CLI commands

The Tessl CLI provides commands for managing plugins, workspaces, authentication, repository configuration, and Tessl projects. This page provides a comprehensive reference for all available commands.

{% hint style="info" %}
On this page, **repository** means your Git repository. **Tessl project** means the stable home for Tessl in that repository.
{% endhint %}

## Getting started

### tessl help command

To see all available commands, run:

```sh
tessl --help
```

To get detailed information about a specific command or command group, use the `--help` flag:

```sh
tessl <command> --help
tessl <command-group> <subcommand> --help
```

***

## Authentication

### tessl login

Authenticate with Tessl.

**Usage**

```shell-session
$ tessl login [--device | --loopback]
```

Use `--device` to sign in with a device code instead of a local browser redirect. Use `--loopback` to sign in with a local browser redirect. Use only one of these flags.

Organizations using the EU region must have `TESSL_ENV=app-eu` set in the CLI's environment and use device login:

```shell-session
$ TESSL_ENV=app-eu tessl login --device
```

See [Using the EU region](/administrators/eu-region) for the full setup, including shell profiles, CI, and MCP servers.

**Alternative**

```sh
tessl auth login
```

### tessl logout

Sign out and clear credentials.

**Usage**

```sh
tessl logout
```

**Alternative**

```sh
tessl auth logout
```

### tessl whoami

Show current authenticated user.

**Usage**

```sh
tessl whoami
```

**Alternative**

```sh
tessl auth whoami
```

***

## Setup and initialization

### tessl init

Set up your repository and configure your coding agent.

**Usage**

```sh
tessl init [--agent <name>]... [--name <name>]
```

**Flags**

* `--agent` (string, optional, repeatable): Configure MCP for coding agent. Supported values: `claude-code`, `cursor`, `gemini`, `antigravity`, `codex`, `openhands`, `openclaw`, `copilot`, `copilot-vscode`, `agents`, `tessl-agent`. Can specify multiple agents. See [Custom Agents](/reference/custom-agent-setup) for information on configuring additional agents.
* `--name` (string, optional): Project name for new `tessl.json` (default: current directory name)

**Notes**

* Configures MCP server settings for AI coding agents
* Creates a `tessl.json` manifest at the repository root

**Examples**

```sh
# Basic initialization with default settings
tessl init

# Initialize and configure Cursor integration
tessl init --agent cursor

# Initialize with multiple agents
tessl init --agent cursor --agent claude-code
```

### tessl project

Create, link, or repair the Tessl project for this repository.

A Tessl project gives Tessl a stable place to attach eval runs and other repository-connected data, so your results stay tied to your codebase over time.

Use these commands to create a new project, link this checkout to an existing project, or repair a missing or broken reference in `tessl.json`. For a workflow guide, see [Manage projects from the CLI](/projects/manage-projects-from-the-cli).

**Usage**

```sh
tessl project <create|link|repair>
```

### tessl project create

Create a new Tessl project for the current repository.

**Usage**

```sh
tessl project create [--nested] [--new] [--workspace <name-or-id>] [<project-name>]
```

**Flags**

* `--nested` (boolean, optional): Required when a parent directory has a `tessl.json` but the current directory has none. Creates a separate `tessl.json` and project for the current directory instead of reusing the parent project.
* `--new` (boolean, optional): Create a new project for a different workspace. Use with `--workspace` and provide a project name when you intentionally want a separate new project for the same repository.
* `--workspace` (string, optional): Name or ID of workspace

**Arguments**

* `project-name` (string, optional): Name for the new project. If omitted, you'll be prompted.

**Notes**

* Use this when this repository is not linked to a project yet and you want to create one in Tessl
* This command always creates a new project
* If this repository should use a project that already exists, use `tessl project link` instead

### tessl project link

Link the current repository to an existing Tessl project.

**Usage**

```sh
tessl project link [--workspace <name-or-id>]
```

**Flags**

* `--workspace` (string, optional): Name or ID of workspace to search

**Notes**

* Use this when the project already exists in Tessl and you want this repository to use it
* This command only links to an existing project
* If you want to create a brand new project for this repository, use `tessl project create` instead

### tessl project repair

Inspect or repair the Tessl project link for the current repository.

**Usage**

```sh
tessl project repair [--json] [--relink] [--workspace <name-or-id>] [--project <name>] [--update-source] [--update-name] [--yes]
```

**Flags**

* `--json` (boolean, optional): Output as JSON, for automation or agent workflows
* `--relink` (boolean, optional): Relink this repository to a different existing project
* `--workspace` (string, optional): Workspace name or ID for `--relink`
* `--project` (string, optional): Project name for `--relink` target
* `--update-source` (boolean, optional): Approve this repository as the linked project source
* `--update-name` (boolean, optional): Rename the linked project to match `tessl.json`
* `--yes` / `-y` (boolean, optional): Confirm and apply requested repair

**Notes**

* Use this when this repository should already be linked to a project, but the link is missing, broken, outdated, or no longer matches the expected repository or project details

### tessl project list

List all projects in a workspace.

**Usage**

```sh
tessl project list [--workspace <name>] [--json]
```

**Flags**

* `--workspace` (string, optional): Name or ID of workspace.
* `--json` (boolean, optional): Output as JSON.

**Notes**

* Shows every project you can access in the selected workspace, with its name, ID, and source.
* The workspace is resolved in this order: `--workspace`, the `TESSL_WORKSPACE` environment variable, the project linked in `tessl.json`, then the workspace prefix in a `.tessl-plugin/plugin.json` when no `tessl.json` exists. If none of those apply and you belong to more than one workspace, you are prompted to pick one, or must pass `--workspace` in a non-interactive shell.

**Examples**

```sh
# List projects in the resolved workspace
tessl project list

# List projects in a specific workspace
tessl project list --workspace engteam

# Output as JSON
tessl project list --json
```

***

## Project dependencies

### tessl install

Install plugins into your repository. plugins are versioned bundles of reusable, agent-agnostic context that make coding agents more effective. They can contain skills, documentation, and rules.

**Alias**: `tessl i`

**Usage**

```sh
tessl install [--global] [--skill <value>]... [--yes] [--verbose] [--watch-local] [--accept-warnings] [--strict] [--agent <name>]... <source>...
```

**Arguments**

* `source` (string, required): plugin or GitHub URL to install. Can be:
  * Registry plugin: `workspace/plugin[@version]` (version optional, defaults to latest)
  * GitHub repository:
    * Full URL: `https://github.com/owner/repo`
    * Shorthand: `github:owner/repo` (e.g., `github:softaworks/agent-toolkit`)
  * File path: `file:<path>` to install from local directory

**Flags**

* `--global` / `-g` (boolean, optional): Install plugins globally to `~/.tessl/` instead of the current repository. Global plugins are available across all your repositories.
* `--skill` (string, repeatable, optional): Select specific skills to install from GitHub repositories. Used when a repository contains multiple skills. If omitted, you'll be prompted interactively to select from available skills. Can be specified multiple times to install multiple skills from the same repository.
* `--yes` (boolean, optional): Skip confirmation prompts and auto-select all skills
* `--verbose` / `-v` (boolean, optional): Show detailed warning messages during installation
* `--watch-local` (boolean, optional): Watch local file-source plugins and reinstall on changes
* `--accept-warnings` (boolean, optional): Pre-accept install policy warnings (no interactive prompt)
* `--strict` (boolean, optional): Fail if a version is too new rather than installing an older fallback
* `--agent` (string, optional, repeatable): Override agents to install for. Supported values: `claude-code`, `cursor`, `gemini`, `antigravity`, `codex`, `openhands`, `openclaw`, `copilot`, `copilot-vscode`, `agents`, `tessl-agent`.

**Notes**

* Plugins are installed to `.tessl/plugins/<plugin-name>/` in your repository (or `~/.tessl/plugins/` when using `--global`)
* Your repository's `tessl.json` is automatically updated with the plugin reference
* If a plugin is already installed and up-to-date, it will be skipped
* **GitHub URL formats**: `https://github.com/owner/repo`, `github:owner/repo`, or with branch/path: `https://github.com/owner/repo/tree/main/skills/pdf`
* **File system installation**: Use `file:<path>` to install from a local directory (`.tessl-plugin/plugin.json` expected in the path)
* **Install policy warnings**: If a plugin triggers install policy warnings (such as critical or high Snyk security findings), you will be asked for permission before installation continues. You can always choose to proceed — installation is never blocked. Use `--accept-warnings` to pre-accept these warnings and skip the prompt.

**Examples**

```sh
# Install a specific plugin from registry
tessl install tessl/svelte@5.38.0

# Install multiple plugins from registry
tessl install tessl/svelte tessl/react

# Install from file system (.tessl-plugin/plugin.json is expected in the specified path)
tessl install file:<path to plugin>

# Install from GitHub repository with interactive skill selection
tessl install https://github.com/anthropics/skills

# Install using GitHub shorthand syntax
tessl install github:softaworks/agent-toolkit

# Install specific skill by name from GitHub repository (when repo has multiple skills)
tessl install https://github.com/anthropics/skills --skill pdf

# Install multiple skills by name from GitHub repository using shorthand
tessl install github:anthropics/skills --skill pdf --skill pptx

# Install from specific branch and path in GitHub repository
tessl install https://github.com/anthropics/skills/tree/main/skills/pdf

# Install from a specific commit (useful when the registry shows security results for that commit)
tessl install engteam/my-skill@abc1234

# Install a plugin globally (available across all repositories)
tessl install --global tessl/svelte
```

### tessl search

Search for plugins and skills in the Tessl registry by name, PURL, or HTTP URL.

**Usage**

```sh
$ tessl search [--global] [--type skills|docs|rules] [--org <slug-or-id> | --workspace <org/workspace-or-id>] [--json] [<query>]
```

**Flags**

* `--global` / `-g` (boolean, optional): Install selected plugins and skills globally to `~/.tessl/` instead of the current project
* `--type` (string, optional): Filter by content type. Supported values: `skills`, `docs`, `rules`
* `--org` (string, optional): Limit results to an organization you can access, identified by its slug or ID
* `--workspace` (string, optional): Limit results to a workspace you can access, identified by `org/workspace` or its ID
* `--json` (boolean, optional): Output search results as JSON

`--org` and `--workspace` require authentication and cannot be used together.

**Arguments**

* `query` (string, optional): Search query (name, PURL, or HTTP URL). If omitted, you'll be prompted interactively.

**Examples**

```sh
# Search for plugins matching "svelte"
$ tessl search svelte
# Search and install selected results globally
$ tessl search --global svelte

# Search for skills in an organization
$ tessl search --type skills --org acme code-review

# Search for skills in a workspace
$ tessl search --type skills --workspace acme/engteam code-review

# Interactive search
$ tessl search
```

### tessl uninstall

Uninstall plugins from your repository.

**Usage**

```sh
tessl uninstall [--global] [--skill <value>]... <workspace/plugin>
```

**Arguments**

* `workspace/plugin` (string, required): Full plugin name in the format `workspace/plugin`

**Flags**

* `--global` / `-g` (boolean, optional): Uninstall plugins from the global `~/.tessl/` directory instead of the current repository
* `--skill` (string, repeatable, optional): Remove specific skills instead of the entire plugin (repeatable)

**Notes**

* Removes the plugin from `tessl.json` and deletes files from `.tessl/plugins/`
* Does not require authentication (local operation only)

**Examples**

```sh
# Uninstall a plugin from the current repository
tessl uninstall tessl/svelte

# Uninstall a globally installed plugin
tessl uninstall --global tessl/svelte
```

### tessl list

List all installed plugins in your repository.

**Usage**

```sh
tessl list [--global] [--json]
```

**Flags**

* `--global` / `-g` (boolean, optional): List plugins installed in global `~/.tessl/` instead of the current repository
* `--json` (boolean, optional): Output plugins as JSON instead of human-readable format

**Notes**

* Lists all plugins installed in your repository based on `tessl.json`
* Shows plugin names in the format `workspace/plugin` with their versions
* Does not require authentication (local operation only)

**Examples**

```sh
# List all installed plugins
tessl list

# List plugins in JSON format
tessl list --json
```

### tessl outdated

Check for available plugin updates.

**Usage**

```sh
tessl outdated [--json]
```

**Flags**

* `--json` (boolean, optional): Output as JSON

**Examples**

```sh
# Check for outdated plugins
tessl outdated

# Output as JSON
tessl outdated --json
```

### tessl update

Update plugins to newer versions.

**Usage**

```sh
tessl update [--yes] [--force] [--global] [--accept-warnings] [--agent <name>]... [<plugin>]
```

**Flags**

* `--yes` / `-y` (boolean, optional): Skip prompts and update all compatible plugins
* `--force` / `-f` (boolean, optional): Include breaking updates
* `--global` / `-g` (boolean, optional): Update plugins installed globally in `~/.tessl/` instead of the current repository
* `--accept-warnings` (boolean, optional): Pre-accept install policy warnings (no interactive prompt)
* `--agent` (string, optional, repeatable): Override agents to sync for. Supported values: `claude-code`, `cursor`, `gemini`, `antigravity`, `codex`, `openhands`, `openclaw`, `copilot`, `copilot-vscode`, `agents`, `tessl-agent`.

**Arguments**

* `plugin` (string, optional): plugin to update in the format `workspace/plugin`. If omitted, updates all plugins.

**Notes**

* If a plugin triggers install policy warnings in the new version (such as critical or high Snyk security findings), you will be asked for permission before the update is applied. Use `--accept-warnings` to pre-accept these warnings and skip the prompt.

**Examples**

```sh
# Update all plugins interactively
tessl update

# Update all plugins without prompts
tessl update --yes

# Update a specific plugin
tessl update engteam/my-plugin

# Include breaking updates
tessl update --force
```

***

## Skill management

### tessl skill new

Create a new skill with an interactive wizard or using flags.

**Usage**

```sh
tessl skill new [--name NAME] [--description DESCRIPTION] [--workspace WORKSPACE] [--path PATH] [--install] [--public]
```

**Flags**

* `--name` (string, optional): Skill name
* `--description` (string, optional): When the skill should be triggered
* `--workspace` (string, optional): Workspace for the skill (default: local)
* `--path` (string, optional): Directory path where the skill will be created
* `--install` (boolean, optional): Automatically install in the current repository after creation
* `--public` (boolean, optional): Make the skill public (default: private)

**Notes**

* If flags are omitted, an interactive wizard will guide you through skill creation
* The skill will be created as a directory with the proper Agent Skills specification structure
* Use `--install` to immediately add the skill to your local agent configuration

**Examples**

```sh
# Create a skill interactively
tessl skill new

# Create a skill with all details specified
tessl skill new --name "commit-conventions" --description "Enforce commit message standards" --workspace myteam --install
```

### tessl skill import

Create `.tessl-plugin/plugin.json` from a local `SKILL.md` file.

**Usage**

```sh
tessl skill import [--workspace WORKSPACE] [--public] [--force] [<path>]
```

**Flags**

* `--workspace` (string, optional): Workspace for the skill (default: local)
* `--public` (boolean, optional): Make skill public (default: private)
* `--force` (boolean, optional): Overwrite existing `.tessl-plugin/plugin.json` without prompting

**Arguments**

* `path` (string, optional): Local path to directory containing SKILL.md or to SKILL.md file. Defaults to current directory if omitted.

**Examples**

```sh
# Import from current directory
tessl skill import

# Import from a specific path
tessl skill import ./my-skill

# Import and make public
tessl skill import --public --workspace myteam ./my-skill
```

### tessl skill lint

Validate skill structure and contents.

**Usage**

```sh
tessl skill lint [<source>]
```

**Arguments**

* `source` (string, optional): Path to skill folder or plugin.json. Defaults to current directory if omitted.

**Notes**

* Checks for required files and proper structure
* Validates frontmatter fields (name, description, etc.)
* Verifies conformance to the Agent Skills specification at [agentskills.io/specification](https://agentskills.io/specification)
* Returns validation errors and warnings

**Examples**

```sh
# Lint skill in current directory
tessl skill lint

# Lint skill at specific directory path
tessl skill lint ./my-skill

```

### tessl skill publish

Import (if needed) and publish skill to the registry. This command bundles skills, rules, documentation into a plugin.

**Usage**

```sh
tessl skill publish [--workspace WORKSPACE] [--public] [--force] [--bump patch|minor|major] [--dry-run] [--skip-evals] [--with-scenario-quality-check] [--eval-scenarios <path>] [--resolve-symlinks] [<path>]
```

**Flags**

* `--workspace` (string, optional): Workspace for the skill (used if importing)
* `--public` (boolean, optional): Make skill public (used if importing)
* `--force` (boolean, optional): Overwrite existing `.tessl-plugin/plugin.json` without prompting
* `--bump` (string, optional): Auto-bump version if it already exists in the registry. Supported values: `patch`, `minor`, `major`
* `--dry-run` (boolean, optional): Run all pre-publish checks without publishing
* `--skip-evals` (boolean, optional): Skip publishing eval scenarios from evals/ directory
* `--with-scenario-quality-check` (boolean, optional): Exclude misleading scenarios from the publish-time eval run
* `--eval-scenarios` (string, optional): Path to eval scenarios directory (default: `<path>/evals`)
* `--resolve-symlinks` (boolean, optional): Resolve symlinked files to their target contents when packing

**Arguments**

* `path` (string, optional): Path to the skill directory to publish. Defaults to current directory if omitted.

**Notes**

* Skills are automatically linted before publishing
* Published skills are versioned based on the version in the skill manifest
* Eval scenarios from the `evals/` directory are published by default; use `--skip-evals` to exclude them
* **Automatic evaluation**: When published, skills are automatically evaluated and review scores are calculated (see [Check a skill's quality using review](/improving-your-skills/reviewing-skills))

**Examples**

```sh
# Publish skill from current directory
tessl skill publish

# Publish to a specific workspace
tessl skill publish --workspace myteam ./my-skill

# Publish publicly
tessl skill publish --public
```

### tessl skill unpublish

Prints guidance explaining that skills cannot be unpublished on their own. Performs no registry action. `tessl skill archive` and `tessl skill delete` print the same guidance.

Skills are published inside a plugin. To remove a published skill, delete the skill and republish the plugin, unpublish the plugin, or archive the plugin. See [tessl plugin unpublish](#tessl-plugin-unpublish) and [tessl plugin archive](#tessl-plugin-archive).

***

## Tessl Review

The `tessl review` family runs an asynchronous review pipeline server-side. These commands require authentication; `run` and `fix` also require a workspace. The pipeline is bundle-aware, and `tessl review run` polls until it completes. This is the primary path for authenticated users and CI gating.

### tessl review run

Run an asynchronous **quality** review of a skill — it scores the skill against a reviewer rubric. `tessl review run` is shorthand for `tessl review run quality`; to run a security scan instead, use `tessl review run security` (documented below). Requires authentication and a workspace.

**Usage**

```sh
tessl review run [--json] [--workspace WORKSPACE] [--threshold <percent>] [--label <text>] [--force] [--review-plugin <ref>] [--no-wait] [<path>]
```

**Flags**

* `--json` (boolean, optional): Output as JSON
* `--workspace` / `-w` (string, optional): Workspace name or ID. Required when `--json` is set
* `--threshold` (number, optional): Exit non-zero if the review score is below this percentage. `0` never fails (skips validation and score gating)
* `--label` / `-l` (string, optional): Label to describe this review run
* `--force` / `-f` (boolean, optional): Run a fresh review even when cached results exist
* `--review-plugin` (string, optional): Reviewer plugin — a path to a local plugin directory, or a registry ref `workspace/plugin[@version]`. Requires a paid plan; on a Free organisation, passing this flag returns an upgrade error
* `--wait` / `--no-wait` (boolean, optional): Wait for the review to finish before returning (default: wait; use `--no-wait` to submit and return the run id immediately, then reconnect with `tessl review view`). Cannot be combined with `--threshold`, which needs the completed score

**Arguments**

* `path` (string, optional): Path to a skill file or skill directory. Defaults to current directory if omitted.

**Notes**

* Requires authentication; sign in with `tessl login` or set the `TESSL_TOKEN` API key in the environment
* `--workspace` is required when `--json` is set, since non-interactive runs cannot prompt for a workspace
* The command polls until the review completes, then prints the results
* `--threshold` causes a non-zero exit when the score falls below the percentage, making it a CI gate
* Press Ctrl+C to detach — the review continues server-side and can be viewed later with `tessl review view`
* `run` has two subcommands: `quality` (the default, documented here) and `security` (below). `tessl review run quality` is the explicit form of the default

**Examples**

```sh
# Review the skill in the current directory
tessl review run --workspace engteam

# Review a skill at a specific path
tessl review run ./my-skill --workspace engteam

# CI gate: fail if the score is below 80%
tessl review run ./my-skill --workspace engteam --json --threshold 80

# Review with a specific reviewer plugin
tessl review run ./my-skill --workspace engteam --review-plugin engteam/my-reviewer
```

### tessl review run security

Run an asynchronous security scan of a skill, powered by Snyk. Reports findings and the highest severity. Requires authentication and a workspace.

**Usage**

```sh
tessl review run security [--json] [--workspace WORKSPACE] [--label <text>] [--force] [--fail-on <severity>] [--no-wait] [<path>]
```

**Flags**

* `--json` (boolean, optional): Output as JSON
* `--workspace` / `-w` (string, optional): Workspace name or ID. Required when `--json` is set
* `--label` / `-l` (string, optional): Label to describe this security review run
* `--force` / `-f` (boolean, optional): Run a fresh scan even when cached results exist
* `--fail-on` (string, optional): Exit non-zero if the highest severity is at or above this level: `low`, `medium`, `high`, `critical`
* `--wait` / `--no-wait` (boolean, optional): Wait for the scan to finish before returning (default: wait; use `--no-wait` to submit and return the run id immediately, then reconnect with `tessl review view`). Cannot be combined with `--fail-on`, which needs the completed findings

**Arguments**

* `path` (string, optional): Path to a skill file or skill directory. Defaults to current directory if omitted.

**Notes**

* Requires authentication; sign in with `tessl login` or set the `TESSL_TOKEN` API key in the environment
* The scan runs server-side; the command polls until it completes, then prints the findings and the highest severity
* `--fail-on` gates the exit code on severity, making it a CI gate — the security equivalent of `--threshold` on a quality review

**Examples**

```sh
# Security-scan the skill in the current directory
tessl review run security --workspace engteam

# Scan a skill at a specific path
tessl review run security ./my-skill --workspace engteam

# CI gate: fail if any finding is high or critical
tessl review run security ./my-skill --workspace engteam --json --fail-on high
```

### tessl review view

View results for a review run of either kind. The run ID resolves whether it is a quality or a security review, and the results are rendered accordingly.

**Usage**

```sh
tessl review view [--json] [--last] [--threshold <percent>] [--fail-on <severity>] [--workspace <name>] [<id>]
```

**Flags**

* `--json` (boolean, optional): Output as JSON, in the same shape as `tessl review run --json`
* `--last` (boolean, optional): View results for the most recent review run
* `--threshold` (number, optional): Quality reviews only. Exit non-zero if the review score is below this percentage (0-100)
* `--fail-on` (string, optional): Security reviews only. Exit non-zero if the highest severity is at or above this level: `low`, `medium`, `high`, `critical`
* `--workspace` / `-w` (string, optional): Workspace name or ID. Only applies with `--last`; an explicit review run ID already identifies its workspace.

**Arguments**

* `id` (string, optional): Review run ID. Provide an ID or use `--last`.

**Notes**

* Provide a review run ID or use `--last`; one is required
* The gate flag follows the run's kind: `--threshold` gates a quality run, `--fail-on` gates a security run. Passing the flag for the other kind is a usage error
* `--workspace` is only accepted with `--last`; passing it alongside an explicit review run ID fails with a validation error

**Examples**

```sh
# View a specific review run
tessl review view abc123

# View the most recent review run
tessl review view --last

# Re-check a stored run against a threshold
tessl review view abc123 --json --threshold 80
```

### tessl review list

List recent review runs.

**Usage**

```sh
tessl review list [--json] [--limit <count>] [--workspace WORKSPACE]
```

**Flags**

* `--json` (boolean, optional): Output as JSON
* `--limit` (number, optional): Maximum number of review runs to display (default: 20)
* `--workspace` / `-w` (string, optional): Workspace name or ID

**Examples**

```sh
# List recent review runs
tessl review list

# List the 50 most recent runs
tessl review list --limit 50

# List runs for a specific workspace
tessl review list --workspace engteam
```

### tessl review retry

Re-run a failed review run.

**Usage**

```sh
tessl review retry [--json] [--workspace <name>] [--last] [<id>]
```

**Flags**

* `--json` (boolean, optional): Output as JSON
* `--workspace` / `-w` (string, optional): Workspace name or ID. Only applies with `--last`; an explicit review run ID already identifies its workspace.
* `--last` (boolean, optional): Retry the most recent review run

**Arguments**

* `id` (string, optional): Review run ID to retry. Provide an ID or use `--last`.

**Notes**

* `--workspace` is only accepted with `--last`; passing it alongside an explicit review run ID fails with a validation error

**Examples**

```sh
# Retry a specific review run
tessl review retry abc123

# Retry the most recent review run
tessl review retry --last

# Retry the most recent review run in a specific workspace
tessl review retry --last --workspace engteam
```

### tessl review fix

Improve a skill by running an automated review-and-fix loop server-side. Requires authentication and a workspace.

**Usage**

```sh
tessl review fix [--json] [--workspace WORKSPACE] [--threshold <percent>] [--max-iterations <count>] [--yes] [--review-plugin <ref>] [<path>]
```

**Flags**

* `--json` (boolean, optional): Output as JSON
* `--workspace` / `-w` (string, optional): Workspace name or ID
* `--threshold` (number, optional): Stop once a review scores at or above this percentage (0-100, default 90)
* `--max-iterations` (number, optional): Maximum improve→review iterations to attempt (1-10, default 3)
* `--yes` / `-y` (boolean, optional): Apply the improved skill without confirmation
* `--review-plugin` (string, optional): Reviewer plugin — a path to a local plugin directory, or a registry ref `workspace/plugin[@version]`. Requires a paid plan; on a Free organisation, passing this flag returns an upgrade error

**Arguments**

* `path` (string, optional): Path to a local skill file or skill directory. Defaults to current directory if omitted.

**Notes**

* Requires authentication; sign in with `tessl login` or set the `TESSL_TOKEN` API key in the environment
* `--workspace` is required when `--json` is set, since non-interactive runs cannot prompt for a workspace
* Runs an asynchronous improve→review loop, stopping early once `--threshold` is met

**Examples**

```sh
# Improve the skill in the current directory
tessl review fix --workspace engteam

# Improve until the score reaches 90%, up to 5 iterations
tessl review fix ./my-skill --workspace engteam --threshold 90 --max-iterations 5

# Apply improvements without a confirmation prompt
tessl review fix ./my-skill --workspace engteam --yes
```

***

## Plugin management

### tessl plugin new

Create a new plugin with an interactive wizard.

**Usage**

```sh
tessl plugin new [--name <workspace/plugin-name>] [--summary <description>] [--path <path>] [--rules <rule-name>] [--rule-description <description>] [--workspace <workspace>] [--skill] [--skill-name <name>] [--skill-description <description>] [--install] [--public]
```

**Flags**

* `--name` (string, optional): plugin name in format `workspace/plugin-name`
* `--summary` (string, optional): Brief description of the plugin
* `--path` (string, optional): Directory path where plugin will be created
* `--rules` (string, optional): Include rule by name
* `--rule-description` (string, optional): Initial rule instructions to include in generated rule file
* `--workspace` (string, optional): Workspace for the plugin
* `--skill` (boolean, optional): Include a skill in the plugin
* `--skill-name` (string, optional): Skill name (implies `--skill`)
* `--skill-description` (string, optional): Skill description (required with `--skill` or `--skill-name`)
* `--install` (boolean, optional): Automatically install in the current repository
* `--public` (boolean, optional): Make plugin public (default: private)

**Notes**

* Creates a new plugin directory with a basic `.tessl-plugin/plugin.json` template
* If run without flags, launches an interactive wizard to guide you through plugin creation
* For non-interactive use, include at least one component with `--skill`, `--skill-name`, or `--rules`
* Use `--skill` or `--skill-name` to create a plugin with a skill component

**Examples**

```sh
# Interactive wizard mode
tessl plugin new

# Create a plugin with specific options
tessl plugin new --name myworkspace/my-skill --summary "My skill description" --path ./my-skill-plugin

# Create a plugin with a skill
tessl plugin new --name myworkspace/my-skill --skill --skill-name "my-skill" --skill-description "Description of my skill"

# Create and automatically install
tessl plugin new --name myworkspace/my-skill --install
```

### tessl plugin lint

Validate plugin structure and contents

**Usage**

```sh
tessl plugin lint [<source>]
```

**Arguments**

* `source` (string, optional): Path to the plugin directory to lint. Defaults to current directory if omitted.

### tessl plugin migrate

Migrate `tile.json` to `.tessl-plugin/plugin.json`.

**Usage**

```sh
tessl plugin migrate [--force] [<path>]
```

**Flags**

* `--force` (boolean, optional): Overwrite existing `.tessl-plugin/plugin.json`

**Arguments**

* `path` (string, optional): Path to plugin directory. Defaults to current directory if omitted.

### tessl plugin pack

Package a plugin into a .tgz file.

**Usage**

```sh
tessl plugin pack [--output path] [<source>]
```

**Flags**

* `--output` (string, optional): Output path for .tgz file. If omitted, uses the plugin name and version to create a file in your current directory.

**Arguments**

* `source` (string, optional): Path to plugin folder. Defaults to current directory if omitted.

### tessl plugin info

Show plugin details from the registry.

**Usage**

```sh
tessl plugin info [<name-or-path>]
```

**Arguments**

* `name-or-path` (string, optional): plugin name (`workspace/plugin[@version]`) or path to plugin directory. Defaults to current directory if omitted.

**Examples**

```sh
# Show info for plugin in current directory
tessl plugin info

# Show info for a specific plugin
tessl plugin info engteam/my-plugin

# Show info for a specific version
tessl plugin info engteam/my-plugin@1.0.0
```

### tessl plugin publish

Publish plugins to the Tessl registry. This command bundles skills, rules, documentation into a plugin.

**Usage**

```sh
tessl plugin publish [--dry-run] [--bump patch|minor|major] [--skip-evals] [--with-scenario-quality-check] [--workspace <workspace>] [--version <version>] [--verbose] [<path>]
```

**Flags**

* `--dry-run` (boolean, optional): Run all pre-publish checks without publishing
* `--bump` (string, optional): Auto-bump version if it already exists in the registry. Supported values: `patch`, `minor`, `major`
* `--skip-evals` (boolean, optional): Skip publishing eval scenarios from evals/ directory
* `--with-scenario-quality-check` (boolean, optional): Have the backend's LLM judge filter out misleading scenarios (low feasibility, rubric leakage, low value) from the publish-time eval run. Equivalent to `tessl eval run --quality-check` for the eval that runs as part of publishing.
* `--workspace` (string, optional): Workspace to publish under when the plugin manifest name is flat (no `workspace/` prefix)
* `--version` (string, optional): Version to publish when the plugin manifest omits one
* `--verbose` / `-v` (boolean, optional): Print every archived file with the reason it was included

**Arguments**

* `path` (string, optional): Path to the plugin directory to publish. Defaults to current directory if omitted.

**Examples**

```sh
# Publish a plugin from current directory
tessl plugin publish

# Publish a plugin from a specific directory
tessl plugin publish path/to/my/plugin
```

{% hint style="info" %}
Not sure what your published plugin's URL will look like? See [Workspaces](/reference/workspaces#how-workspaces-appear-in-the-registry) to learn how workspace and plugin names map to Registry URLs.
{% endhint %}

### tessl plugin unpublish

Unpublish plugins from the registry (within 2 days of publishing).

**Usage**

```sh
tessl plugin unpublish [--plugin <workspace/plugin@version>]
```

**Flags**

* `--plugin` (string, optional): plugin to unpublish in the format `workspace/plugin@version`. If omitted, you'll be prompted.

**Notes**

* Only available within 2 days of publishing

**Examples**

```sh
# Unpublish a specific plugin version
tessl plugin unpublish --plugin myworkspace/myplugin@1.0.0

# Interactive mode
tessl plugin unpublish
```

### tessl plugin archive

Archive plugins in the registry.

**Usage**

```sh
tessl plugin archive [--plugin <workspace/plugin[@version]>] --reason <reason>
```

**Flags**

* `--plugin` (string, optional): plugin to archive in the format `workspace/plugin` (archives all versions) or `workspace/plugin@version` (archives a single version)
* `--reason` (string, required): Reason for archiving the plugin

**Notes**

* Prevents new installations while preserving existing installations

**Examples**

```sh
# Archive a specific plugin version
tessl plugin archive --plugin engteam/myplugin@1.0.0 --reason "Deprecated in favor of v2"

# Archive all versions of a plugin
tessl plugin archive --plugin engteam/myplugin --reason "No longer maintained"
```

***

## Organization management

### tessl org list

List all organizations that you are a member of.

**Usage**

```sh
tessl org list
```

### tessl org usage

Show your Tessl credit usage.

**Usage**

```sh
tessl org usage [--org <nameOrId>] [--json]
```

**Flags**

* `--org` (string, optional): Organization name or ID. You are prompted when you belong to several.
* `--json` (boolean, optional): Output as JSON. The payload contains `plan` and `credits` fields.

**Examples**

```sh
# Show credit usage
tessl org usage

# Show usage for a specific organization
tessl org usage --org my-org

# Output as JSON
tessl org usage --json
```

***

## Workspace management

Workspaces enable you to create private collections of plugins that are restricted to yourself or your organization. You can control member access and permissions. To learn more, see [Distributing via registry](/distribute/distributing-via-registry).

### tessl workspace create

Create a new workspace.

**Usage**

```sh
tessl workspace create [--org <nameOrId>] [<name>]
```

**Flags**

* `--org` (string, optional): Organization name or ID to own the workspace. When omitted and you belong to several organizations, interactive terminals prompt you to pick one; noninteractive callers must pass `--org <nameOrId>`.

**Arguments**

* `name` (string, optional): Name for the workspace. The name must be lowercase. If omitted, you'll be prompted.

**Examples**

```sh
# Create a workspace
tessl workspace create myworkspace

# Interactive creation
tessl workspace create

# Create a workspace in a specific organization
tessl workspace create --org 01935e2c-1234-5678-abcd-ef0123456789 engteam
```

### tessl workspace list

List all workspaces that you are a member of.

**Usage**

```sh
tessl workspace list [--has-publish-access]
```

**Flags**

* `--has-publish-access` (boolean, optional): Only show workspaces where you have permission to publish.

**Examples**

```sh
# List all workspaces you belong to
tessl workspace list

# List only workspaces you can publish to
tessl workspace list --has-publish-access
```

### tessl workspace delete

Delete an existing workspace.

**Usage**

```sh
tessl workspace delete [<name>]
```

**Arguments**

* `name` (string, optional): The name or ID of the workspace. If omitted, you'll be prompted.

**Examples**

```sh
# Delete a workspace
tessl workspace delete myworkspace

# Interactive deletion
tessl workspace delete
```

If a workspace has published a public plugin, deleting it permanently reserves its name: the name cannot be used again, in this organization or any other. To move a workspace between organizations, do not delete it. Email <support@tessl.io> and we can move it for you after a review.

### tessl workspace add-member

Add a user to a workspace with specified permissions.

**Usage**

```sh
tessl workspace add-member [--workspace <name>] [--username <username>] [--role <role>]
```

**Flags**

* `--workspace` (string, optional): Name or ID of workspace
* `--username` (string, optional): Username or ID of user to add
* `--role` (string, optional): Role to assign. Supported values: `member`, `publisher`, `manager`, `owner`

See [Roles](/administrators/roles) for more information

**Examples**

```sh
# Add a user to the workspace with a member role
tessl workspace add-member --workspace myworkspace --username johnsmith --role member

# Add a user to the workspace with an owner role
tessl workspace add-member --workspace myworkspace --username janesmith --role owner

# Interactive mode (prompts for all fields)
tessl workspace add-member
```

### tessl workspace remove-member

Remove a user from a workspace.

**Usage**

```sh
tessl workspace remove-member [--workspace <name>] [--username <username>]
```

**Flags**

* `--workspace` (string, optional): Name or ID of workspace
* `--username` (string, optional): Username or ID of user to remove

**Examples**

```sh
# Remove a member
tessl workspace remove-member --workspace myworkspace --username johnsmith

# Interactive mode
tessl workspace remove-member
```

### tessl workspace list-members

List all members of a workspace.

**Usage**

```sh
tessl workspace list-members [<name>]
```

**Arguments**

* `name` (string, optional): The name or ID of the workspace. If omitted, you'll be prompted.

**Examples**

```sh
# List members of a workspace
tessl workspace list-members myworkspace

# Interactive mode
tessl workspace list-members
```

### tessl workspace archive

Archive a workspace. An archived workspace won't be available to publish to or read from, but can be unarchived later.

**Usage**

```sh
tessl workspace archive --reason <text> [<name>]
```

**Flags**

* `--reason` (string, required): Reason for archiving the workspace

**Arguments**

* `name` (string, optional): The name or ID of the workspace. If omitted, you'll be prompted.

**Notes**

* Archiving is a safer alternative to deleting, as archived workspaces can be restored
* Use `tessl workspace unarchive` to restore an archived workspace

**Examples**

```sh
# Archive a workspace with a reason
tessl workspace archive --reason "deprecated" myworkspace

# Interactive mode
tessl workspace archive --reason "no longer in use"
```

### tessl workspace unarchive

Unarchive an archived workspace, making it active again.

**Usage**

```sh
tessl workspace unarchive <name>
```

**Arguments**

* `name` (string, required): The name or ID of the workspace.

**Examples**

```sh
# Unarchive a workspace
tessl workspace unarchive myworkspace
```

***

## Configuration management

### tessl config get

Get a configuration value or show all configuration.

**Usage**

```sh
tessl config get [<key>]
```

**Arguments**

* `key` (string, optional): Configuration key to get. If omitted, shows all configuration.

**Examples**

```sh
# Show all configuration
tessl config get

# Get a specific configuration value
tessl config get shareUsageData
```

### tessl config set

Set a configuration value.

**Usage**

```sh
tessl config set <key> <value>
```

**Arguments**

* `key` (string, required): Configuration key to set. Supported values: `shareUsageData`, `termsAcceptedAt`, `evalDefaultModelNoticeShownAt`, `evalDefaultModelNoticeModel`, `agents`
* `value` (string, required): Configuration value to set

**Examples**

```sh
# Set a configuration value
tessl config set shareUsageData false
```

### tessl config list

List all configuration values.

**Usage**

```sh
tessl config list [<key>]
```

**Arguments**

* `key` (string, optional): Configuration key to list. If omitted, lists all configuration.

**Notes**

* Similar to `tessl config get` but specifically for listing configuration values

**Examples**

```sh
# List all configuration
tessl config list

# List a specific configuration key
tessl config list shareUsageData
```

### tessl config add

Add one or more values to a configuration array.

**Usage**

```sh
tessl config add <key> <value>...
```

**Arguments**

* `key` (string, required): Configuration key for the array
* `value` (string, required, repeatable): One or more values to add to the array

**Notes**

* Use this command to add values to array-type configuration settings
* Can specify multiple values in a single command

**Examples**

```sh
# Add a single value to an array configuration
tessl config add agents cursor

# Add multiple values to an array configuration
tessl config add agents cursor claude-code
```

### tessl config remove

Remove a value from a configuration array.

**Usage**

```sh
tessl config remove <key> <value>
```

**Arguments**

* `key` (string, required): Configuration key for the array
* `value` (string, required): Value to remove from the array

**Notes**

* Use this command to remove values from array-type configuration settings
* Only removes the specified value, not the entire configuration key

**Examples**

```sh
# Remove a value from an array configuration
tessl config remove agents cursor
```

***

## Diagnostics and utilities

### tessl doctor

Run authentication and manifest diagnostics to troubleshoot issues.

**Usage**

```sh
tessl doctor [--json]
```

**Flags**

* `--json` (boolean, optional): Output diagnostics as JSON for programmatic processing

**Examples**

```sh
# Run diagnostics with human-readable output
tessl doctor

# Output diagnostics as JSON
tessl doctor --json
```

### tessl feedback

Send feedback to the Tessl team.

**Usage**

```sh
tessl feedback [<message>]
```

**Arguments**

* `message` (string, optional): Feedback message to send. If omitted, you'll be prompted interactively.

**Examples**

```sh
# Send feedback directly
tessl feedback "Great tool! Would love to see Python support."

# Interactive feedback
tessl feedback
```

***

## CLI self-management

### tessl cli update

Update the CLI to the latest or specified version.

**Usage**

```sh
tessl cli update [--target <version>] [--channel <name>] [--dry-run]
```

**Flags**

* `--target` / `-t` (string, optional): Target version to update to (example: v1.0.0)
* `--channel` / `-c` (string, optional): Release channel to switch to. Supported values: `latest`, `beta`
* `--dry-run` (boolean, optional): Show what would be run without executing the update

**Examples**

```sh
# Update to the latest version
tessl cli update

# Update to a specific version
tessl cli update --target v1.0.0

# Switch to the beta release channel
tessl cli update --channel beta

# Preview update without executing
tessl cli update --dry-run
```

***

## API key management

### tessl api-key create

Create an API key for a workspace.

**Usage**

```sh
tessl api-key create [--workspace <name>] [--name <name>] [--role <role>] [--expiry-date <date> | --ttl <duration>]
```

**Flags**

* `--workspace` (string, optional): Name or ID of workspace
* `--name` (string, optional): Name for the API key
* `--role` (string, optional): Role to assign. Supported values: `member`, `publisher`, `manager`, `owner`
* `--expiry-date` (string, optional): Expiry date in ISO 8601 format (example: 2027-12-31T00:00:00Z). Defaults to 30 days from creation
* `--ttl` (string, optional): Time until expiry as a positive whole number followed by `h` (hours), `d` (days), `w` (weeks), `mo` (30-day months), or `y` (365-day years), such as `3h`, `4w`, or `1y`. Minutes are not supported. Calculated using the local clock after interactive prompts, just before the creation request. Use either `--ttl` or `--expiry-date`. Without either flag, keys expire 30 days from creation

**Examples**

```sh
# Create an API key interactively
tessl api-key create

# Create an API key with all details specified
tessl api-key create --workspace engteam --name "ci-key" --role publisher --expiry-date 2027-12-31T00:00:00Z

# Create an API key that expires in four weeks
tessl api-key create --workspace engteam --name "ci-key" --role publisher --ttl 4w
```

### tessl api-key list

List API keys for a workspace.

**Usage**

```sh
tessl api-key list [--workspace <name>]
```

**Flags**

* `--workspace` (string, optional): Name or ID of workspace

**Examples**

```sh
# List API keys interactively
tessl api-key list

# List API keys for a specific workspace
tessl api-key list --workspace engteam
```

### tessl api-key delete

Delete an API key from a workspace.

**Usage**

```sh
tessl api-key delete [--workspace <name>] [--yes] [<api-key-id>]
```

**Flags**

* `--workspace` (string, optional): Name or ID of workspace
* `--yes` / `-y` (boolean, optional): Skip confirmation prompt and delete immediately

**Arguments**

* `api-key-id` (string, optional): ID of the API key to delete. If omitted, you'll be prompted.

**Examples**

```sh
# Delete an API key interactively
tessl api-key delete

# Delete a specific API key
tessl api-key delete --workspace engteam <api-key-id>
```

***

## Environment management

An environment is a named, workspace-scoped set of variables that a scheduled run, a launch, or a cloud run executes inside. Its values are sealed and write-only: you can set and replace them, but never read them back. Use an environment to supply the GitHub token, API keys, and any other credentials a run needs.

For what belongs in an environment and how runs consume it, see [Environments](/automations/environments).

{% hint style="info" %}
`tessl env` is a short alias for `tessl environment`. A scheduled run cannot mint its own GitHub token, so include a `GITHUB_TOKEN` (or `GH_TOKEN`) in the environment for any schedule that checks out a repository or opens pull requests.
{% endhint %}

### tessl environment create

Create an environment for a workspace.

**Usage**

```sh
tessl environment create [--workspace <name>] [--env-file <path>] [--mcp-server <name>] [--github-access <none|read|write>] [--linear-access <none|read|write>] <name>
```

**Arguments**

* `name` (string, required): Name for the environment.

**Flags**

* `--workspace` (string, optional): Name or ID of workspace.
* `--env-file` (string, optional): Path to a `.env` file with environment variables.
* `--mcp-server` (string, optional, repeatable): Name of a workspace MCP server to configure.
* `--github-access` (`none`, `read`, or `write`, optional): GitHub access granted to runs that use this environment. The default is `read`.
* `--linear-access` (`none`, `read`, or `write`, optional): Linear access granted to cloud launch runs that use this environment. The default is `none`.

`none` mints no GitHub credential, so the run must bring its own token. `read` can clone the repository but cannot push. `write` can push branches and open pull requests; a person remains responsible for reviewing the changes and deciding whether to merge them.

For Linear access, `none` injects no platform credential. `read` permits Linear queries, while `write` also permits mutations. An environment cannot grant `read` or `write` while defining a non-empty `LINEAR_TOKEN` or `LINEAR_API_URL`. Tools must use `LINEAR_TOKEN` for authorization and `LINEAR_API_URL` as the API base URL; a tool that ignores `LINEAR_API_URL` sends the run key to Linear, which rejects it.

**Examples**

```sh
# Create an environment and load its values from a .env file
tessl environment create production --env-file production.env

# Create an environment in a specific workspace
tessl environment create production --workspace engteam --env-file production.env

# Wire workspace MCP servers into the environment
tessl environment create production --env-file production.env --mcp-server linear --mcp-server slack

# Create an environment whose runs can push branches and open pull requests
tessl environment create production --env-file production.env --github-access write

# Create an environment whose cloud launch runs can read and write Linear
tessl environment create production --linear-access write
```

### tessl environment list

List environments for a workspace.

**Usage**

```sh
tessl environment list [--workspace <name>] [--json]
```

**Flags**

* `--workspace` (string, optional): Name or ID of workspace.
* `--json` (boolean, optional): Output as JSON.

**Examples**

```sh
# List environments in the resolved workspace
tessl environment list

# List environments in a specific workspace
tessl environment list --workspace engteam

# Output as JSON
tessl environment list --json
```

### tessl environment view

View metadata for an environment.

**Usage**

```sh
tessl environment view [--workspace <name>] [--json] <name>
```

**Arguments**

* `name` (string, required): Name or ID of the environment to view.

**Flags**

* `--workspace` (string, optional): Name or ID of workspace.
* `--json` (boolean, optional): Output as JSON.

**Notes**

* This shows metadata only, such as the variable names, GitHub access, Linear access, and MCP servers wired in. Sealed values are write-only and are never returned.

**Examples**

```sh
# View an environment
tessl environment view production

# View an environment as JSON
tessl environment view production --json
```

### tessl environment update

Update an environment for a workspace.

**Usage**

```sh
tessl environment update [--workspace <name>] [--env-file <path>] [--mcp-server <name>] [--github-access <none|read|write>] [--linear-access <none|read|write>] [--clear-mcp-servers] <name>
```

**Arguments**

* `name` (string, required): Name or ID of the environment to update.

**Flags**

* `--workspace` (string, optional): Name or ID of workspace.
* `--env-file` (string, optional): Path to a `.env` file with environment variables.
* `--mcp-server` (string, optional, repeatable): Name of a workspace MCP server to configure.
* `--github-access` (`none`, `read`, or `write`, optional): GitHub access granted to runs that use this environment.
* `--linear-access` (`none`, `read`, or `write`, optional): Linear access granted to cloud launch runs that use this environment.
* `--clear-mcp-servers` (boolean, optional): Remove all MCP servers wired into this environment.

`none` mints no GitHub credential, so the run must bring its own token. `read` can clone the repository but cannot push. `write` can push branches and open pull requests.

For Linear access, `none` injects no platform credential. `read` permits Linear queries, while `write` also permits mutations. An environment cannot grant `read` or `write` while defining a non-empty `LINEAR_TOKEN` or `LINEAR_API_URL`. Tools must use both injected variables: `LINEAR_TOKEN` for authorization and `LINEAR_API_URL` as the API base URL.

**Notes**

* `--env-file` is required unless at least one `--mcp-server`, `--github-access`, or `--linear-access` is provided. A metadata-only update with no `--env-file` changes just the metadata and leaves every stored value untouched. Passing `--env-file` replaces the sealed values instead: the env file becomes the complete set, so it must include every credential you want to keep.
* `--clear-mcp-servers` always requires `--env-file`, including when it is combined with `--github-access`, `--linear-access`, or `--mcp-server`, because clearing is treated as an update with no new MCP servers.

**Examples**

```sh
# Replace an environment's values from a .env file
tessl environment update production --env-file production.env

# Rewire which MCP servers the environment configures (stored values are kept)
tessl environment update production --mcp-server linear

# Allow runs to push branches and open pull requests (stored values are kept)
tessl environment update production --github-access write

# Allow cloud launch runs to read Linear (stored values are kept)
tessl environment update production --linear-access read

# Remove every MCP server from the environment (credentials are replaced by the env file — include every value to keep)
tessl environment update production --clear-mcp-servers --env-file production.env
```

### tessl environment delete

Delete an environment from a workspace.

**Usage**

```sh
tessl environment delete [--workspace <name>] [name]
```

**Arguments**

* `name` (string, optional): Name or ID of the environment to delete. You are prompted when it is omitted.

**Flags**

* `--workspace` (string, optional): Name or ID of workspace.

**Examples**

```sh
# Delete an environment
tessl environment delete staging

# Delete an environment from a specific workspace
tessl environment delete staging --workspace engteam
```

***

## Model Context Protocol (MCP)

### tessl mcp start

Start the Tessl MCP (Model Context Protocol) server for integration with AI coding agents.

**Usage**

```sh
tessl mcp start
```

### tessl mcp proxy

Bridge a coding agent to a workspace MCP server through the Tessl MCP gateway. Starts a stdio MCP proxy that forwards JSON-RPC messages to a workspace-registered upstream MCP server, authenticating to the gateway with your Tessl credentials (`tessl login`) or `TESSL_TOKEN`. See [MCP gateway](/reference/mcp-gateway) for how to register the upstream server and wire the proxy into an agent or plugin `.mcp.json`.

**Usage**

```sh
tessl mcp proxy <workspace>/<mcp>
```

**Arguments**

* `workspace/mcp` (string, required): Slug of the workspace and the MCP server registered in that workspace's gateway (for example `engteam/linear`).

**Authentication**

* **Interactive use** — sign in once with `tessl login`; the proxy reuses the same credentials.
* **CI or other non-interactive contexts** — set `TESSL_TOKEN` to a Tessl API key for the workspace. See [`tessl api-key create`](#tessl-api-key-create).

**Examples**

```sh
# Proxy to an MCP server named `linear` in the `engteam` workspace
tessl mcp proxy engteam/linear
```

***

## Repository

### tessl repo select-commits

Browse recent commits in a repository and select which ones to evaluate. Used as the first step in [generating codebase scenarios](/improving-your-skills/evaluate-skill-quality-using-scenarios#generate-scenarios-from-your-codebase).

{% hint style="info" %}
This command is not listed in `tessl --help` but works when invoked directly.
{% endhint %}

**Usage**

```sh
tessl repo select-commits <org/repo> [flags]
```

**Arguments**

* `org/repo` (string, required): Repository in `org/repo` format

**Flags**

* `--keyword` (string, optional): Filter by commit message keyword
* `--author` (string, optional): Filter by author name
* `--since` / `--until` (string, optional): Date range in YYYY-MM-DD format
* `--count` / `-n` (number, optional): Number of commits to show (1–100)
* `--workspace` / `-w` (string, optional): Required outside interactive mode
* `--json` (boolean, optional): Output as JSON

**Prerequisite:** your GitHub or GitLab account must be connected in workspace settings. If it isn't, the error message includes a direct link to the settings page.

**Examples**

```sh
# Browse recent commits
$ tessl repo select-commits org/repo

# Filter by keyword and date range
$ tessl repo select-commits org/repo --keyword=feat --since=2026-01-01

# Show last 50 commits by a specific author
$ tessl repo select-commits org/repo --author="Alice" --count=50
```

***

## GitHub integration

Tessl Helper is a GitHub App your workspace installs. These commands mint short-lived installation tokens so CI steps and coding agents can act as the Tessl Helper bot without holding the App's private key.

A workspace can reach the Tessl Helper App on more than one GitHub account. Pass `--repo owner/name` to say which: the owner selects the installation to mint from, and the repository names limit what the token can reach. Without it, the most recently updated installation is used, which may not be the one that owns the repositories you want.

### tessl github token

Mint a short-lived GitHub installation token for the Tessl Helper bot and print it to stdout.

**Usage**

```sh
$ tessl github token [--workspace <name>] [--repo <owner/name>]...
```

**Flags**

* `--workspace` (string, optional): Name or ID of workspace
* `--repo` (string, optional, repeatable): Scope the token to a repository and select its installation by owner, in `owner/name` form. All values must share one owner, since a token is minted against a single installation.

**Notes**

* The token is written to stdout so it can be captured directly, for example `GH_TOKEN=$(tessl github token ...)`. The expiry is written to stderr, so it does not pollute that capture.
* The minted token is a GitHub App installation token, not a user token. Installation-scoped endpoints such as `/installation/repositories` work; user-scoped endpoints such as `gh org list` fail.
* Without `--repo`, the token covers everything the selected installation can reach.

**Examples**

```sh
# Mint a token scoped to one repository
$ tessl github token --workspace engteam --repo my-org/my-repo

# Scope to several repositories owned by the same account
$ tessl github token --workspace engteam --repo my-org/api --repo my-org/web

# Capture the token for a single command
$ GH_TOKEN=$(tessl github token --workspace engteam --repo my-org/my-repo) gh api /installation/repositories
```

### tessl github setup-auth

Configure `gh` and `git` on this machine to act as the Tessl Helper bot, so subsequent commands authenticate without a token in the environment.

**Usage**

```sh
$ tessl github setup-auth [--workspace <name>] [--repo <owner/name>]... [--force] [--auto-refresh] [--refresh-interval-seconds <seconds>]
```

**Flags**

* `--workspace` (string, optional): Name or ID of workspace
* `--repo` (string, optional, repeatable): Scope the token to a repository and select its installation by owner, in `owner/name` form. All values must share one owner.
* `--force` (boolean, optional): Overwrite an existing `gh`/`git` auth configuration. Without it, the command stops rather than replacing one already in place.
* `--auto-refresh` (boolean, optional): Spawn a background process that re-mints the token periodically, for steps that outlive a single token.
* `--refresh-interval-seconds` (number, optional): How often that background process re-mints, in seconds. Defaults to 2700, which is 45 minutes.

**Notes**

* Signs in `gh` and sets a global git identity for the bot. This persists after the command exits, so prefer an ephemeral runner or container.
* Refuses to run when `gh` is already signed in or a global git identity is already set. Use `--force` to replace them.
* Clear `GH_TOKEN` and `GITHUB_TOKEN` first. `gh` prefers a token in the environment over the credential this writes.
* Without `--auto-refresh` the credential lasts until the expiry the command prints.
* With `--repo`, refreshes re-mint against the same owner and repositories. Without it, each refresh picks the most recently updated installation.

**Examples**

```sh
# Authenticate for one repository
$ tessl github setup-auth --workspace engteam --repo my-org/my-repo

# Long-running CI step, refreshing in the background
$ tessl github setup-auth --workspace engteam --repo my-org/my-repo --auto-refresh

# Replace an existing gh/git configuration
$ tessl github setup-auth --workspace engteam --repo my-org/my-repo --force
```

***

## Inventory

### tessl inventory import

Scan a GitHub account or org for installed skills and upload an inventory. Forked repos are skipped.

**Usage**

```sh
tessl inventory import [--org <account>] [--workspace <name>] [--repo <repo>]... [--ignore-repo <repo>]... [--dry-run <path>]
```

**Flags**

* `--org` (string, optional): GitHub account or org to scan. Prompted if omitted.
* `--workspace` (string, optional): Name or ID of workspace to upload to. Prompted if omitted.
* `--repo` (string, repeatable, optional): Limit scan to this repo (repeatable)
* `--ignore-repo` (string, repeatable, optional): Exclude this repo from the scan (repeatable)
* `--dry-run` (string, optional): Write inventory JSON to the given path instead of uploading

**Notes**

* `--org` and `--workspace` are prompted when omitted in interactive sessions, but required as flags in non-interactive environments (including with `--dry-run`)

**Examples**

```sh
# Scan an org and upload to a workspace
tessl inventory import --org my-github-org --workspace engteam

# Limit the scan to specific repositories
tessl inventory import --org my-github-org --workspace engteam --repo repo-a --repo repo-b

# Preview the inventory without uploading
tessl inventory import --org my-github-org --workspace engteam --dry-run ./inventory.json
```

***

## Scenario management

{% hint style="warning" %}
Scenario management and evals are in public beta. Workflows and output formats may change in upcoming releases.
{% endhint %}

Scenario commands generate and manage eval scenarios from real repository commits. For a full walkthrough, see [Prove a skill works using evaluation](/improving-your-skills/evaluate-skill-quality-using-scenarios).

### tessl scenario generate

Generate eval scenarios from a plugin or repository commits. Runs server-side — the CLI polls until complete. Ctrl-C detaches without cancelling; check progress with `tessl scenario list`.

**Alias:** `tessl scenarios generate`

**Usage**

```sh
tessl scenario generate [--commits <hash1,hash2>] [--prs <id1,id2>] [--count <n>] [--workspace <name>] [--context <globs>] [--json] [--no-wait] <source>
```

**Arguments**

* `source` (string, required): plugin path or `org/repo` to generate scenarios from

**Flags**

* `--commits` (string, optional): Comma-separated commit hashes to generate scenarios from (repo only)
* `--prs` (string, optional): Comma-separated PR numbers to resolve as commit ranges (repo only)
* `--count` / `-n` (number, optional): Number of new scenarios to add (plugin only)
* `--context` (string, optional): Glob patterns for context files to exclude from baseline, comma-separated (repo only). These patterns are stored in each generated `scenario.json` as `fixture.exclude` — they are stripped for the baseline eval variant and injected back for the with-context variant. Defaults to `*.mdc`, `*.md`, `tile.json`, `.tessl-plugin/plugin.json`, `tessl.json`, `.tessl/` when omitted.
* `--workspace` / `-w` (string, optional): Workspace name or ID (repo only). Required outside interactive mode.
* `--json` (boolean, optional): Output as JSON
* `--wait` / `--no-wait` (boolean, optional): Wait for the generation to finish before returning (default: wait when interactive; a non-interactive run, such as CI, submits and returns without waiting; use `--no-wait` to force that anywhere, then reconnect with `tessl scenario view`). `--json` on its own also detaches; pass `--wait --json` to poll to completion and still get JSON.

**Examples**

```sh
# Generate scenarios for two commits
$ tessl scenario generate org/repo --commits=abc123,def456

# Generate scenarios from PRs
$ tessl scenario generate org/repo --prs=42,43

# Generate scenarios from a plugin
$ tessl scenario generate ./my-plugin --count=5

# Specify context file patterns
$ tessl scenario generate org/repo --commits=abc123 --context="*.mdc,*.md"

# Non-interactive with workspace
$ tessl scenario generate org/repo --commits=abc123 --workspace=engteam
```

### tessl scenario list

List recent scenario generation runs in reverse chronological order.

**Alias:** `tessl scenarios list`

**Usage**

```sh
tessl scenario list [--mine] [--workspace <name>] [--limit <count>] [--status <status>] [--json]
```

**Flags**

* `--mine` (boolean, optional): Only show generation runs created by you
* `--workspace` / `-w` (string, optional): Filter by workspace name
* `--limit` (number, optional): Maximum number of runs to display (default: 20)
* `--status` (string, optional): Filter by status. Supported values: `pending`, `in_progress`, `completed`, `failed`
* `--json` (boolean, optional): Output as JSON

### tessl scenario view

Inspect a scenario generation run. Shows run metadata and a table of generated scenarios with titles and checklist item counts.

**Alias:** `tessl scenarios view`

**Usage**

```sh
tessl scenario view [--last] [--json] [<id>]
```

**Flags**

* `--last` (boolean, optional): View the most recent generation run
* `--json` (boolean, optional): Output as JSON

**Arguments**

* `id` (string, optional): Generation run ID. Either `id` or `--last` is required.

**Examples**

```sh
# View the most recent generation run
$ tessl scenario view --last

# View a specific run
$ tessl scenario view 019c4791-9eec-7458-b28a-6c94405a3d38
```

### tessl scenario download

Download generated scenarios to your local `evals/` folder.

**Alias:** `tessl scenarios download`

**Usage**

```sh
tessl scenario download [--output <dir>] [--strategy <strategy>] [--json] [--last] <id>...
```

**Flags**

* `--output` / `-o` (string, optional): Output directory (default: `evals`)
* `--strategy` / `-s` (string, optional): Write strategy: `merge` (default) adds new scenarios alongside existing ones; `replace` clears the directory first
* `--json` (boolean, optional): Output as JSON
* `--last` (boolean, optional): Download from the most recent generation run

**Arguments**

* `id` (string, required, repeatable): Generation IDs to download. Can specify multiple IDs. Either `id` or `--last` is required.

If the generation run is still in progress or pending, the command reports the status and exits.

**Downloaded structure:**

```
evals/
  <7-char-hash>-<slug>/
    task.md          ← task brief shown to the agent
    criteria.json    ← weighted checklist rubric
    scenario.json    ← fixture with repo URL, commit ref, and context exclude patterns
```

**Examples**

```sh
# Download from the most recent generation run
$ tessl scenario download --last

# Download to a custom directory, replacing existing scenarios
$ tessl scenario download --last --output=my-evals --strategy=replace

# Download a specific run
$ tessl scenario download 019c4791-9eec-7458-b28a-6c94405a3d38
```

***

## Evaluations

For more information on workflows and usage, see [Prove a skill works using evaluation](/improving-your-skills/evaluate-skill-quality-using-scenarios).

### tessl eval run

Run evals from a scenarios directory. The context under test, if any, comes from `--context`; with no context, the run is the baseline variant alone (see Notes).

* **Codebase eval**: source directory contains `scenario.json` fixtures → runs with smart defaults (agent `claude`, model `deepseek-v4.1-flash`, context pattern from `fixture.exclude`, context ref `infer`)
* **Plugin eval** — source contains `.tessl-plugin/plugin.json` → runs the plugin's own evals. Pass the plugin root directory (the directory that contains `.tessl-plugin/`, not `.tessl-plugin/` itself).

Pointing at a plugin directory is shorthand: `tessl eval run ./my-plugin` expands to `tessl eval run ./my-plugin/evals --context ./my-plugin` — the plugin's `evals/` as the scenarios, the plugin itself as the context. An explicit `--context` opts out of the shorthand.

**Usage**

```sh
tessl eval run [--agent <agent>] [--model <model>] [--scorer-agent <agent>] [--scorer-model <model>] [--context <source>] [--context-commit <ref>] [--skill <name>]... [--skip-baseline] [--variant-json <json-or-path>] [--allow-unsafe-fixture-paths] [--runs <count>] [--label <text>] [--env-file <path>] [--skip-forced-context-activation] [--skip-scoring] [--force] [--quality-check] [--list-agents] [--yes] [--json] [--no-wait] [<source>]
```

**Flags**

* `--agent` (string, optional): Agent to run with (e.g. `claude`). May be passed alone or together with `--model`. Run `tessl eval run --list-agents` to see supported values and combinations. Default pair when neither override is supplied: `claude` / `deepseek-v4.1-flash`
* `--model` (string, optional): Model to run with (e.g. `claude-sonnet-4-5`). May be passed alone or together with `--agent`. Run `tessl eval run --list-agents` to see supported values and combinations. Default pair when neither override is supplied: `claude` / `deepseek-v4.1-flash`
* `--scorer-agent` (string, optional): Override the agent used for the scoring step; the solver agent is unchanged. May be passed alone or together with `--scorer-model`.
* `--scorer-model` (string, optional): Override the model used for the scoring step; the solver model is unchanged. May be passed alone or together with `--scorer-agent`.
* `--context` (string, optional): Context to test — a local path (a plugin directory, or a file/glob), or a published `workspace/plugin@version`. Adds the with-context variant alongside the baseline. See the examples below.
* `--context-commit` (string, optional): Commit, branch, or tag to source a local `--context` from instead of the working tree. Requires `--context`.
* `--skill` (string, optional, repeatable): Narrow a local plugin `--context` to specific skills. Local plugin contexts only — not valid with a published context.
* `--skip-baseline` (boolean, optional): Skip the always-on baseline variant, running only the with-context variant.
* `--variant-json` (string, optional): Test variants to run, as a JSON array or the path to a JSON file (up to 1 MiB). The run solves only the listed variants. Cannot be combined with `--skip-baseline` or `--arms-json`. See [Comparing test variants](/improving-your-skills/evaluate-skill-quality-using-scenarios#comparing-test-variants).
* `--arms-json` (string, optional): Deprecated. Use `--variant-json`.
* `--allow-unsafe-fixture-paths` (boolean, optional): Allow directory fixtures, `localPath` variant fixtures, and packed symlinks that resolve outside the repository (or the source directory, when it is not in a repository). The CLI packages and uploads that external content for remote evaluation, where the evaluation agent can access it. Use this flag only for content you intend to share, including resolved symlink targets.
* `--runs` / `-n` (number, optional): Number of times to repeat each variant, from 1 to 10.
* `--label` / `-l` (string, optional): A short description to attach to this eval run. Visible in `tessl eval list` and the Tessl web UI.
* `--env-file` (string, optional): Path to a `.env` file whose variables are encrypted client-side and made available to the run's sandbox environment.
* `--skip-forced-context-activation` (boolean, optional): Omit the prompt line that tells the agent to use the injected context, so the run measures whether the agent activates the context on its own. Default off (the agent is told to use the context).
* `--skip-scoring` (boolean, optional): Skip the rubric scoring step. Default off (runs are scored).
* `--force` / `-f` (boolean, optional): Re-run all evals, including previously solved cases.
* `--quality-check` (boolean, optional): Exclude misleading scenarios (low feasibility, rubric leakage, low value) from the run. Excluded scenarios appear in `tessl eval view` under a `✗ Excluded by quality check` line.
* `--list-agents` (boolean, optional): Print the supported `--agent` / `--model` values, then exit without running an eval. Combine with `--json` for a machine-readable shape.
* `--yes` / `-y` (boolean, optional): Skip the confirmation prompt for large-scale eval runs.
* `--json` (boolean, optional): Output eval run IDs as JSON. On its own it detaches and prints the run ids immediately without polling; pass `--wait --json` to poll to completion and emit each run id with its final status.
* `--wait` / `--no-wait` (boolean, optional): Wait for the run to finish before returning (default: wait when interactive; use `--no-wait` to submit and return the run ids immediately, then reconnect with `tessl eval view`). `--json` on its own detaches; pass `--wait --json` to poll to completion and still get JSON.

**Arguments**

* `source` (string, optional): Path to a plugin folder or scenarios directory. Defaults to the current directory if omitted.

**Notes**

* Without `--variant-json`, a run has up to two variants. The **baseline** solves each scenario as authored; the **with-context** variant solves it with the injected context added on top. Baseline is not an empty run: each scenario keeps any fixtures it defines; no context is injected on top. The with-context variant runs when context is specified. Baseline runs by default; use `--skip-baseline` to run only the with-context variant. With `--variant-json`, the run has exactly the variants you list.
* Eval runs require this directory to be linked to a Tessl project first, and always run under that project's workspace. See [Projects overview](/projects/overview).
* To measure whether the agent activates the injected context on its own, with no rubric scoring, combine both flags: `--skip-forced-context-activation --skip-scoring`.
* Ctrl-C detaches without cancelling: runs continue server-side. The CLI prints each run ID.
* `--no-wait` submits the runs and returns their ids without polling; reconnect later with `tessl eval view`. `--json` alone also detaches, so pass `--wait --json` when a pipeline needs to block for the final per-run status.

**Agent and model:** `--agent` and `--model` are independent overrides; supply either or both. Run `tessl eval run --list-agents` for the supported combinations.

**Examples**

```sh
# Run a plugin's evals (shorthand for ./my-plugin/evals --context ./my-plugin)
$ tessl eval run .

# ...or point at the plugin by path
$ tessl eval run ./my-plugin

# Run a scenarios directory directly — baseline only, no context
$ tessl eval run ./evals/

# Force re-run all evals
$ tessl eval run --force ./my-plugin

# Run with a specific agent and model
$ tessl eval run ./evals/ --agent claude --model claude-sonnet-4-6

# List the supported --agent / --model values
$ tessl eval run --list-agents

# Skip scenarios the LLM judge flags as misleading
$ tessl eval run ./my-plugin --quality-check

# Measure context activation only: don't force the agent to use the context, and skip scoring
$ tessl eval run ./my-plugin --skip-forced-context-activation --skip-scoring

# Test a published context against a scenarios directory
$ tessl eval run ./evals/ --context engteam/my-plugin@1.0.0

# Test a local plugin directory as the context
$ tessl eval run ./evals/ --context ./my-plugin

# Test a glob of local files as the context
$ tessl eval run ./evals/ --context 'src/**/*.knowledge.md'

# Test a local context as it was at a specific commit
$ tessl eval run ./evals/ --context ./my-skill --context-commit HEAD

# Run only the with-context variant (skip the baseline)
$ tessl eval run ./my-plugin --skip-baseline

# Label a run for easier identification later
$ tessl eval run ./my-plugin --label "testing claude-opus-4-6"

# Pass secrets into the sandbox via an env file
$ tessl eval run ./my-plugin --env-file ./secrets.env
```

### tessl eval list

List recent eval runs in reverse chronological order.

**Usage**

```sh
tessl eval list [--json] [--limit <count>] [--mine] [--workspace <name>] [--plugin <name>] [--status <status>] [--context-type <type>]
```

**Flags**

* `--json` (boolean, optional): Output results as JSON
* `--limit` (number, optional): Maximum number of eval runs to display (default: 20)
* `--mine` (boolean, optional): Only show eval runs created by you
* `--workspace` / `-w` (string, optional): Filter by workspace name
* `--plugin` (string, optional): Filter by plugin name
* `--status` (string, optional): Filter by run status. Supported values: `pending`, `completed`, `failed`
* `--context-type` (string, optional): Filter by eval type. Supported values: `plugin`, `plugin-directory`, `files`, `commit`, `no-context`

**Notes**

* The output table includes ID, Type, Subject, Status, Created By, and Created columns
* The Type column reflects the context fixture used by the run:
  * `plugin` → `Plugin`
  * `plugin-directory` → `Plugin directory`
  * `files` (alias for the `directory` fixture) → `Selected files` when globs filter it to a subset, otherwise `Directory`
  * `commit` → `Commit`
* The Subject column formats entries by fixture: `plugin` runs as `workspace/plugin@version` (or just `workspace/plugin` when no version is set); `plugin-directory` and `files` as the source path; `commit` as `org/repo@ref`
* `--context-type no-context` filters to baseline runs that did not inject any context fixture
* `files` is the documented spelling of the canonical `directory` fixture — both names accepted

**Examples**

```sh
# List all recent eval runs
tessl eval list

# List only completed runs
tessl eval list --status completed

# List your runs in a specific workspace
tessl eval list --mine --workspace myteam

# Output as JSON
tessl eval list --json
```

### tessl eval view

View results for a specific eval run.

**Usage**

```sh
tessl eval view [--json] [--last] [<id>]
```

**Flags**

* `--json` (boolean, optional): Output results as JSON
* `--last` (boolean, optional): View results for the most recent eval run

**Arguments**

* `id` (string, optional): Eval run ID

**Examples**

```sh
# View a specific eval run
tessl eval view 019c4791-9eec-7458-b28a-6c94405a3d38

# View the most recent eval run
tessl eval view --last

# View results as JSON
tessl eval view --json 019c4791-9eec-7458-b28a-6c94405a3d38
```

### tessl eval retry

Re-run a failed eval run.

**Usage**

```sh
tessl eval retry [--json] [--last] [<id>]
```

**Flags**

* `--json` (boolean, optional): Output as JSON
* `--last` (boolean, optional): Retry the most recent eval run

**Arguments**

* `id` (string, optional): Eval run ID to retry. Either `id` or `--last` is required.

**Examples**

```sh
# Retry a specific eval run
tessl eval retry 019c4791-9eec-7458-b28a-6c94405a3d38

# Retry the most recent eval run
tessl eval retry --last
```

### tessl eval lint

Validate eval scenario structure. This checks the shape of the scenarios, not skill quality.

**Usage**

```sh
tessl eval lint [source]
```

**Arguments**

* `source` (string, optional): Path to a plugin folder or a scenarios directory.

**Notes**

* A scenario directory is recognized by the presence of `task.md`. Directories without `task.md` are skipped rather than reported as invalid, so an intended scenario that is missing the file passes lint silently. Each recognized scenario directory should contain a non-empty `task.md` and a `criteria.json` weighted checklist rubric.
* `scenario.json`, a `resources/` directory, and `setup.sh` are optional. `resources/` is included automatically and `setup.sh` runs automatically when present.
* Run `tessl eval lint --help` for the full `criteria.json` and `scenario.json` field reference, including the supported fixture types.

**Examples**

```sh
# Lint scenarios in the current directory
tessl eval lint

# Lint a specific plugin folder
tessl eval lint ./my-plugin

# Lint a scenarios directory
tessl eval lint ./evals
```

***

## Agent

The `tessl agent` command runs the Tessl agent directly from the CLI. The `tessl agent mcp` subcommands manage MCP server entries used by the Tessl agent.

### tessl agent

Run the Tessl agent in an interactive or non-interactive session.

**Usage**

```sh
tessl agent [flags] [prompt]
```

**Arguments**

* `prompt` (string, optional): Prompt to send to the agent.

**Flags**

* `--permission-mode` (string, optional): Permission mode for this run (supported values: `ask`, `accept-edits`, `auto`, `yolo`) (default: `auto`)
* `--yolo` (boolean, optional): Auto-approve every permission check (alias for `--permission-mode yolo`)
* `--print` / `-p` (boolean, optional): Run non-interactively, print the response, and exit
* `--output` (string, optional): Output format when `--print` is set (supported values: `text`, `json`) (default: `text`)
* `--message` (string, optional): Initial user message to send to the agent
* `--model` (string, optional): Model ID to use (example: `anthropic/claude-opus-4-6`)
* `--thinking` (string, optional): Thinking level (supported values: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`)
* `--continue` / `-c` (boolean, optional): Resume the most recent session for this directory
* `--resume` / `-r` (string, optional): Resume a specific session by ID (uuid or unambiguous prefix)
* `--no-session` (boolean, optional): Disable agent session persistence
* `--session-dir` (string, optional): Agent session storage directory
* `--ephemeral` (boolean, optional): Use in-memory agent settings for this run only
* `--tools` (string, optional): Comma-separated tool allowlist (example: `read,grep,find,ls`)
* `--no-builtin-tools` (boolean, optional): Disable built-in tools but keep extension tools enabled
* `--no-tools` (boolean, optional): Disable all tools
* `--extension` (string, repeatable, optional): Extension source to load (example: `./my-extension.ts` or `npm:my-ext`)
* `--no-extensions` (boolean, optional): Disable extension discovery while keeping explicit `--extension`
* `--skill` (string, repeatable, optional): Skill path to load
* `--no-skills` (boolean, optional): Disable skill discovery while keeping explicit `--skill`
* `--prompt-template` (string, repeatable, optional): Prompt template path to load
* `--no-prompt-templates` (boolean, optional): Disable prompt template discovery while keeping explicit `--prompt-template`
* `--theme` (string, repeatable, optional): Theme path to load
* `--no-themes` (boolean, optional): Disable theme discovery while keeping explicit `--theme`
* `--no-context-files` (boolean, optional): Disable AGENTS.md and CLAUDE.md discovery
* `--no-memory` (boolean, optional): Disable `.tessl/memory` injection and updates

**Notes**

* Uses Tessl-managed model settings and shared Tessl authentication. `tessl login` and `/login` inside the agent are equivalent.
* Use `--print` / `-p` for non-interactive use (combine with `--output json` for structured output).
* Use `--continue` / `-c` to resume the most recent session for the current directory, or `--resume` / `-r <id>` to reopen a specific session.

**Examples**

```sh
# Start an interactive agent session
tessl agent

# Send a one-shot prompt and print the response
tessl agent --print "Summarize the recent changes"

# Run non-interactively with JSON output
tessl agent --print --output json "List the files changed in the last commit"

# Resume the most recent session
tessl agent --continue

# Use a specific model
tessl agent --model anthropic/claude-opus-4-6

# Run in yolo mode (auto-approve all permissions)
tessl agent --yolo
```

### tessl agent mcp add

Add or update an MCP server entry in the agent's MCP configuration.

**Usage**

```sh
tessl agent mcp add [--scope <project|global>] [--url <url>] [--env KEY=VALUE]... [--header Header: value]... <name> [-- <command> [args...]]
```

**Arguments**

* `name` (string, required): MCP server name.

**Flags**

* `--scope` (string, optional): Config scope (supported values: `project`, `global`) (default: `project`). `project` writes to `.mcp.json` in the current directory; `global` writes to `~/.tessl/`.
* `--url` (string, optional): HTTP MCP server URL. Omit for stdio servers.
* `--env` / `-e` (string, repeatable, optional): Environment variable for stdio servers in `KEY=VALUE` format.
* `--header` (string, repeatable, optional): HTTP header for HTTP servers in `Header: value` format.

**Notes**

* For stdio servers: omit `--url` and pass the server command after `--` (e.g., `-- npx my-server`).
* For HTTP servers: pass `--url`. Use `--header` for authentication headers.
* If the server name already exists, it is overwritten.

**Examples**

```sh
# Add a stdio MCP server
tessl agent mcp add my-server -- npx my-mcp-server

# Add a stdio server with environment variables
tessl agent mcp add my-server --env API_KEY=abc123 -- npx my-mcp-server

# Add an HTTP MCP server
tessl agent mcp add my-server --url https://mcp.example.com

# Add an HTTP server with an authentication header
tessl agent mcp add my-server --url https://mcp.example.com --header "Authorization: Bearer token"

# Add to global scope (available across all projects)
tessl agent mcp add my-server --scope global -- npx my-mcp-server
```

### tessl agent mcp get

Show a configured MCP server entry.

**Usage**

```sh
tessl agent mcp get [--json] <name>
```

**Arguments**

* `name` (string, required): MCP server name.

**Flags**

* `--json` (boolean, optional): Emit the resolved server entry as JSON.

### tessl agent mcp list

List all configured MCP servers.

**Usage**

```sh
tessl agent mcp list [--scope <project|global>] [--json]
```

**Flags**

* `--scope` (string, optional): Config scope to list (supported values: `project`, `global`). When omitted, servers from all scopes are listed.
* `--json` (boolean, optional): Output as JSON.

### tessl agent mcp remove

Remove an MCP server entry from the agent's MCP configuration.

**Usage**

```sh
tessl agent mcp remove [--scope <project|global>] <name>
```

**Arguments**

* `name` (string, required): MCP server name.

**Flags**

* `--scope` (string, optional): Config scope to remove from (supported values: `project`, `global`) (default: `project`)

***

## Change

The `tessl change` family assesses and verifies the current diff against your team's policies. Commands require a checked-in configuration and produce structured output suitable for CI use.

### tessl change risk

Assess the risk of the current PR/diff against the team's checked-in risk policy and decide whether it requires human review.

**Usage**

```sh
tessl change risk [--base <ref>] [--output <path>] [--fail-if-review-required] [--json]
```

**Flags**

* `--base` (string, optional): Diff base ref (default: the `baseRef` in the risk config, or `origin/main`)
* `--output` (string, optional): Write results to a file
* `--fail-if-review-required` (boolean, optional): Exit non-zero when scoring succeeds but human review is required
* `--json` (boolean, optional): Output as JSON

**Notes**

* Requires a risk policy under `.github/pr-review-gate`. Run `tessl change risk init` first if it is missing.
* Scoring is split into deterministic git measurements, an advisory agent risk judgment, strict validation, and a deterministic gate decision.
* Tooling failures (missing config, failed agent call, malformed output) are reported as distinct errors rather than a "human review required" result.

**Examples**

```sh
# Assess risk for the current diff
tessl change risk

# Assess risk and exit non-zero if human review is required (for CI use)
tessl change risk --fail-if-review-required

# Write results to a file as JSON
tessl change risk --output risk.json --json
```

### tessl change risk init

Write PR review-gate starter files under `.github/pr-review-gate`.

**Usage**

```sh
tessl change risk init [--policy <starter>] [--force] [--json]
```

**Flags**

* `--policy` (string, optional): Starter policy to write. Supported values:
  * `conservative-starter`: strict — clears only clearly surface-level changes; requires review for real behavior changes, sensitive paths, migrations, auth, architecture, and policy/config changes.
  * `official-review-policy-parity`: permissive — requires human review only when the official written review policy requires it.
  * If omitted, prompts interactively.
* `--force` (boolean, optional): Overwrite existing files.
* `--json` (boolean, optional): Output as JSON.

**Notes**

* Writes `config.json`, `policy.md`, and `prompt.md` under `.github/pr-review-gate`.
* Existing files are left untouched unless `--force` is passed.
* Read and edit the generated policy and config before relying on the gate.
* This command does not change branch protection rules or create any GitHub workflow.

**Examples**

```sh
# Write starter files interactively
tessl change risk init

# Write the conservative starter without prompting
tessl change risk init --policy conservative-starter

# Overwrite existing files
tessl change risk init --policy conservative-starter --force
```

### tessl change verify

Verify changed files against configured verifiers.

**Usage**

```sh
tessl change verify [--json] [--all] [--recursive] [--base <ref>] [--changed-files-mode introduced-only|whole-file] [--group <name>] [--verifier <id>]... [--include <glob>]... [--exclude <glob>]... [--max-concurrency <n>] [--max-calls <n>] [--sample <n>] [--batch-timeout <s>] [--dry-run] [--show-files] [--include-disabled] [--output <path>] [--report-json <path>] [--no-cache] [--refresh-cache] [--verbose] [--strict-coverage] [--fail-on-warn] [--github] [<path>...]
```

**Arguments**

* `path` (string, repeatable, optional): Optional path scope (file or directory) to verify

**Flags**

* `--json` (boolean, optional): Output as JSON
* `--all` (boolean, optional): Verify every matching file, not just the diff
* `--recursive` (boolean, optional): Verify each configured root from this project plus its child manifests
* `--base` (string, optional): Diff base ref (default: `main`)
* `--changed-files-mode` (string, optional): How to judge changed files. Supported values: `introduced-only` (gates on diff-attributable violations), `whole-file` (judges each changed file in full) (default: `introduced-only`)
* `--group` (string, optional): Run only this verifier group from the manifest
* `--verifier` (string, repeatable, optional): Run only this verifier id (repeatable)
* `--include` (string, repeatable, optional): Override include glob (repeatable)
* `--exclude` (string, repeatable, optional): Override exclude glob (repeatable)
* `--max-concurrency` (number, optional): Cap parallel judge calls (default: 48)
* `--max-calls` (number, optional): Refuse broad runs above this many judge calls (default: 300)
* `--sample` (number, optional): Run a deterministic sample of verifier targets
* `--batch-timeout` (number, optional): Abort the judge batch if it runs longer than this many seconds
* `--dry-run` (boolean, optional): Show what a verify run would do without spending judge calls
* `--show-files` (boolean, optional): Print the matched file list for each verifier (use with `--dry-run`)
* `--include-disabled` (boolean, optional): Also run groups marked `enabled=false`
* `--output` (string, optional): Write the report to a file instead of stdout
* `--report-json` (string, optional): Write the structured JSON report to a file, leaving stdout as-is. Uses the same shape as `--json` (a recursive run writes the per-root envelope). Combine with `--github` to write the report and emit annotations in one run
* `--no-cache` (boolean, optional): Skip the local result cache entirely (no reads, no writes)
* `--refresh-cache` (boolean, optional): Skip cache reads but save new results
* `--verbose` (boolean, optional): Show every considered file/verifier target
* `--strict-coverage` (boolean, optional): Stale `covered_by` links cause non-zero exit even when judges pass
* `--fail-on-warn` (boolean, optional): Treat warning verdicts as non-zero exit
* `--github` (boolean, optional): Emit GitHub Actions warning/error annotations for findings

**Examples**

```sh
# Verify the current diff against configured verifiers
tessl change verify

# Preview what a run would do without spending judge calls
tessl change verify --dry-run --show-files

# Verify only a specific group and emit GitHub annotations (for CI use)
tessl change verify --group my-group --github

# Write a machine-readable report for CI while still emitting annotations
$ tessl change verify --github --report-json verify-report.json
```

### tessl change verify suggest-scopes

Suggest repo-specific verifier scopes for a group.

**Usage**

```sh
tessl change verify suggest-scopes [--json] [--group <name>] [--heuristic] [--no-llm] [--apply] [--force]
```

**Flags**

* `--json` (boolean, optional): Output as JSON
* `--group` (string, optional): Suggest scopes only for this verifier group
* `--heuristic` (boolean, optional): Use the offline heuristic instead of the default LLM suggestion
* `--no-llm` (boolean, optional): Alias for `--heuristic`
* `--apply` (boolean, optional): Write suggestions into `tessl.json`
* `--force` (boolean, optional): Replace existing verifier-level overrides

### tessl change verify lint

Validate a verifier JSON file against the schema.

**Usage**

```sh
tessl change verify lint <verifier.json>
```

**Arguments**

* `verifier.json` (string, required): Path to the verifier JSON file to validate

***

## Code review

### tessl code review

Run a Tessl Code Review over a code change and print the result. `tessl code review` is shorthand for `tessl code review run`.

**Usage**

```sh
tessl code review [--profile <name-or-path>] [--skill <skill-ref>]... [--effort <level>] [--approver <login>]... [--pr <ref>] [--base <ref>] [--head <ref>] [--pr-fixture <path>] [--publish <comment|verdict>] [--json]
```

**Flags**

* `--profile` (string, optional): Review profile. Use the built-in `standard` profile (the default), or pass an explicit path to a repository-owned `.yml` or `.yaml` file.
* `--skill` (string, repeatable, optional): Reviewer lens: a local SKILL.md path or skill directory, an installed skill name, or a registry ref `workspace/plugin[@version]#skill-name`. Order is preserved, and a maximum of 8 lenses is supported. Passing `--skill` replaces the profile's default lens set entirely rather than adding to it.
* `--pr` (string, optional): Review a GitHub pull request or GitLab merge request by reference (number, URL, or provider ref) and capture its context. Mutually exclusive with `--base` and `--head`.
* `--base` (string, optional): Base ref of an explicit Git range to review. Defaults to the repository's own default branch when omitted. No pull request is required.
* `--head` (string, optional): Head ref of an explicit Git range to review. Defaults to the local working tree, including uncommitted changes, when omitted.
* `--pr-fixture` (string, optional): Replay a versioned pull-request fixture file instead of resolving a live pull request (experimental). Mutually exclusive with `--pr`, `--base`, and `--head`.
* `--effort` (string, optional): How hard each review lens thinks: `low`, `medium`, `high`, or `adaptive`. Applies to every lens, overriding any `effort` the profile sets. `adaptive` uses `medium` for a full review and `low` for a re-review; `low`, `medium`, and `high` stay fixed at that level. The built-in `standard` profile uses `adaptive`, so omitting `--effort` on a standard review already scales this way. Omit it and each lens uses its own `effort` where the profile gives it one, then the profile's `effort`, then the model's own default. Higher settings take longer.
* `--approver` (string, repeatable, optional): A comment-author login permitted to ask the review to approve the pull request instead of reviewing it, beyond the repository members who always may. The same allowlist also permits requesting a full re-review of the whole change (rather than the narrower incremental round), so a login named here gains both privileged intents, not just approval. Repository owners, members, and collaborators can already do both; a GitHub App cannot, because it comments with association `NONE` whatever its permissions, so name its `[bot]` login here to let it approve or request a full review. Capitalization does not matter. Omit it to name no extra login, which does not stop a member using either.
* `--publish` (string, optional): Publish the completed review to the pull request or merge request it reviewed. On GitHub, `comment` publishes a review that neither approves nor blocks; `verdict` approves or requests changes according to the review result. On GitLab, publication posts a summary and inline discussions; `verdict` falls back to a comment and does not submit native approvals or request-changes decisions. Cannot be combined with `--pr-fixture`.
* `--json` (boolean, optional): Output as JSON

**Notes**

* With no selection flags, the review auto-selects its subject: the current GitHub pull request or GitLab merge request in a supported CI environment, otherwise the complete local change. A local change is diffed against the repository's default branch, taken from the remote's recorded `origin/HEAD` and, in a clone that records none, from `origin/main` or `origin/master`. Where none of those resolves, the command says so rather than guessing, and `--base` names the ref to use instead.
* A local commit range with nothing in it is not reviewed. On a clean, up-to-date checkout the command exits successfully reporting that there are no changes to review, having run no lens, rather than reporting an approval it has no change to base one on. A working-tree change and a pull request are always reviewed.
* Reviewing a GitHub pull request in CI requires GitHub authentication: `GITHUB_TOKEN` or `GH_TOKEN` in the environment, or an authenticated `gh` session. Local runs without credentials fall back to anonymous access to public repositories.
* GitLab authentication uses `GITLAB_TOKEN` or `GL_TOKEN`, then an authenticated `glab` session. Context requires `read_api`; publication requires `api`. `CI_JOB_TOKEN` alone is insufficient. Tessl authentication uses `TESSL_TOKEN` separately in CI. See [Run Code Review with GitLab](/tutorials/setting-up-agentic-code-review/code-review-in-gitlab).
* Full request URLs select their provider explicitly. Local numbers are rejected when fetch or push remotes span GitHub and GitLab; use a full URL. GitHub Actions selects GitHub when `GITHUB_ACTIONS=true` and `GITHUB_REPOSITORY` are present. GitLab merge request pipelines select GitLab when `GITLAB_CI=true` and `CI_MERGE_REQUEST_PROJECT_PATH` are present. Outside those cases, use a full URL when remotes are mixed.
* The default lenses come from the `standard` profile and are pinned to a specific version of the `tessl/code-review` plugin, so the default review does not change when the plugin publishes a new version.
* Selected lenses contribute to one review of the whole change.
* Transcript capture and upload require the internal `TESSL_CODE_REVIEW_USAGE_DIAGNOSTICS=1` gate during the initial release. A gated CI or Cloud Launch run captures without project configuration; a gated developer run also requires agent-log collection with the `tessl-cli` provider enabled. The general usage-sharing preference does not control this diagnostic-only capture.
* By default the command runs a review and prints the result, publishing nothing. Pass `--publish` to post the completed review to the request it reviewed. GitLab receives a summary note and inline discussions. GitHub receives one native review: a summary body, an inline comment on each finding GitHub can anchor to a changed line, and a reply on each earlier finding's thread this round reconciled.
* The following publication fallback and receipt details describe GitHub. For GitLab thread reconciliation, revision checks, and comment-only verdict behavior, see [Run Code Review with GitLab](/tutorials/setting-up-agentic-code-review/code-review-in-gitlab#publish-findings-and-review-again).
* Inline placement is attempted, not guaranteed. A finding GitHub cannot anchor to a changed line is carried in the summary body instead. If GitHub rejects the whole batch because it cannot resolve an anchor, the CLI retries once with every finding in the summary instead, and that retry can itself fail or find that the head has moved on. Replies to earlier threads are best effort in the same way: a failure to read or post them warns and leaves the published review standing.
* `--publish` takes a value rather than being a boolean, because the two ways to publish differ in what they claim. `comment` states no position on the change. `verdict` carries the review's own conclusion, and the CLI resolves it into an approval or a request for changes once the review has run, since the verdict does not exist before then. There is no way to ask for an approval that contradicts what the review found. Some rejections of a review carrying that event lead the CLI to retry once as a plain comment, and the receipt records that downgrade; others end the run without publishing. Repository policy and token permissions are the usual reasons, but the receipt does not establish the cause, and the retry can fail as well.
* Publishing applies to pull requests and merge requests, needs provider credentials that can write reviews or discussions, and is refused for a replayed fixture.
* With `--json`, a `publication` receipt appears once the run reaches a terminal publication state, and is absent otherwise, including when the review itself failed and when publication failed. Its `status` is `published`, `published-with-policy-fallback` (the verdict was downgraded to a comment), `superseded` (the pull request head moved while the review ran, so nothing was published), or `reused` (this attempt had already published its review). A consumer that depends on the review event should branch on `status` first: `publishedEvent` is carried by `published` and `published-with-policy-fallback` only, and it says what was submitted rather than what was requested.
* The [Tessl Review GitHub App](/tutorials/setting-up-agentic-code-review) is the supported way to run reviews on GitHub pull requests automatically.

#### YAML file profiles

Pass a profile path explicitly. Tessl does not discover profile files automatically:

```sh
$ tessl code review --profile ./.tessl-code-review.yml
```

The file must be a block-style YAML mapping with `schemaVersion: 1` and a non-empty, ordered `lenses` list:

```yaml
schemaVersion: 1
reviewMode: standard
requestChangesAt: major
ignore:
  - '**/*.generated.ts'
  - vendor/**
  - '**/__snapshots__/**'
lenses:
  - ref: ./review-lenses/backend/SKILL.md
    globs:
      - apps/backend/**
      - '!apps/backend/**/*.generated.ts'
  - ref: tessl/code-review@0.2.0#review-security-and-privacy
    globs:
      - infra/**
  - ref: ./review-lenses/general/SKILL.md
```

Each `ref` is a local `SKILL.md` path or skill directory, an installed skill name, or a registry reference. Local paths are resolved relative to the profile and must remain inside the repository. A lens without `globs` applies to the whole change.

Globs are case-sensitive, repository-relative POSIX patterns. Each list must contain at least one positive pattern. Patterns are evaluated in order: a matching positive pattern includes a path, a matching pattern prefixed with `!` excludes it, and a later positive pattern can include it again. For a renamed file, the lens is selected when either the old or new path matches.

The top-level `ignore` list bounds which paths any lens reviews, so you exclude generated code, lockfiles, snapshots, or vendored directories once instead of repeating a `!` negation in every lens. Each pattern is a repository-relative POSIX glob, and each is positive: it excludes what it matches, so a leading `!` is rejected rather than read as a double negative. The list applies to every lens, including a lens that declares no `globs` of its own; a lens's own `globs` still narrow it further, and `ignore` overrides anything a lens selected. At most 64 patterns are allowed, and a bare `**` (which would exclude everything) is rejected. Because a file is selected when either rename endpoint matches, a file renamed into an ignored path is still reviewed, and that ignored path appears in the rename patch. `ignore` bounds which paths a lens reviews; it is not a way to keep paths or their contents out of a model's context.

Each lens receives only its selected files in its prompt and the `diff_stat`, `diff_files`, `diff_file`, and `diff_full` tools. Lenses with no matching files do not run or count toward the limit of eight lenses. If no lens matches, the command exits successfully with a skipped result and does not imply approval.

Passing `--skill` replaces the file profile's complete lens list and its routing.

The file may set `effort` for every lens, and any lens may set its own `effort` to think harder or less hard than the rest. A lens without one runs at the file's `effort`; with neither, the model applies its own default. `--effort` applies to every lens and takes precedence over both.

`reviewMode` controls the blocking strength required from an eligible finding. `strict` blocks at zero, `standard` at one, and `relaxed` at two. Omit it to use `standard`. Low-confidence fresh findings are omitted in every mode. Pre-existing findings never block.

Each valid finding is assessed internally by consequence severity, likelihood, confidence, and relationship to the change. The CLI combines consequence and likelihood into the stable published severity: Critical consequence becomes Critical, Major, or Minor at High, Medium, or Low likelihood; Major consequence becomes Major, Major, or Minor; Minor consequence becomes Minor, Minor, or Nit; and Nit remains Nit. Confidence remains supporting assessment data rather than changing severity or blocking strength.

`requestChangesAt` sets the minimum consequence severity eligible to block: `critical`, `major`, `minor`, or `nit`, defaulting to `major`. Each consequence level above that baseline adds one strength point; Low, Medium, and High likelihood add zero, one, and two. A finding blocks when its strength reaches the selected review mode's threshold.

Because published severity also reflects likelihood, a Strict or Standard review can require a change for a finding displayed below `requestChangesAt`. The finding's `requiresChanges` value is authoritative.

The first review and an authorised full re-review show every valid optional finding. An incremental re-review shows a fresh finding only when it blocks and continues to reconcile earlier findings.

Whether a changes-requested result blocks a merge depends on how the review runs, and in every case a merge is held only by the repository's branch protection or ruleset. Under the [Tessl Review GitHub App](/tutorials/setting-up-agentic-code-review), the repository's review behavior (Enforcing by default, or Advisory) sets whether the `Tessl Reviewer` check passes or fails on a changes-requested result; that check holds a merge only when branch protection requires it. Under the unsupported [GitHub Action](/tutorials/setting-up-agentic-code-review/code-review-action), the Action's `mode` input decides whether its check fails, and again branch protection decides whether a failing check holds the pull request.

File profiles also work with the GitHub Action. Set its `profile` input to the repository-relative path.

**Examples**

```sh
# Review the current change (the current PR in CI, otherwise the local change)
tessl code review

# Review a specific pull request
tessl code review --pr 128

# Review a GitLab merge request and publish comments
$ tessl code review --pr https://gitlab.com/engteam/my-project/-/merge_requests/42 --publish comment

# Review an explicit Git range
tessl code review --base origin/main --head my-branch

# Review with specific lenses (replaces the default set; pin the version)
tessl code review --skill tessl/code-review@0.2.0#review-security-and-privacy --skill tessl/code-review@0.2.0#review-correctness-and-data-integrity

# Route a local review with a repository-owned YAML profile
$ tessl code review --profile ./.tessl-code-review.yml

# Review a pull request and publish the verdict to it
$ tessl code review --pr 128 --publish verdict

# Let a bot login ask the review to approve instead of reviewing
tessl code review --pr 128 --approver my-agent[bot]

# Output as JSON
tessl code review --json
```

***

## Launch

The `tessl launch` family runs agent workflows. Use `tessl launch skill` to run a specific skill with a chosen agent, or `tessl launch <agent>` to open an interactive agent session. The `list`, `view`, and `logs` subcommands inspect past cloud launch runs.

### tessl launch \<agent>

Launch a coding agent in an interactive session.

**Usage**

```sh
tessl launch <agent>
```

**Arguments**

* `agent` (string, required): Coding agent to launch. Supported values: `claude-code`, `codex`, `gemini`, `antigravity`, `cursor`, `copilot`, `openhands`, `tessl-agent`.

**Examples**

```sh
# Launch Claude Code
tessl launch claude-code

# Launch Cursor
tessl launch cursor
```

### tessl launch skill

Run a skill using an agent.

**Usage**

```sh
tessl launch skill <skill> [--agent <agent>] [--interactive] [--yolo] [--instructions <text>]
tessl launch skill <skill> --cloud --repo <owner/name> [--agent <agent>] [cloud flags]
```

**Arguments**

* `skill` (string, required): The skill to run. The local and cloud modes accept different argument forms.

  **Local (without `--cloud`)**

  * Installed skill name: a bare name with no slashes (e.g., `canary-reporter`), resolved from skills installed in the current project.
  * `file:path`: resolved on your machine. Without a `#skill` selector, the directory must contain a top-level `SKILL.md`. With a `#skill` selector (e.g., `file:./my-plugin#review-diff`), it looks for `skills/<name>/SKILL.md` inside the directory.

  Any value containing a `/` is treated as a filesystem path, not a registry ref. Registry-style values (e.g., `workspace/plugin`) do not resolve remotely in local mode; they are looked up as relative paths on disk.

  **Cloud (with `--cloud`)**

  * Registry ref: `workspace/plugin[@version]`, or a bare `plugin[@version]` resolved across the workspaces you can see.
  * `file:path/to/plugin`: a path inside the cloned target repository (not the caller's machine), pointing at the plugin root (the directory holding `.tessl-plugin/plugin.json` or `tile.json`).

  Either cloud form may carry a trailing `#skill` selector (for example `workspace/plugin@1.0.0#review-diff` or `file:tiles/my-plugin#review-diff`). It installs the whole plugin and runs the named skill. The selector is required when the plugin exposes more than one skill.

**Flags**

* `--agent` (string, optional): Agent to run the skill with. Supported values: `claude-code`, `codex`, `gemini`, `antigravity`, `cursor`, `copilot`, `openhands`, `tessl-agent`. Required for local (non-cloud) runs; with `--cloud` it selects the cloud strategy (supported: `claude-code`, `cursor`, `codex`, `tessl-agent`).
* `--interactive` / `-i` (boolean, optional): Run the agent in the foreground instead of streaming output.
* `--yolo` (boolean, optional): Skip agent permission prompts and approval checks.
* `--instructions` (string, optional): Instructions to append to the skill prompt.

**Notes**

* In cloud mode, a `file:` path must point at the plugin root inside the target repository. Pointing it at a skill subdirectory fails at the install step because that subdirectory has no `.tessl-plugin/plugin.json` or `tile.json`. Use the plugin root with a `#skill` selector instead.
* In local mode, the `#skill` selector looks for `skills/<name>/SKILL.md` relative to the given directory. It applies when the directory is a multi-skill plugin root. It does not apply to a single-skill directory (which uses a top-level `SKILL.md`) or a bare installed skill name.
* Local mode does not resolve registry-style refs (e.g., `workspace/plugin`). Any value with a `/` is treated as a filesystem path.

**Cloud flags**

Run the skill against a repository in the cloud rather than locally.

* `--cloud` (boolean, optional): Run the skill against a repo in the cloud.
* `--repo` (string, optional): Target repository as `owner/name` or URL (required with `--cloud`).
* `--base-branch` (string, optional): Branch to clone and target the PR against (default: `main`).
* `--env-file` (string, optional): Path to a `.env` file whose variables are encrypted and made available to the cloud launch.
* `--workspace` (string, optional): Name of workspace to launch in (default: linked project workspace).
* `--wait` / `--no-wait` (boolean, optional): Wait for the cloud launch to finish before returning (default: wait; use `--no-wait` to return immediately).
* `--label` (string, optional): Label to describe this launch run.
* `--model` (string, optional): Model to use for the cloud launch run.
* `--cwd` (string, optional): Subdirectory to run the skill in, relative to the current directory (default: repo root).

**Examples**

```sh
# Run an installed skill locally by name
tessl launch skill canary-reporter --agent tessl-agent

# Run a skill from a single-skill directory (contains a top-level SKILL.md)
tessl launch skill file:./my-skill --agent claude-code

# Run one skill from a multi-skill plugin directory
tessl launch skill file:./my-plugin#review-diff --agent tessl-agent

# Run a skill interactively with Claude Code
tessl launch skill file:./my-plugin#review-diff --agent claude-code --interactive

# Skip permission prompts
tessl launch skill file:./my-plugin#review-diff --agent tessl-agent --yolo

# Run with additional instructions
tessl launch skill file:./my-plugin#review-diff --agent tessl-agent --instructions "Focus on the auth module only"

# Run a registry plugin in the cloud and wait for the result
tessl launch skill engteam/my-plugin --cloud --repo tesslio/monorepo --agent tessl-agent

# Choose one skill from a multi-skill registry plugin in the cloud
tessl launch skill engteam/my-plugin#review-diff --cloud --repo tesslio/monorepo --agent tessl-agent

# Run a local plugin path against a cloud repo
tessl launch skill file:tiles/my-plugin#review-diff --cloud --repo tesslio/monorepo --agent tessl-agent

# Trigger a cloud launch without waiting for completion
tessl launch skill engteam/my-plugin --cloud --repo tesslio/monorepo --no-wait
```

### tessl launch list

List recent launch runs in reverse chronological order.

**Usage**

```sh
tessl launch list [--workspace <name>] [--limit <count>] [--json]
```

**Flags**

* `--workspace` / `-w` (string, optional): Filter by workspace name.
* `--limit` (number, optional): Maximum number of launch runs to display (default: 20).
* `--json` (boolean, optional): Output as JSON.

**Examples**

```sh
# List recent launch runs
tessl launch list

# List runs for a specific workspace
tessl launch list --workspace engteam

# List as JSON
tessl launch list --json
```

### tessl launch resume

Resume a completed cloud launch run with its saved workspace archive. A run stays resumable for 7 days after that archive is saved. After that the archive is deleted, so start a new launch run instead.

**Usage**

```sh
tessl launch resume <id> [--instructions <text>] [--label <label>] [--json]
```

**Arguments**

* `id` (string, required): Parent launch run ID to resume.

**Flags**

* `--instructions` / `-i` (string, optional): Follow-up instructions for the resumed run. Overrides the parent's instructions.
* `--label` (string, optional): Label for the resumed run.
* `--json` (boolean, optional): Output as JSON.

**Examples**

```sh
# Resume a recent run with follow-up instructions
tessl launch resume 019c4791-9eec-7458-b28a-6c94405a3d38 --instructions "Address the review comments"
```

### tessl launch view

View status and details for a launch run.

**Usage**

```sh
tessl launch view [--json] <id>
```

**Arguments**

* `id` (string, required): Launch run ID.

**Flags**

* `--json` (boolean, optional): Output as JSON.

**Examples**

```sh
# View a specific launch run
tessl launch view 019c4791-9eec-7458-b28a-6c94405a3d38
```

### tessl launch logs

Print agent logs for a launch run.

**Usage**

```sh
tessl launch logs <launchRunId>
```

**Arguments**

* `launchRunId` (string, required): Launch run ID.

**Examples**

```sh
# Print logs for a launch run
tessl launch logs 019c4791-9eec-7458-b28a-6c94405a3d38
```

***

## Schedules

The `tessl schedule` family runs a plugin against a repository on a cron. The plugin owns the task; the schedule owns the timing. Each time a schedule fires, Tessl starts a cloud launch run and records the result. Inspect schedules with `list`, `view`, and `runs`, and manage them with `pause`, `resume`, `update`, `trigger`, and `delete`.

For the concepts and a walkthrough, see [Schedules](/automations/overview/overview). For the secrets a run executes with, see [Environments](/automations/environments).

{% hint style="info" %}
Tessl skips a tick if the previous run from the same schedule is still active when the next one would start. There is no queue, so a skipped tick does not run later.
{% endhint %}

### tessl schedule list

List scheduled runs.

**Usage**

```sh
tessl schedule list [--workspace <name>] [--status <status>] [--limit <count>] [--json]
```

**Flags**

* `--workspace` / `-w` (string, optional): Filter by workspace name.
* `--status` (string, optional): Filter by status. Supported values: `active`, `paused`.
* `--limit` (number, optional): Maximum number of scheduled runs to display (default: 20).
* `--json` (boolean, optional): Output as JSON.

**Examples**

```sh
# List scheduled runs
tessl schedule list

# List only paused schedules
tessl schedule list --status paused

# List schedules in a specific workspace
tessl schedule list --workspace engteam

# List as JSON
tessl schedule list --json
```

### tessl schedule view

View config, cron, and next/last run for a scheduled run.

**Usage**

```sh
tessl schedule view [--json] <id>
```

**Arguments**

* `id` (string, required): Scheduled run ID.

**Flags**

* `--json` (boolean, optional): Output as JSON.

**Examples**

```sh
# View a schedule
tessl schedule view 019c4791-9eec-7458-b28a-6c94405a3d38
```

### tessl schedule runs

List the launch runs a scheduled run has spawned.

**Usage**

```sh
tessl schedule runs [--limit <count>] [--json] <id>
```

**Arguments**

* `id` (string, required): Scheduled run ID.

**Flags**

* `--limit` (number, optional): Maximum number of launch runs to display (default: 20).
* `--json` (boolean, optional): Output as JSON.

**Examples**

```sh
# List the runs a schedule has spawned
tessl schedule runs 019c4791-9eec-7458-b28a-6c94405a3d38

# Show the last 5 runs as JSON
tessl schedule runs 019c4791-9eec-7458-b28a-6c94405a3d38 --limit 5 --json
```

### tessl schedule pause

Pause a scheduled run.

**Usage**

```sh
tessl schedule pause [--json] <id>
```

**Arguments**

* `id` (string, required): Scheduled run ID.

**Flags**

* `--json` (boolean, optional): Output as JSON.

**Examples**

```sh
# Pause a schedule
tessl schedule pause 019c4791-9eec-7458-b28a-6c94405a3d38
```

### tessl schedule resume

Resume a paused scheduled run.

**Usage**

```sh
tessl schedule resume [--json] <id>
```

**Arguments**

* `id` (string, required): Scheduled run ID.

**Flags**

* `--json` (boolean, optional): Output as JSON.

**Examples**

```sh
# Resume a paused schedule
tessl schedule resume 019c4791-9eec-7458-b28a-6c94405a3d38
```

### tessl schedule update

Update a scheduled run's cron, timezone, or label.

**Usage**

```sh
tessl schedule update [--cron <expr>] [--timezone <name>] [--label <text>] [--json] <id>
```

**Arguments**

* `id` (string, required): Scheduled run ID.

**Flags**

* `--cron` (string, optional): New cron expression, interpreted in the schedule timezone unless `--timezone` is also provided.
* `--timezone` (string, optional): New IANA timezone the cron is interpreted in.
* `--label` (string, optional): New label to describe this schedule.
* `--json` (boolean, optional): Output as JSON.

**Notes**

* At least one of `--cron`, `--timezone`, or `--label` is required.
* Only the cron, timezone, and label can be changed. To change the skill, repo, environment, or any other launch config, delete the schedule and create a new one.

**Examples**

```sh
# Move a schedule to 07:00 daily
tessl schedule update 019c4791-9eec-7458-b28a-6c94405a3d38 --cron "0 7 * * *"

# Change the timezone
tessl schedule update 019c4791-9eec-7458-b28a-6c94405a3d38 --timezone America/New_York

# Rename a schedule
tessl schedule update 019c4791-9eec-7458-b28a-6c94405a3d38 --label "Nightly docs audit"
```

### tessl schedule trigger

Fire a scheduled run now, out of band.

**Usage**

```sh
tessl schedule trigger [--json] <id>
```

**Arguments**

* `id` (string, required): Scheduled run ID.

**Flags**

* `--json` (boolean, optional): Output as JSON.

**Notes**

* Triggering dispatches a launch run asynchronously and returns immediately. It does not change the cron and does not affect the next scheduled tick. Use `tessl schedule runs <id>` to see the spawned run and its result.

**Examples**

```sh
# Run a schedule now
tessl schedule trigger 019c4791-9eec-7458-b28a-6c94405a3d38
```

### tessl schedule delete

Delete a scheduled run.

**Usage**

```sh
tessl schedule delete <id>
```

**Arguments**

* `id` (string, required): Scheduled run ID.

**Notes**

* Deleting a schedule stops future runs. Launch runs it has already spawned are kept.

**Examples**

```sh
# Delete a schedule
tessl schedule delete 019c4791-9eec-7458-b28a-6c94405a3d38
```


# Configuration files

## Plugin configuration

### plugin.json

The `.tessl-plugin/plugin.json` file is the configuration file for a plugin. It includes important metadata that helps Tessl identify, version and describe plugins.

Plugins can contain four types of content:

* **rules**: Rule files (markdown) that provide subjective guidance and instructions to agents, such as coding standards, best practices, and organizational policies.
* **skills**: Skill files (SKILL.md format) that extend AI coding agents with specialized knowledge, workflows, or tool integrations. Skills follow the [Agent Skills Specification](https://agentskills.io/specification).
* **MCP servers**: [Model Context Protocol](https://modelcontextprotocol.io) servers, declared in a bundled `.mcp.json` file, that connect agents to external tools and services during a session.
* **hooks**: Deterministic commands that run automatically in response to agent events (for example, running a linter after an edit), declared with the `hooks` and `nativeHooks` fields.

A plugin can contain one or more of these content types. For example, a plugin might have only rules, only skills, only MCP servers, or any combination of the four.

Here's an example `.tessl-plugin/plugin.json`:

```json
{
  "name": "tessl/skill-optimizer",
  "version": "0.8.1",
  "description": "Optimize your skills and plugins: review SKILL.md quality, generate eval scenarios, run evals, compare across models, diagnose gaps, and re-run until scores improve.",
  "private": false
}
```

The following sections show examples of plugins with different content types. Remember that plugins can contain **rules**, **skills**, **MCP servers**, or **hooks**.

For example, here's what the `./tessl-plugin/plugin.json` for a private, rules-only plugin might look like:

```json
{
  "name": "myorg/code-style",
  "version": "0.0.1",
  "description": "TypeScript code style guidelines for MyOrg",
  "private": true,
  "rules": "rules" // not needed, when rules is omitted, "rules" is used as the path by convention
}
```

Plugins can also contain skills, which are reusable capabilities that extend AI coding agents. Here's an example of a plugin with skills:

```json
  {
  "name": "myworkspace/my-skill",
  "version": "1.0.0",
  "description": "A skill that provides specialized functionality for my agent",
  "private": true,
  "skills": "skills" // not needed, when skills is omitted, "skills" is used as the path by convention
}
```

And here's a plugin that combines rules, and skills:

```json
{
  "name": "myorg/comprehensive-plugin",
  "version": "2.0.0",
  "description": "Complete plugin with rules, and skills",
  "rules": "rules",      // can be omitted, set to "rules" when missing
  "skills": "skills",    // can be omitted, set to "skills" when missing
}
```

`.tessl-plugin/plugin.json` supports the following fields:

`name (string, required)`: Name for the plugin in workspace/plugin-name format

`version (string, required)`: Semantic version of the plugin

`description (string, required)`: Brief description of the plugin

`private (boolean, optional)`: Controls plugin visibility in the registry. Set to false to make the plugin publicly discoverable by all users, or true to restrict access to your workspace only. Defaults to true if not specified. Note: Authentication with tessl login is required to publish plugins regardless of this setting.

`rules (string or string[], optional)`: Path to a directory of rule files, or an array of specific .md file paths. Rules are always-loaded guidance for agents — the plugin.json equivalent of steering. Omit to use convention discovery, which scans ./rules/ for .md files automatically.

`skills (string or string[], optional)`: Path to a directory containing skill subdirectories, or an array of specific skill directory paths. Each skill directory must contain a SKILL.md file. Skills are reusable capabilities that extend AI coding agents with specialized knowledge, workflows, or tool integrations. Omit to use convention discovery, which scans ./skills/ for subdirectories containing SKILL.md automatically.

`mcpServers (string, optional)`: Pointer to the plugin's bundled `.mcp.json` file at the plugin root. The value is fixed — either `".mcp.json"` or `"./.mcp.json"`. Declaring it is optional and non-gating: the `.mcp.json` file is discovered by convention at the plugin root whether or not the manifest mentions it, so the field only lets authors be explicit. Note that this string field is a *pointer* to the file — not to be confused with the `mcpServers` map *inside* the `.mcp.json` file itself (see below), which lists the actual servers. See [Bundled `.mcp.json`](#bundled-mcp-json) for the file's shape.

`hooks (object, optional)`: Cross-agent (generic) hooks, deterministic commands that run in response to agent events. A map of event name to hook groups, authored once and dispatched to every supported agent. See [Hooks](#hooks) for the shape.

`nativeHooks (object, optional)`: Per-agent (native) hooks, hook entries written in an individual agent's own hook format, keyed by agent id. Use this when a hook needs an agent-specific event or field the generic `hooks` field can't express. See [Hooks](#hooks).

A note on plugin.json validation:

1. `rules`, `skills`, `mcpServers`, `hooks`, and `nativeHooks` are all optional. A plugin with none of these relies entirely on convention discovery. There is no requirement that at least one content type be explicitly declared.

### Bundled `.mcp.json`

A plugin declares MCP (Model Context Protocol) servers by adding a `.mcp.json` file at the plugin root — the same format Claude Code already reads in a repository. This is the file the `mcpServers` pointer field above points at; it is discovered by convention even when the field is omitted. (The pointer field shares the `mcpServers` name with the map *inside* this file, described below, but the two are distinct: the field names the file, the map lists the servers.)

> **Note:** This is a *different* file from the `.mcp.json` Tessl writes into a consuming project (see [MCP configuration files](#mcp-configuration-files) below). The plugin-bundled `.mcp.json` is authored and shipped *inside* the plugin package to declare the servers it provides. The project `.mcp.json` is generated by Tessl in the consuming repository to wire the agent up to Tessl's own `tessl mcp` server. They share a name and format but serve different purposes.

The file has a top-level `mcpServers` map of server name to server config. Each config is one of two transport types, selected by its `type` field:

**stdio** — a server the agent launches as a local subprocess:

* `type` (string, required): `"stdio"`.
* `command` (string, required): The executable to run.
* `args` (string\[], optional): Arguments passed to the command.
* `env` (object, optional): Environment variables (string values) set for the process.

**http** — a server reachable over HTTP:

* `type` (string, required): `"http"`.
* `url` (string, required): The server endpoint. Must be an `http` or `https` URL.
* `headers` (object, optional): HTTP headers (string values) sent with each request.

```json
{
  "mcpServers": {
    "feature-flags": {
      "type": "stdio",
      "command": "node",
      "args": ["./scripts/flags-mcp.js"],
      "env": {
        "FLAGS_ENV": "staging"
      }
    },
    "component-library": {
      "type": "http",
      "url": "https://components.internal.example.com/mcp",
      "headers": {
        "X-Workspace": "myorg"
      }
    }
  }
}
```

#### How bundled servers are installed

When a plugin is installed, Tessl materialises its bundled servers into each supported agent's own native MCP configuration — the file that agent already reads, in the format and location it expects. By default servers are written into the project; installing with `--global` writes them into each agent's user-scope configuration instead.

Tessl reconciles these servers on every install and prunes them when the plugin is uninstalled. Your own hand-added MCP servers are always left untouched. If you hand-edit a server Tessl wrote, it stops recognising the entry as its own and leaves it in place with a warning rather than overwriting it.

### Hooks

Hooks are deterministic commands that run automatically in response to events within an agent harness: running a linter after an edit, validating a spec when a route handler changes, or posting a summary when a session ends. A plugin declares hooks in `.tessl-plugin/plugin.json` using one of **two tiers**, expressed as two separate top-level fields:

* **`hooks`**: the **cross-agent (generic)** tier. You author **one** command against Tessl's generic event schema, and Tessl dispatches it to each agent that supports the event. This is the paved road: use it whenever the hook's logic is agent-independent (a linter, a validator, a notifier) so it works across agents without per-agent authoring. Not every agent implements every event; an event an agent doesn't support is skipped for that agent (see the note below).
* **`nativeHooks`**: the **per-agent (native)** tier. You write hook entries in an individual agent's own hook format, keyed by agent id. This is the escape hatch: use it when a hook needs an agent-specific event or field that the generic schema can't express. Tessl carries these entries through near-verbatim. It does not own the shape, so each agent validates its own entries.

A plugin can use either tier or both.

#### Cross-agent hooks (`hooks`)

The `hooks` field is a map of **event name** to an array of **hook groups**. The supported event names are:

* `PreToolUse`: before the agent runs a tool.
* `PostToolUse`: after a tool call completes.
* `UserPromptSubmit`: when the user submits a prompt.
* `SessionStart`: when a session begins.
* `Stop`: when the agent finishes responding.

> **Note:** Agents vary in which of these events they implement. Gemini, for example, supports only `PreToolUse`, `PostToolUse`, and `SessionStart`. When an agent does not implement a declared event, that event is skipped for that agent (the rest still install), so prefer events supported by the agents you target.

Each hook group has an optional `matcher` and one or more command hooks:

* `matcher` (string, optional): A tool-name filter: a single tool name (`Bash`), a pipe-separated list (`Edit|Write`), or a regex. Omit it to fire the group for every tool call. It only narrows tool-call events (`PreToolUse`, `PostToolUse`); on `UserPromptSubmit`, `SessionStart`, and `Stop` there is no tool name to match, so omit it.
* `hooks` (array, required): One or more command hooks (see below).

A **command hook** has a `type` of `"command"` and takes one of two forms, selected by whether `args` is present:

* **Exec form** (`args` present): `command` is the program to spawn (`argv[0]`, e.g. `bash` or `node`) and `args` are its arguments. The program is spawned directly, with **no shell**, so pipes, globs, and `&&` are *not* interpreted.
* **Shell form** (`args` absent): `command` is a full command string run via `sh -c`, so shell features like pipes, globs, and `&&` work.

Command hook fields:

* `type` (string, required): Always `"command"`.
* `command` (string, required): The program (exec form) or the shell command string (shell form).
* `args` (string\[], optional): The program's arguments. Its presence selects exec form; its absence selects shell form. `[]` is a valid exec form (a program with no arguments).
* `env` (object, optional): Environment variables for the hook. A value can be a static string, or `{ "fromEnv": "VAR" }` to forward the named variable from the agent's shell environment at hook-fire time.

To reference a file bundled inside your plugin, use the `${TESSL_PLUGIN_DIR}` token: it is substituted across `command` and `args` with the plugin's installed location. A **bare relative path is not anchored to the plugin**; it resolves against the event's working directory (the project root) at spawn time, like any argument. In shell form, quote the token (`"${TESSL_PLUGIN_DIR}/lint.sh"`) so paths with spaces survive.

> **Note:** Shell form runs under POSIX `sh -c` (on Windows this relies on git-bash). PowerShell and `cmd.exe` are not used.

```json
{
  "name": "engteam/lint-on-edit",
  "version": "1.0.0",
  "description": "Run the workspace linter after the agent edits a file",
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "bash",
            "args": ["${TESSL_PLUGIN_DIR}/scripts/lint.sh"]
          }
        ]
      }
    ]
  }
}
```

#### Per-agent hooks (`nativeHooks`)

The `nativeHooks` field is a map keyed by **agent id** (for example `claude-code`). Each agent's value is that agent's own native hook config, keyed by the agent's own event names, which Tessl passes through near-verbatim. Because Tessl does not own this shape, refer to the target agent's own hook documentation for the events and fields it accepts.

`${TESSL_PLUGIN_DIR}` works here too, but is resolved at **install time** (not hook-fire time) into a path the agent will find when the hook fires: anchored to the agent's project-directory variable or a repo-relative path in project scope, and an absolute path in global scope. Resolved paths that would escape the plugin directory are rejected.

```json
{
  "name": "engteam/native-hook-example",
  "version": "1.0.0",
  "description": "A hook using an agent-specific event",
  "nativeHooks": {
    "claude-code": {
      "PreToolUse": [
        {
          "matcher": "Bash",
          "hooks": [
            {
              "type": "command",
              "command": "${TESSL_PLUGIN_DIR}/scripts/guard.sh"
            }
          ]
        }
      ]
    }
  }
}
```

#### How hooks are installed

When a plugin is installed, Tessl materialises its hooks into each supported agent's own native hook configuration: the file that agent already reads.

* **Cross-agent hooks** are installed as a `tessl hook run` dispatch entry: the agent invokes Tessl when the event fires, and Tessl runs your declared command, translating the agent's native event payload to and from the generic schema.
* **Per-agent hooks** are written into the agent's config as your own entries (after `${TESSL_PLUGIN_DIR}` is resolved).

By default hooks are written into the project; installing with `--global` writes them into each agent's user-scope configuration instead. Agents without a user-scope hook location are the exception: OpenHands, for instance, has no global hook config, so `--global` skips it with a warning rather than writing an unread file. Tessl tracks the entries it writes and prunes them when the plugin is uninstalled, leaving your hand-added hooks untouched.

## Project configuration

### tessl.json

The `tessl.json` file is the manifest for your repository's plugin dependencies. It specifies which plugins are installed and their versions:

```json
{
  "name": "my-project",
  "dependencies": {
    "workspace/plugin-name": {
      "version": "1.0.0"
    }
  }
}
```

This file is created automatically when you run `tessl init`, `tessl install`, or `tessl project create`. Tessl manages the dependencies in this file as you install or uninstall plugins.

### Verify block

The optional `verify` block in `tessl.json` configures `tessl change verify`. It groups the standalone verifier JSON files in your repository, points each group at the files it should check, and sets the severity of its findings. The block is the runtime source of truth for verification. `tessl change verify` reads it plus the verifier files its groups reference. See [Verifiers overview](/codifying-and-enforcing-your-skill-standards/verifiers-overview) for when to use a verifier and [Verify agent changes in CI](/codifying-and-enforcing-your-skill-standards/verify-agent-changes-in-ci) for a complete setup walkthrough.

```json
{
  "name": "my-project",
  "dependencies": {
    "workspace/plugin-name": {
      "version": "1.0.0"
    }
  },
  "verify": {
    "groups": {
      "core": {
        "path": "verifiers/*.json",
        "level": "error",
        "include": ["src/**/*.ts"],
        "exclude": ["**/*.test.ts"]
      }
    }
  }
}
```

#### `verify`

* `groups` (object, optional): Named groups of verifiers. Keys are arbitrary group names; values are group entries (see below).

#### Group entry

Each entry under `verify.groups` accepts:

* `path` (string or string\[], required): Path or glob (or list of them) to the verifier JSON files this group runs.
* `level` (string, required): Severity for the group's findings — one of `info`, `warn`, or `error`. `error` makes a failing check blocking in CI; `warn` and `info` are advisory.
* `include` (string\[], optional): Globs of files this group targets. Files added later that match are covered automatically.
* `exclude` (string\[], optional): Globs of files to drop from the group's targets.
* `content` (object, optional): Content-based file filtering (see [Content predicate](#content-predicate)).
* `enabled` (boolean, optional): Set to `false` to skip the whole group.
* `verifiers` (object, optional): Per-verifier overrides, keyed by verifier file name. Each override accepts `include`, `exclude`, `content`, `enabled`, and `level` — letting the same verifier be advisory (`warn`) in one project and blocking (`error`) in another without editing the verifier JSON.

#### Content predicate

The `content` field (on a group or a per-verifier override) keeps or drops files by what they contain, before the judge runs:

* `containsAny` (string\[]): Keep files containing at least one of these literal strings.
* `containsAll` (string\[]): Keep files containing every one of these literal strings.
* `regexAny` (string\[]): Keep files matching at least one JavaScript regular-expression pattern.
* `regexAll` (string\[]): Keep files matching every JavaScript regular-expression pattern.
* `notContainsAny` (string\[]): Drop files containing any of these literal strings.
* `notRegexAny` (string\[]): Drop files matching any of these JavaScript regular-expression patterns.

#### Verifier JSON files

The files referenced by a group's `path` describe the invariant to check. A verifier file accepts:

* `name` (string, required): Short human label for CLI output. The filename remains the stable identifier.
* `instruction` (string, required): What the judge should check for.
* `relevant_when` (string, required): When the verifier applies.
* `context` (string, required): Background the judge needs to score the file.
* `scope` (string, required): The unit the judge evaluates — `file`, `paths`, or `diff`.
* `scope_reason` (string, required): Why that scope is the right target shape.
* `checklist` (array, required): One or more check items, each with a `name`, a `rule`, and an optional `relevant_when`.
* `references` (array, optional): Informational provenance for where the rule came from.
* `project_when` (object, optional): A precondition evaluated once per run (no LLM call); when it is false the verifier is skipped entirely. Supports `package_json_has_dep`, `file_exists`, `glob_matches`, and the combinators `any_of`, `all_of`, and `not`.
* `covered_by` (array, optional): Links to existing lint rules or tests that already enforce the rule. When every link is fresh the judge is skipped and the verifier reports as `covered`; when any link is stale the judge runs anyway and the staleness is surfaced.

```json
{
  "name": "User-facing errors include a next step",
  "instruction": "When code returns a user-facing error, it should tell the user what to do next.",
  "relevant_when": "TypeScript code that constructs a UserFacingError or prints an error for a CLI command.",
  "context": "CLI errors should be actionable for users running commands in a terminal.",
  "scope": "file",
  "scope_reason": "Each command file can be checked independently for its own error handling.",
  "checklist": [
    {
      "name": "actionable-error",
      "rule": "Each user-facing error includes either the command to run next, the flag to change, or the file to inspect."
    }
  ]
}
```

Validate a verifier file with `tessl change verify lint <file>`, and preview which files a group matches with `tessl change verify --dry-run --all --show-files` before running judge calls. Verifiers are separate from [Tessl Code Review](/tutorials/setting-up-agentic-code-review), so add their workflow only when the repository needs an invariant check alongside pull request review.

### Tessl project link

`tessl.json` stores the Tessl configuration for this repository, including its project link.

A Tessl project gives Tessl a stable place to attach eval runs and other repository-connected data, so your results stay tied to your codebase over time.

For example:

```diff
{
-   "name": "my-project",
+   "name": "my-workspace/my-project",
    "dependencies": {
      "workspace/plugin-name": {
        "version": "1.0.0"
    }
  }
}
```

If the local project link is missing or no longer correct, use `tessl project link` or `tessl project repair`. See [Manage projects from the CLI](/projects/manage-projects-from-the-cli).

### Project Mode (Managed vs Vendored)

The `mode` field in `tessl.json` controls how plugin content is managed in your repository. You can choose between two modes depending on your workflow and requirements:

#### Managed mode

**Default for existing repositories** - plugin contents are gitignored like `node_modules`

```json
{
  "name": "my-project",
  "mode": "managed",
  "dependencies": {
    "workspace/plugin-name": {
      "version": "1.0.0"
    }
  }
}
```

**Behavior:**

* plugin contents in `.tessl/plugins/` are automatically added to `.gitignore`
* plugins are reinstalled from the registry based on `tessl.json`
* Works like package managers (npm, pip) - dependencies not committed
* Keeps repository clean and small
* Team members run `tessl install` after cloning the repository

**Best for:**

* Repositories with frequent plugin updates
* Teams who prefer lighter repositories
* Standard development workflows with internet access

#### Vendored mode

**Default for new repositories** - plugin contents are committed to your repository

```json
{
  "name": "my-project",
  "mode": "vendored",
  "dependencies": {
    "workspace/plugin-name": {
      "version": "1.0.0"
    }
  }
}
```

**Behavior:**

* Plugin contents in `.tessl/plugins/` are committed to version control
* Exact plugin versions are checked into the repository
* Works offline without registry access
* Team members get plugins automatically when cloning
* Ensures reproducible builds in all environments

**Best for:**

* Air-gapped or restricted network environments
* Repositories requiring complete offline capability
* Ensuring exact reproducibility without external dependencies
* Compliance requirements for vendoring all dependencies

#### Switching modes

You can change modes at any time by updating the `mode` field in `tessl.json`:

```json
{
  "name": "my-project",
  "mode": "vendored",  // Change to "managed" or "vendored"
  "dependencies": {
    "workspace/plugin-name": {
      "version": "1.0.0"
    }
  }
}
```

After changing the mode:

* Tessl will automatically update `.gitignore` accordingly
* In **managed mode**, `.tessl/plugins/` will be added to `.gitignore`
* In **vendored mode**, `.tessl/plugins/` will be removed from `.gitignore`
* Commit the changes to apply the new mode for your team

#### Default behavior

* **New projects** (running `tessl init` in a fresh project): Defaults to **vendored mode**
* **Existing projects** (running `tessl init` in a project with existing plugins): Defaults to **managed mode**
* You can explicitly set the mode in `tessl.json` to override the default

### .tessl directory

The `.tessl` directory contains Tessl's configuration and cached data:

```
.tessl/
|-- .gitignore                  # Ignores plugins/ and RULES.md (in managed mode)
|-- plugins/                      # Downloaded plugins
|   `-- workspace/
|       `-- plugin-name/
|           |-- .tessl-plugin/plugin.json
|           |-- evals
|           |-- rules/           # Rule files
|           `-- skills/          # Skill files
|               `-- skill-name/
|                   `-- SKILL.md
`-- RULES.md                    # Generated rules for agents (not committed to git)
```

The `.tessl/.gitignore` file is automatically managed based on your project mode:

* **Managed mode**: The `plugins/` directory and `RULES.md` are added to `.gitignore` (not committed to version control)
* **Vendored mode**: The `plugins/` directory is removed from `.gitignore` (committed to version control), but `RULES.md` remains ignored as it's generated from plugin content

### Plugin directory structure

Below we see a plugin structure as it's being authored:

<pre><code><strong>&#x3C;your-plugin/skill-name>/
</strong><strong>├── .tessl-plugin/
</strong>│     └── plugin.json
└── skills/&#x3C;your-skill-name>/SKILL.md
├── evals/
│     └── scenario-1/
│          ├── task.md
│          ├── criteria.json
│          └── scenario.json        # optional: fixtures, includes, setup scripts
│     └── scenario-2/
</code></pre>

* evals/ is only present if [scenarios](/improving-your-skills/overview-improving-skills-and-plugins) were generated
* skills/\<your-skill-name>/SKILL.md is suggested structure, otherwise plugin.json will need a specific pointer

### Agent rule files

Tessl creates and updates rule files for AI coding agents to help them understand your repository context and installed plugins. The location and format of these files varies by agent:

* **Cursor**:
  * `.cursor/rules/tessl__*.mdc` - plugin-specific rules (auto-generated, not committed to git)
  * `.cursor/rules/tessl_context.mdc` - Instructions for gathering context from Tessl MCP
* **Claude Code**:
  * `CLAUDE.md` - Context file with instructions for gathering context from Tessl MCP
  * `.tessl/RULES.md` - Consolidated rules from all installed plugins
  * `AGENTS.md` - If this file exists, Tessl adds a reference to `.tessl/RULES.md`

These files are created when you run `tessl init --agent <agent-name>` or when Tessl auto-detects an agent in your repository.

### MCP configuration files

When configuring AI agents, Tessl adds MCP (Model Context Protocol) server configuration to connect the agent to Tessl's MCP server. The location varies by agent:

* **Cursor**: `.cursor/mcp.json`
* **Claude Code**: `.mcp.json` in the repository root

These files configure the agent to run `tessl mcp start` as an MCP server, enabling the agent to access Tessl's tools and context.

This is the agent host configuration Tessl writes into a consuming project. It is distinct from the [bundled `.mcp.json`](#bundled-mcp-json) a plugin author ships inside their package to declare the servers the plugin provides.

### AGENTS.md

The `AGENTS.md` file provides repository context to AI coding agents. This file is similar to `CLAUDE.md` and other agent context files used by various AI coding assistants.

**Note**: Tessl does not create or manage `AGENTS.md` directly. However, if `AGENTS.md` exists in your repository, Tessl will automatically add a reference to `.tessl/RULES.md` when you configure an agent with `tessl init`. This allows your agent to access plugin-specific guidance and context.

If you're using `AGENTS.md` in your repository:

* Add your own repository context, coding conventions, and patterns
* Tessl will append a section linking to `.tessl/RULES.md` (marked with `<!-- tessl-managed -->`)
* The content is used by AI agents during code generation

### .tesslignore

Exclude files from plugin validation and packing.

**Purpose**

The `.tesslignore` file allows you to exclude files from orphaned file warnings and prevent them from being included in published plugin packages.

**Location**

Place a `.tesslignore` file in the root of your plugin directory (same level as `.tessl-plugin/plugin.json`).

**Syntax**

Uses gitignore-style patterns:

```gitignore
# Comments start with #

# Exact file names
notes.md

# Glob patterns
*.draft.md

# Directory patterns (trailing slash)
drafts/

# Recursive patterns
**/internal/**

# Path-specific patterns
docs/internal.md

# Negation patterns (exclude from ignore)
!important.md
```

**Default ignored files**

These files are always ignored, even without a `.tesslignore` file:

* `AGENTS.md`
* `CLAUDE.md`
* `GEMINI.md`

**Important rules**

* **Links to ignored files cause errors**: If your docs link to a file that's in `.tesslignore`, validation will fail. This prevents broken links in published plugins.
* **Manifest files can't be ignored**: You cannot put `docs`, `rules`, or `skills` entrypoints in `.tesslignore`.

**Example `.tesslignore`**

```gitignore
# Development notes
notes.md
TODO.md

# Draft files
*.draft.md

# Local testing
test-data/

# Keep this one even though it matches *.draft.md
!important.draft.md
```

## Code Review configuration

### .tessl-code-review\.yml

A repository-owned YAML profile routes Tessl Code Review lenses to parts of a change and sets the review's finding policy. How the file is discovered depends on how the review runs:

* **The** [**Tessl Review GitHub App**](/tutorials/setting-up-agentic-code-review) **discovers it automatically.** The App decides presence on the repository's default branch: it looks for `.tessl-code-review.yml` at the root of the default branch, and if the file is not there, the review runs the built-in `standard` profile and nothing in the file matters, including a copy added in the pull request under review. Once the file exists on the default branch, the App reads its content at the pull request's head commit, so an edit to the file takes effect on the pull request that makes it. Because the content comes from the pull request head, the `CODEOWNERS` warning below applies to the App too. Under the App, the file name is fixed: only `.tessl-code-review.yml` at the repository root is discovered.
* **The CLI and the unsupported** [**GitHub Action**](/tutorials/setting-up-agentic-code-review/code-review-action) **do not discover it.** They run it only when a command or the Action names its path. Pass it to the CLI with `--profile ./.tessl-code-review.yml`, or to the Action with `profile: ./.tessl-code-review.yml`. For the CLI and the Action the name is a convention, and any `.yml` or `.yaml` path works.

The file is a block-style YAML mapping. It must set `schemaVersion: 1` and a non-empty, ordered `lenses` list; the rest is optional.

{% hint style="warning" %}
The [Tessl Review GitHub App](/tutorials/setting-up-agentic-code-review) and the [GitHub Action](/tutorials/setting-up-agentic-code-review/code-review-action) both read this file's content from the pull request head, so a contributor who can push to the pull request can also change its lens routing or `requestChangesAt` in the same change and weaken the review. A `ref` pointing at a repository-local `SKILL.md` reads that file's instructions from the pull request head too, so protecting only this file leaves the referenced local lenses reachable by the change author. To require separate approval for review-policy changes, protect this file **and every repository-local `SKILL.md` its lenses reference** with `CODEOWNERS`, then enable required code-owner approval in the repository's branch protection or ruleset. A lens pinned to a registry reference is fixed by its version and needs no such protection.
{% endhint %}

```yaml
schemaVersion: 1
effort: low
reviewMode: standard
requestChangesAt: major
ignore:
  - '**/*.generated.ts'
  - vendor/**
lenses:
  - ref: ./review-lenses/backend/SKILL.md
    effort: high
    globs:
      - apps/backend/**
      - '!apps/backend/**/*.generated.ts'
  - ref: tessl/code-review@0.2.0#review-security-and-privacy
    globs:
      - infra/**
  - ref: ./review-lenses/general/SKILL.md
```

#### Top-level keys

* `schemaVersion` (required): must be `1`.
* `lenses` (required): an ordered, non-empty list of lenses. See [Lens entries](#lens-entries). At most eight lenses run; lenses whose `globs` match no changed file do not run and do not count toward that limit.
* `effort` (optional): how hard every lens thinks: `low`, `medium`, `high`, or `adaptive`. `adaptive` uses `medium` for a full review and `low` for a re-review. A single lens can override it with a fixed level. See [Effort](#effort).
* `reviewMode` (optional): blocking strength, one of `strict`, `standard`, or `relaxed`. Omit it for `standard`. `strict` blocks every eligible finding, `standard` requires Medium likelihood strength, and `relaxed` requires High likelihood strength. Low-confidence fresh findings are omitted in every mode, and pre-existing findings never block.
* `requestChangesAt` (optional): the minimum consequence severity eligible to block, one of `critical`, `major`, `minor`, or `nit`. Omit it for `major`. Each consequence level above this baseline adds one blocking-strength point.
* `ignore` (optional): repository-relative POSIX globs excluded from every lens. See [Ignore](#ignore).

#### Lens entries

Each entry in `lenses` is a mapping:

* `ref` (required): a local `SKILL.md` path or skill directory, an installed skill name, or a registry reference `workspace/plugin[@version]#skill-name`. Local paths are resolved relative to the profile and must stay inside the repository. Pin every registry reference so the review does not change when a plugin publishes a new version.
* `globs` (optional): repository-relative POSIX patterns selecting the files this lens sees. A lens without `globs` applies to the whole change.
* `effort` (optional): this lens's own effort, a fixed `low`, `medium`, or `high`, overriding the file-level `effort`. A single lens cannot take `adaptive`; only the file-level `effort` and the CLI/Action override accept it.

Lens order is preserved. Each lens receives only its selected files. If no lens matches the change, the review returns a skipped result without implying approval.

#### Globs

Globs are case-sensitive and repository-relative. Each list must contain at least one positive pattern. Patterns are evaluated in order: a positive pattern includes a path, a pattern prefixed with `!` excludes it, and a later positive pattern can include it again. For a renamed file, a lens is selected when either the old or new path matches.

#### Ignore

The top-level `ignore` list bounds which paths any lens reviews, so generated code, lockfiles, snapshots, or vendored directories are excluded once instead of negated in every lens. Each pattern is positive (it excludes what it matches), so a leading `!` is rejected, and a bare `**` that would exclude everything is rejected. At most 64 patterns are allowed. `ignore` overrides anything a lens selected, and a lens's own `globs` still narrow it further. Because a file is selected when either rename endpoint matches, a file renamed into an ignored path is still reviewed. `ignore` bounds which paths a lens reviews; it does not keep a path's contents out of a model's context.

#### Effort

`effort` at the file level applies to every lens; on a single lens it makes that one think harder or less hard than the rest. A lens without its own effort uses the file's; with neither, the model applies its own default. The CLI `--effort` flag and the Action `effort` input apply to every lens and take precedence over both.

The file-level `effort`, `--effort`, and the Action `effort` input each accept `low`, `medium`, `high`, or `adaptive`. `adaptive` uses `medium` for a full review and `low` for a re-review, which is how the built-in `standard` profile scales by round. A single lens's own `effort` must be a fixed `low`, `medium`, or `high`; `adaptive` is rejected there, because the round-dependent policy is set once for the whole review rather than per lens.

For the complete behavior and how the file interacts with `--skill`, see the [`tessl code review` CLI reference](/reference/cli-commands#yaml-file-profiles). For routing worked into a full setup, see [Configure reviews with `.tessl-code-review.yml`](/tutorials/setting-up-agentic-code-review#configure-reviews-with-tessl-code-review-yml).

## Eval scenario configuration

### scenario.json

`scenario.json` lives next to `task.md` and `criteria.json` in a scenario directory. It declares what the platform sets up in the working directory before an agent solves the task — fixtures to install, local files to copy in, and setup scripts to run.

`scenario.json` is **optional**. With nothing but `task.md` and `criteria.json`, conventional defaults take over (see [Conventional defaults](#conventional-defaults)).

```json
{
  "description": "Add a CSV import endpoint",
  "fixtures": {
    "codebase": {
      "type": "commit",
      "repoUrl": "https://github.com/org/repo.git",
      "ref": "abc1234",
      "exclude": ["*.mdc", "*.md", ".tessl/"]
    }
  },
  "include": ["./resources"],
  "setup": ["./seed-db.sh"]
}
```

#### Fields

* `description` (string, optional): Human-readable summary of the scenario.
* `fixtures` (object, optional): Named external content the platform installs into the working directory. Keys are arbitrary names; values are fixture objects (see [Fixture types](#fixture-types)). Order-independent.
* `include` (string\[], optional): Paths inside the scenario directory copied into the working directory at the **same relative path**. Each entry must be a directory.
* `setup` (string\[], optional): Paths to scripts inside the scenario directory, run **in order** after fixtures and includes are installed.

#### Fixture types

Two fixture types are supported in `scenario.json`:

`commit` — snapshot of a git repo at a specific ref.

```json
{
  "type": "commit",
  "repoUrl": "https://github.com/org/repo.git",
  "ref": "abc1234",
  "installPath": ".",
  "exclude": ["*.mdc"]
}
```

* `repoUrl` (string, required): HTTPS clone URL.
* `ref` (string, required): Commit SHA or any ref resolvable in the remote.
* `installPath` (string, optional, default `.`): Write destination, confined to the working directory.
* `exclude` (string\[], optional): Glob patterns of paths to exclude from the snapshot.

`directory` — content from a local path relative to the scenario.

```json
{
  "type": "directory",
  "path": "./fixtures/seed-data",
  "installPath": "data",
  "include": ["src/**/*.ts"],
  "exclude": ["**/*.test.ts"]
}
```

* `path` (string, required): Read source, resolved relative to the scenario directory but not confined to it; it may resolve to a location outside the scenario directory, including parent traversal.
* `installPath` (string, required): Write destination, confined to the working directory.
* `include` (string\[], optional): Glob patterns of files to keep from the directory. When omitted, the whole directory is installed; when present, only matching files are kept. Applied before `exclude`.
* `exclude` (string\[], optional): Glob patterns of files to remove from the directory. Applied after `include`.

#### Conventional defaults

Without explicit configuration, two on-disk conventions take effect:

* If a `resources/` directory sits alongside `task.md`, it's automatically copied into the working directory at `resources/`. Equivalent to `"include": ["./resources"]`.
* If a `setup.sh` file sits alongside `task.md`, it's automatically run after fixtures and includes install. Equivalent to `"setup": ["./setup.sh"]`.

A scenario directory containing only `task.md`, `criteria.json`, `resources/`, and `setup.sh` therefore needs no `scenario.json` at all.

#### Working directory assembly order

1. Install fixtures (all entries, order-independent).
2. Copy includes (paths preserved).
3. Run setup scripts in `setup` order, with auto-detected `setup.sh` last.

#### Legacy format

A singular `fixture` object (without the `s`) at the top level is read as a `commit` fixture named `codebase` for backward compatibility with scenarios generated before the `fixtures` plural form. Prefer the plural form for new scenarios.

## User preferences

Tessl stores user preferences globally to customize your experience. You can view and modify these preferences using the `tessl config` commands.

### Available preferences

* `shareUsageData` - Whether to share telemetry and usage data with Tessl (defaults to `true`)
* `agents` - Which agents to configure on `tessl init` (default to auto-detect when empty); see supported agents in `tessl init --help` .

### Managing preferences

View all current preferences:

```bash
tessl config get
```

View a specific preference:

```bash
tessl config get shareUsageData
```

Set a preference:

```bash
tessl config set shareUsageData false
```

### Opting out of telemetry

To opt out of sharing telemetry and usage data:

```bash
tessl config set shareUsageData false
```

For more information about data collection, see [Sharing Usage Data](/legal/sharing-usage-data).


# Skill schemas

How a launchable skill declares typed input and output JSON Schemas, how \`$ref\`s are resolved, and the structured result a cloud launch returns.

A launchable skill can declare typed **input** and **output** contracts in its `SKILL.md` frontmatter. When you run the skill with `tessl launch skill --cloud`, Tessl validates the launcher-supplied inputs against the declared input schema before the skill runs, and validates the recorded result against the declared output schema when it finishes — so a launch either conforms to the contract or fails fast with a field-level diagnostic.

This page covers authoring the `schemas` block, the `$ref` forms it accepts, how those refs are resolved, and the structured `result` a launch returns.

## Authoring the `schemas` block

Declare schemas under a single top-level `schemas` key in the skill's `SKILL.md` frontmatter. It has two optional slots, `inputs` and `output`:

```yaml
---
name: greet
description: Greet someone by name.
schemas:
  inputs:
    $ref: ./schemas/inputs.schema.json
  output:
    $ref: ./schemas/output.schema.json
---
```

Each slot is either an **inline JSON Schema** or a single **`$ref`** reference. Both slots are optional and independent — declare one, both, or neither:

* **`schemas.inputs`** — validated against the launcher-supplied inputs (the `INPUTS` object, keyed by the skill's own placeholder names) before the skill runs. A missing required input fails the launch during preparation rather than being discovered mid-run.
* **`schemas.output`** — describes the structured payload the skill produces. It is composed into the returned result (see [The returned result](#the-returned-result)).

A slot may be written inline instead of via a `$ref`:

```yaml
schemas:
  output:
    type: object
    properties:
      message: { type: string }
    required: [message]
    additionalProperties: false
```

For a complete, copyable example — a `greet` skill with both slots and a bundled `./schemas/` directory — see the [quickstart-typed-skill](https://github.com/tesslio/monorepo/tree/main/apps/docs/tiles/quickstart-typed-skill/README.md) tile.

## `$ref` forms

A slot's `$ref` must be a non-empty string and the **sole** key of the slot (you cannot mix `$ref` with other keys). Three forms are accepted:

| Form       | Example                                     | Resolved from                                                                    |
| ---------- | ------------------------------------------- | -------------------------------------------------------------------------------- |
| **inline** | `{ type: object, ... }` (no `$ref`)         | Used as-is; nothing is fetched.                                                  |
| **local**  | `{ $ref: ./schemas/output.schema.json }`    | A file read relative to the `SKILL.md` directory. Must start with `./` or `../`. |
| **remote** | `{ $ref: https://example.com/schema.json }` | Fetched over HTTPS at launch preparation time.                                   |

Anything else (a bare name, an `http://` URL, a fragment-only `#/...` pointer) is rejected.

### Local `$ref`s are contained to the skill bundle

A local `$ref` is resolved relative to the directory holding the `SKILL.md`, but it is **contained** to the skill's bundle: a ref may point at a sibling or a shared file elsewhere in the same bundle (`../shared/output.schema.json`), but one that escapes the bundle directory — lexically or through a symlink — is rejected. This lets skills that genuinely share a contract stay DRY by pointing a local `$ref` at one shared file, without a ref reaching outside the bundle.

### Remote `$ref`s are fetched safely

A remote `$ref` is fetched only at launch **preparation** time (never during plain validation), over a guarded HTTPS client:

* **HTTPS only**, with no embedded credentials.
* **No private or internal targets** — `localhost`, private/reserved IP ranges, and link-local / unique-local IPv6 are refused. The host is re-resolved and pinned to the validated address at connection time, so a DNS-rebinding trick cannot swap in an internal address after the check.
* **Bounded** — a 10-second timeout and a 1 MiB body cap; HTTP redirects are refused outright.

The fetched body must be valid JSON describing a JSON Schema object.

## Resolution precedence

When a launch prepares a skill, each declared slot is resolved into a fully inline JSON Schema:

1. **Read** the `schemas` block from the skill's `SKILL.md` frontmatter. A skill with no `schemas` block declares no contract — its inputs are not validated and its output is left unconstrained.
2. **Resolve** each slot by its form: an inline schema is used as-is; a local `$ref` is read from the bundle; a remote `$ref` is fetched.
3. **Inline** the resolved schema. Resolution is **single-level** — a `$ref` inside a resolved schema document is not followed. Author self-contained schema files.

If a slot cannot be resolved (a malformed block, an unreadable local file, an unfetchable or unsafe remote URL), the launch fails during preparation with a diagnostic naming the failing slot, rather than proceeding with a partial contract.

## The returned result

Every cloud launch returns an **envelope**, regardless of whether the skill declared any schemas. It is one of four shapes, chosen by its `status`, and each shape carries only the fields listed for it:

```json
{ "status": "succeeded",   "summary": "One sentence on what the skill did.", "output": { } }
{ "status": "no_action",   "summary": "The skill ran but took no action." }
{ "status": "needs_input", "summary": "…", "questions": ["<the exact question>"] }
{ "status": "failed",      "summary": "…", "reason": "<why it could not complete>" }
```

* **`status`** says what the skill did. `succeeded`: it did its work. `no_action`: it ran but made no change. `needs_input`: it asked a blocking question and stopped. `failed`: it tried and could not complete. This is the skill's own account, distinct from the launch run's own status.
* **`summary`** is a short human-readable sentence, required on every shape.
* **`questions`** carries the exact questions the skill asked, on `needs_input`.
* **`reason`** says why the skill could not complete, on `failed`.
* **`output`** is the skill-governed structured payload, on `succeeded` only.

`output` appears when the skill declares a `schemas.output`: that resolved schema becomes the `output` property and `output` becomes **required**, so the skill must produce a payload conforming to its own declared schema and the launch validates it. When the skill declares no output schema, `output` is not permitted at all. `output` belongs entirely to the skill, so there is nothing for it to hold when the skill has not said what it holds. A skill that needs to report structured detail on a non-`succeeded` outcome puts it in `summary` or `reason`.

**`needs_input` is only available to a launch that can route the answer.** A task turn can put a blocking question to a human and resume, so its contract carries that shape; an interactive or scheduled launch cannot, and its contract omits it. On those, record the blocking question as `failed` with the question in `reason`.

### Consuming the result from the CLI

`tessl launch skill --cloud` renders the returned result when a launch completes, and `--json` emits the whole envelope for a script to consume:

```sh
$ tessl launch skill engteam/greet --cloud --repo engteam/my-repo --json
{
  "launchRunId": "…",
  "status": "completed",
  "result": {
    "status": "succeeded",
    "summary": "Greeted Ada.",
    "output": { "message": "Hello, Ada!", "recipient": "Ada" }
  }
}
```

See [`tessl launch skill`](/reference/cli-commands#tessl-launch-skill) for the full flag reference.


# Custom agent setup

For agent setup, perform the following steps:

1. Run `tessl init` from your project root first. Auto-detected agents (Cursor, Claude, Gemini, Copilot, Codex) need no further configuration. You can skip the rest of these steps and start your agent to [validate it's configured](#validating-configuration).
2. MCP configuration
3. Setup rules
4. Accept running Tessl

## Configuring your agent

### MCP setup

This will allow your agent to access context from installed plugins through the Tessl exposed tools.

Typically most agents have a settings file for MCP, often .mcp.json or support for that file. Refer to your agents documentation for the specific formats and files involved for your agent. The values are the following:\
\
**Name**: "tessl"\
\
**Command**: "tessl"

**Args**: "mcp", start"

**Type**, if asked: "stdio"

#### Examples

```json
{
  "mcpServers": {
    "tessl": {
      "command": "tessl",
      "args": [
        "mcp",
        "start"
      ]
    }
  }
}
```

```toml
[mcp_servers.tessl]
type = "stdio"
command = "tessl"
args = ["mcp", "start"]
```

### Rules setup

Rules setup will load the configuration to help your agent use the Tessl MCP effectively plus any other rules from the plugins you installed.

Determine what file you can indicate to the agent to use Tessl and provide steering rules. If there is capability to use AGENTS.md, use or create an `AGENTS.md` file with the following content:

```
# Agent Rules

@.tessl/RULES.md
```

### Enable Tessl

Most agents will prompt on startup to allow the MCP tool to be used, however some may require you to take an extra step and go into the agent's settings to enable it:

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-2f2e1eeece163942531890b4e63298031710fac3%2Fimage%20(147).png?alt=media" alt=""><figcaption></figcaption></figure>

## Validating configuration

Review the agent mcp configuration, often "/mcp" in terminal based agents, and determine if Tessl is enabled/review tools are available.\
\
`/mcp`

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-d5a1fd78c124f1e90cc61a0ae947426e2576e948%2Fimage%20(148).png?alt=media" alt=""><figcaption></figcaption></figure>

Prompt your agent:\
`Use tessl to search for code review skills`

You will see calls to tessl **query\_library\_docs**, and/or tessl **search** , and results if configured properly.

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-cce9176f4b902115b042080bbb365281b03ea111%2Fimage%20(149).png?alt=media" alt=""><figcaption></figcaption></figure>

## Sample configurations for agents not auto-detected

[OpenCode](/reference/custom-agent-setup/opencode)


# OpenCode

## **Tessl Configuration**

Run `tessl init` in your project to create the tessl files and directory structure.

## **MCP Setup**

In your project folder, create a opencode.json with the following content:

```
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "tessl": {
      "type": "local",
      "command": ["tessl", "mcp", "start"],
      "enabled": true
    }
  }
}
```

After opening OpenCode to that project, use **/mcp** and ensure it's enabled and connected

<figure><img src="https://docs.tessl.io/~gitbook/image?url=https%3A%2F%2F1171143958-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXbWzspFdXV0HNujz6gJ9%252Fuploads%252Fn4qpeASLCLCKjNRKJvkR%252Fimage.png%3Falt%3Dmedia%26token%3Dd430663c-3130-4b07-81bc-ab960e0c6c41&#x26;width=768&#x26;dpr=3&#x26;quality=100&#x26;sign=e896a393&#x26;sv=2" alt=""><figcaption></figcaption></figure>

## **Rules setup**

If not already present in your project, create an `AGENTS.md` file with the following content, ensuring the .tessl and RULES.md paths are correct:

```
 @.tessl/RULES.md
```


# OpenClaw

### OpenClaw

OpenClaw is an autonomous AI agent framework with its own workspace and skills system. Tessl integrates with OpenClaw by installing skills directly into an agent workspace — no MCP configuration or rules files are used, to keep config clean.

### How skills work with OpenClaw

Tessl installs skills as symlinks into the `skills/` directory of your OpenClaw agent workspace. OpenClaw reads skills from this directory automatically.

Skills installed by Tessl are prefixed with `tessl__` so they can be identified and managed separately from your other skills, if necessary.

### Installing skills

Navigate to your agent workspace directory before running `tessl install`. A workspace is identified by the presence of a `.openclaw/workspace-state.json` file.

```bash
cd ~/.openclaw/workspace   # or whichever workspace you want to install into
tessl install plugin-name
```

If you run `tessl install` from anywhere other than a workspace root, Tessl will exit with an error and suggest the correct directories to navigate to.

### Multiple agents

If you have more than one OpenClaw agent, run `tessl install` separately from each workspace:

```bash
cd ~/.openclaw/workspaces/agent1
tessl install plugin-name

cd ~/.openclaw/workspaces/agent2
tessl install plugin-name
```

### Installing for coding agents at the same time

If your OpenClaw agent uses coding agents (Claude Code, Codex, etc.), use the `--agent` flag to control which agents receive skills:

```bash
# Install for both OpenClaw and a coding agent
tessl install plugin-name --agent openclaw --agent claude-code

# Install for a coding agent only, skip OpenClaw
tessl install plugin-name --agent claude-code
```

With no `--agent` flag, Tessl auto-detects which agents are present in the workspace directory, and installs for all of them.

### Using tessl init

`tessl init` is not supported in OpenClaw workspaces. If you run it from a workspace root, Tessl will exit with an error and prompt you to run tessl install instead.


# Workspaces

Overview of creating and managing Tessl workspaces

Workspaces within Tessl make managing it easy to create, share and manage your team's context.

## Setting up a new workspace

### create

To create a workspace, run `tessl workspace create <name>`

```
tessl workspace create engteam

> ✔ Created workspace engteam (019b324b-...)
```

### list

Verify that your workspace has been created using `tessl workspace list`.

```
tessl workspace list

> ╔════════════╤══════════════════════════════════════╗
  ║ Name       │ ID                                   ║
  ╟────────────┼──────────────────────────────────────╢
  ║ engteam    │ 019952d9-...                         ║
  ╚════════════╧══════════════════════════════════════╝
```

## How workspaces appear in the Registry

When you publish plugins to a workspace, they appear in the Tessl Registry with URLs that follow this pattern:

```
https://tessl.io/registry/<workspace>/<pluginname>/<version>
```

**Example:** If you publish a plugin with:

* Workspace name: `engteam`
* Plugin name: `api-patterns`
* Version: `1.0.0`

Your plugin will be available at:

```
https://tessl.io/registry/engteam/api-patterns/1.0.0
```

{% hint style="info" %}
The workspace name and plugin name are determined by the `"name"` field in your `.tessl-plugin/plugin.json` file:

* `"name": "engteam/api-patterns"` where `engteam` is the workspace and `api-patterns` is the plugin name
* This is independent of your GitHub repository organization or name
  {% endhint %}

The version in the URL is optional - if omitted, the latest version will be displayed.

## Configuring workspace users

### add-member

Add a new member to a workspace using:

`tessl workspace add-member --workspace <workspace-name> --username <username> --role <role>`

```
tessl workspace add-member --workspace engteam --username macebake --role publisher
```

Check out our supported [roles](/administrators/roles) to see which are appropriate for your users.

### list-members

Verify that your users were added correctly using `tessl workspace list-members <workspace-name>`

```
tessl workspace list-members engteam
```

### remove-members

Remove members using `tessl workspace remove-member --workspace <workspace-name> --username <username>`

```
tessl workspace remove-member --workspace engteam --username macebake
```

## Managing your workspace

### archive

Archive a workspace using `tessl workspace archive --reason <text> [workspace-name]`. The `--reason` flag is required. An archived workspace won't be available to publish to or read from, but can be unarchived later.

```
tessl workspace archive --reason "deprecated" engteam
```

### unarchive

To unarchive an archived workspace, making it active again, use `tessl workspace unarchive <workspace-name>`.

```
tessl workspace unarchive engteam
```

### delete

Delete a workspace entirely using `tessl workspace delete <workspace-name>`. Note: this will delete all content in a workspace, and cannot be undone! Use `archive` above as a safer alternative.

```
tessl workspace delete engteam
```

If a workspace has published a public plugin, deleting it permanently reserves its name: the name cannot be used again, in this organization or any other. To move a workspace between organizations, do not delete it. Email <support@tessl.io> and we can move it for you after a review.


# MCP tools

Overview of Tessl MCP tools for AI coding agents.

The Tessl MCP (Model Context Protocol) server provides tools that AI coding agents can use to search for, install, and work with plugins from the Tessl registry.

## Registry MCP server vs. workspace MCP gateway

Tessl exposes MCP through two distinct entry points — pick the one that matches what you need:

* **Registry MCP server (`tessl mcp start`)**: a local stdio server that gives the agent the tools listed on this page. They cover authentication, registry search, plugin install and update, workspace management, publishing, and skill review. Use it when the agent needs to discover and manage Tessl plugins. This is the server `tessl init` configures for auto-detected agents.
* **Workspace MCP gateway (`tessl mcp proxy <workspace>/<mcp>`)** — a stdio proxy that connects the agent to an external MCP server registered in a Tessl workspace. Use it when several teammates need to share access to an upstream MCP server (such as a third-party integration) without each person re-running its OAuth flow. See [MCP gateway](/reference/mcp-gateway) for how to create and connect to one.

The rest of this page documents the tools exposed by the registry MCP server.

## Server Configuration

The Tessl MCP server can be configured in your AI coding agent's MCP configuration or by running:

```sh
tessl init --agent <agent-name>
```

**Server Details:**

* **Command**: `tessl mcp start`
* **Type**: stdio
* **Name**: Tessl Registry MCP

## Available Tools

The registry MCP server exposes 27 tools:

| Tool                                                | Purpose                                           | Authentication |
| --------------------------------------------------- | ------------------------------------------------- | -------------- |
| [`login`](#login)                                   | Authenticate with Tessl                           | Not required   |
| [`search`](#search)                                 | Search the registry                               | Not required   |
| [`install`](#install)                               | Install plugins into a project                    | Not required   |
| [`uninstall`](#uninstall)                           | Remove plugins from a project                     | Not required   |
| [`outdated`](#outdated)                             | List installed plugins with newer versions        | Not required   |
| [`update`](#update)                                 | Update named plugins                              | Not required   |
| [`status`](#status)                                 | Report authentication and sync status             | Not required   |
| [`skills_available`](#skills_available)             | List installable skills in your workspaces        | Required       |
| [`workspace_list`](#workspace_list)                 | List your workspaces                              | Required       |
| [`workspace_list_members`](#workspace_list_members) | List members of a workspace                       | Required       |
| [`workspace_create`](#workspace_create)             | Create a workspace                                | Required       |
| [`new_plugin`](#new_plugin)                         | Scaffold a new plugin                             | Required       |
| [`tile_publish`](#tile_publish)                     | Publish a plugin directory                        | Required       |
| [`skill_publish`](#skill_publish)                   | Publish a skill directory                         | Required       |
| [`review_run`](#review_run)                         | Start a skill review                              | Required       |
| [`review_fix`](#review_fix)                         | Start a skill-improvement fix run                 | Required       |
| [`review_view`](#review_view)                       | Read review or fix run results                    | Required       |
| [`project_list`](#project_list)                     | List projects in a workspace                      | Required       |
| [`schedule_create`](#schedule_create)               | Create a scheduled run                            | Required       |
| [`schedule_list`](#schedule_list)                   | List scheduled runs                               | Required       |
| [`schedule_view`](#schedule_view)                   | View a scheduled run                              | Required       |
| [`schedule_runs`](#schedule_runs)                   | List launch runs spawned by a schedule            | Required       |
| [`schedule_pause`](#schedule_pause)                 | Pause a scheduled run                             | Required       |
| [`schedule_resume`](#schedule_resume)               | Resume a paused scheduled run                     | Required       |
| [`schedule_update`](#schedule_update)               | Update a scheduled run's cron, timezone, or label | Required       |
| [`schedule_trigger`](#schedule_trigger)             | Trigger a scheduled run immediately               | Required       |
| [`schedule_delete`](#schedule_delete)               | Delete a scheduled run                            | Required       |

Tools marked "Required" start the browser authentication flow automatically if you call them while signed out. Tools marked "Not required" work without being signed in, though `search` and `install` may return a hint suggesting `login` when missing credentials limit their results: they never trigger the sign-in flow themselves.

### login

Authenticate with Tessl to access registry features.

**Authentication**: Not required (this tool provides authentication)

**Parameters**

None

**Notes**

* Attempts to open authentication URL in your browser automatically
* Authentication happens asynchronously - credentials are saved in the background
* After completing authentication once, authenticated tools work automatically
* Triggers automatic dependency sync after successful login

***

### search

Search for plugins in the Tessl registry by name, package URL (PURL), or HTTP URL.

**Authentication**: Not required. If you are signed out, results are limited to public plugins and the response includes a hint suggesting `login`

**Parameters**

* `query` (string, required): Search query - can be a plugin name, package URL, or HTTP URL

**Notes**

* Works with partial names and package URLs (PURLs)
* Supports HTTP URLs from package registries (e.g., npm, PyPI)
* Returns plugin name, version, description, and installation command

**Example**

```json
{
  "query": "svelte"
}
```

***

### install

Install plugins into your project. Can install a specific plugin or sync all missing plugins from tessl.json.

**Authentication**: Not required. If you are signed out, only public plugins can be installed

**Parameters**

* `packageName` (string, optional): plugin name to install (e.g., "workspace/plugin" or "workspace/plugin\@version"). If not provided, installs all missing plugins from tessl.json
* `projectDir` (string, optional): Project directory. Defaults to the tessl project found by walking up from the MCP server's working directory. Required if no project is found. Only used when `packageName` is also supplied, in which case supplying it creates a new tessl project at that path. Without `packageName`, the sync writes nothing to disk if there are no plugins to add or remove
* `skills` (string array, optional): Skill names to install, for a source that exposes selectable skills (a GitHub repository with several skills, or a registry plugin skill). If omitted for such a source, the tool lists the available skills instead of installing

**Notes**

* plugins are installed to `.tessl/plugins/<workspace>/<plugin>/` in your project
* Your project's `tessl.json` is automatically updated with the plugin reference
* If a plugin is already installed and up-to-date, it will be skipped
* If no version is specified, installs the latest version
* `packageName` also accepts a GitHub source (`https://github.com/user/repo` or `github:user/repo`). For a repository with several skills, pass `skills`. The tool lists the available skills if you omit it

**Example: Install specific plugin**

```json
{
  "packageName": "tessl/svelte@5.38.0"
}
```

**Example: Sync all missing plugins**

```json
{}
```

***

### uninstall

Uninstall a plugin from your project.

**Authentication**: Not required

**Parameters**

* `packageName` (string, required): plugin name to uninstall (e.g., "workspace/plugin"). Also accepts a source such as `file:path/to/plugin`
* `skills` (string array, optional): Specific skills to remove instead of the whole plugin. If every skill is removed, the plugin is uninstalled

**Notes**

* Removes the plugin from `tessl.json` and deletes files from `.tessl/plugins/`
* Does not require authentication (local operation only)
* Only affects your local project - doesn't unpublish from the registry

**Example**

```json
{
  "packageName": "tessl/svelte"
}
```

***

### outdated

Check which installed plugins have newer versions available in the registry.

**Authentication**: Not required

**Parameters**

* `projectDir` (string, optional): Project directory to check. Defaults to the tessl project found by walking up from the MCP server's working directory

**Notes**

* Reports only. It does not change any installed plugin
* Use `update` to apply the newer versions it lists

**Example**

```json
{
  "projectDir": "./my-app"
}
```

***

### update

Update named plugins to newer versions.

**Authentication**: Not required

**Parameters**

* `tiles` (string array, required): plugin names to update (e.g., `["engteam/svelte"]`). At least one name is required
* `force` (boolean, optional): Include breaking updates. Defaults to `false`

**Notes**

* There is no "update everything" form. Name the plugins you want changed
* Without `force`, updates that cross a major version are skipped

**Example**

```json
{
  "tiles": ["engteam/svelte"],
  "force": false
}
```

***

### status

Check authentication status and plugin synchronization status for your project.

**Authentication**: Not required

**Parameters**

None

**Notes**

* Returns JSON with authentication status and plugin sync summary
* Shows plugins that are up-to-date, missing, outdated, or invalid
* Useful for checking if you need to run `tessl install` to sync plugins

**Example**

```json
{                                                                     
       "authentication": {
         "authenticated": true,
         "email": "example@tessl.io"
       },
       "manifest": {
         "summary": {
           "upToDate": 1,
           "missing": 0,
           "outdated": 0,
           "invalid": 0
         },
         "tiles": {
           "upToDate": [
             {
               "workspaceName": "tessl",
               "tileName": "pypi-fastapi",
               "manifestVersion": "0.116.0",
               "installedVersion": "0.116.0"
             }
           ],
           "missing": [],
           "outdated": [],
           "invalidPackages": []
         }
       }
     }

```

***

### skills\_available

List the installable skills across the workspaces you are a member of.

**Authentication**: Required

**Parameters**

* `workspace` (string, optional): Filter results to a single workspace by name or ID

**Notes**

* Each result includes the name, version, description, and an installation reference you can pass straight to `install`
* Use it to see what your team already publishes before you scaffold something new
* Without `workspace`, the tool queries every workspace you belong to at once. If any one of those queries fails, the whole call fails. Pass `workspace` to scope the call to a single workspace and make retries cheaper

**Example**

```json
{
  "workspace": "engteam"
}
```

***

### workspace\_list

List all workspaces that you are a member of.

**Authentication**: Required

**Parameters**

None

**Notes**

* Returns the workspace name and ID for each membership
* Use the ID with `workspace_list_members`, or as the workspace argument to the publish tools

**Example**

```json
{}
```

***

### workspace\_list\_members

List all members of a workspace.

**Authentication**: Required

**Parameters**

* `workspace` (string, required): Name or ID of the workspace to list members for

**Example**

```json
{
  "workspace": "engteam"
}
```

***

### workspace\_create

Create a new workspace.

**Authentication**: Required

**Parameters**

* `name` (string, required): Name of the workspace to create (1-100 characters)
* `orgId` (string, optional): ID of the organization to create the workspace in

**Notes**

* Without `orgId`, the workspace is created in your default organization

**Example**

```json
{
  "name": "engteam"
}
```

***

### new\_plugin

Scaffold a new plugin, generating the files you then edit to add the context you want.

**Authentication**: Required

**Parameters**

* `name` (string, required): plugin name in `workspace/plugin-name` format
* `summary` (string, required): Brief description of the plugin
* `path` (string, optional): Directory to create the plugin in. Defaults to `plugins/{plugin-name}`
* `rules` (string or object, optional): Rule name to create. The object form takes `name` and an optional `description` holding the initial rule instructions
* `skill` (object, optional): Skill to include, with `name` and `description`
* `isPrivate` (boolean, optional): Make the plugin private. Defaults to `true`

**Notes**

* You must pass at least one of `skill` or `rules`. A plugin with neither has no content to generate

**Example**

```json
{
  "name": "engteam/svelte",
  "summary": "Svelte 5 conventions for our apps",
  "skill": {
    "name": "svelte-runes",
    "description": "How to use runes for reactive state"
  }
}
```

***

### tile\_publish

Publish a plugin directory to the Tessl registry and return the registry URL.

**Authentication**: Required

**Parameters**

* `path` (string, optional): Path to the plugin directory to publish. Defaults to the current directory
* `workspace` (string, optional): Workspace name override

**Notes**

* Requires an existing manifest in the directory, and never changes its visibility. Use `skill_publish` to publish a skill that has no manifest yet
* Auto-bumps the patch version on collision, so you do not manage versions yourself
* Skips eval publishing. Use the CLI for evals

**Example**

```json
{
  "path": "plugins/svelte"
}
```

***

### skill\_publish

Publish a skill directory to the Tessl registry and return the registry URL.

**Authentication**: Required

**Parameters**

* `path` (string, optional): Path to the skill directory to publish. Defaults to the current directory
* `workspace` (string, optional): Workspace to create the manifest under when the skill has neither a `tile.json` nor a `plugin.json` yet. Ignored when either manifest already exists, since its name decides the destination workspace
* `public` (boolean, optional): Make the skill publicly visible in the registry. Defaults to private

**Notes**

* Auto-bumps the patch version on collision
* Skips eval publishing. Use the CLI for evals

**Example**

```json
{
  "path": "plugins/svelte/skills/svelte-runes",
  "workspace": "engteam"
}
```

***

### review\_run

Start an asynchronous review of a skill.

**Authentication**: Required

**Parameters**

* `path` (string, optional): Path to a skill file or skill directory. Defaults to the current directory
* `workspace` (string, optional): Workspace name or ID. Resolved automatically when not provided
* `kind` (string, optional): `quality` for a skill quality review, or `security` for a security findings review. Defaults to `quality`

**Notes**

* Returns a run ID immediately. Poll `review_view` for the result
* Quality reviews (`kind: "quality"`) consume credits; security reviews (`kind: "security"`) are free. Either way, call it once per user action. `review_view` reports the actual charge once the run finishes

**Example**

```json
{
  "path": "plugins/svelte/skills/svelte-runes",
  "kind": "quality"
}
```

***

### review\_fix

Start an asynchronous skill-improvement fix run.

**Authentication**: Required

**Parameters**

* `path` (string, optional): Path to a skill file or skill directory. Defaults to the current directory
* `workspace` (string, optional): Workspace name or ID. Resolved automatically when not provided
* `threshold` (number, optional): Stop improving once the review score reaches this percentage (0-100). Defaults to the server default
* `maxIterations` (number, optional): Maximum number of improve-then-review iterations (1-10). Defaults to the server default

**Notes**

* Returns a fix run ID immediately, and writes nothing locally
* When the run completes, call `review_view` with `apply: true` and the skill path to write the improved skill back. This is the equivalent of `tessl review fix --yes`
* A fix run consumes credits. Call it once per user action

**Example**

```json
{
  "path": "plugins/svelte/skills/svelte-runes",
  "threshold": 90
}
```

***

### review\_view

Fetch the current status and results for a review run or a fix run.

**Authentication**: Required

**Parameters**

* `runId` (string, required): The run ID returned by `review_run`, or the fix run ID returned by `review_fix`
* `apply` (boolean, optional): Fix runs only. When `true`, writes the completed fix run's improved skill back to the local files. Defaults to `false`, which reports the score and summary and changes nothing on disk
* `path` (string, optional): Fix runs only, paired with `apply`. Path to the local skill file or directory to write the improvement into. Defaults to the current directory. Use the same skill the fix run was started against

**Notes**

* Poll until `status` is `completed`, `failed`, or `cancelled`. These are the only terminal states
* Review and fix runs typically take from tens of seconds to a few minutes. Poll every 5-10 seconds rather than continuously

**Example**

```json
{
  "runId": "run_01j9x2c8",
  "apply": true,
  "path": "plugins/svelte/skills/svelte-runes"
}
```

***

### project\_list

List all projects in a workspace.

**Authentication**: Required

**Parameters**

* `workspace` (string, required): Name or ID of the workspace to list projects for

**Example**

```json
{
  "workspace": "engteam"
}
```

***

### schedule\_create

Create a scheduled run that fires a launch run on a cron. Provide either `fromRun` (to copy an existing run's config) or `skill` + `repo` + `environment` (inline spec).

**Authentication**: Required

**Parameters**

* `cron` (string, required): Cron expression the schedule fires on (5-field standard cron, e.g. `"0 9 * * 1-5"`)
* `timezone` (string, optional): IANA timezone the cron is interpreted in. Defaults to `UTC`
* `fromRun` (string, optional): Launch run ID whose config this schedule copies. Mutually exclusive with all inline launch-spec fields (`skill`, `repo`, `baseBranch`, `cwd`, `agent`, `model`, `environment`, `instructions`)
* `skill` (string, optional): Skill to run, as `workspace/plugin[@version][#skill]` or `file:path/to/plugin[#skill]`. Required without `fromRun`
* `repo` (string, optional): Target repository as `owner/name` or URL. Required without `fromRun`
* `baseBranch` (string, optional): Branch to clone and target the PR against. Defaults to the project or repository default
* `cwd` (string, optional): Subdirectory to run the skill in, relative to the repo root
* `agent` (string, optional): Agent to run the skill with (selects the cloud strategy)
* `model` (string, optional): Model to use for each run (e.g. `claude-opus-4-6`)
* `environment` (string, optional): Name or ID of workspace environment whose variables are made available to each run. Required for inline schedules
* `inputs` (object, optional): Skill input values as a map of string keys to string values (e.g. `{ "KEY": "value" }`). Both keys and values must be strings
* `instructions` (string, optional): Instructions to append to the skill prompt
* `workspace` (string, optional): Workspace name or ID to schedule in. Resolved in order: explicit input, `TESSL_WORKSPACE` env var, workspace linked to the current directory via `tessl init`. Required if none of those apply
* `label` (string, optional): Label to describe this schedule

**Example**

```json
{
  "cron": "0 9 * * 1-5",
  "timezone": "America/New_York",
  "skill": "engteam/daily-checks",
  "repo": "myorg/myrepo",
  "environment": "prod",
  "workspace": "engteam"
}
```

***

### schedule\_list

List scheduled runs. Returns id, status, cron, workspace, and next/last run time for each schedule.

**Authentication**: Required

**Parameters**

* `workspace` (string, optional): Filter by workspace name or ID
* `status` (string, optional): Filter by status: `active` or `paused`
* `limit` (positive integer, optional): Maximum number of schedules to return. Defaults to `20`

**Example**

```json
{
  "workspace": "engteam",
  "status": "active"
}
```

***

### schedule\_view

View config, cron, and next/last run for a scheduled run.

**Authentication**: Required

**Parameters**

* `id` (string, required): Scheduled run ID

**Example**

```json
{
  "id": "018e1d4e-6153-7000-b00f-000000000001"
}
```

***

### schedule\_runs

List the launch runs a scheduled run has spawned, newest first. Returns status, timestamps, and liveness for each run. The result is bounded: the backend returns at most 100 runs and there is no cursor for older pages.

**Authentication**: Required

**Parameters**

* `id` (string, required): Scheduled run ID (UUID)
* `limit` (positive integer, optional): Maximum number of launch runs to return. Defaults to `20`, maximum `100`

**Example**

```json
{
  "id": "018e1d4e-6153-7000-b00f-000000000001",
  "limit": 10
}
```

***

### schedule\_pause

Pause a scheduled run so it stops firing. Use `schedule_resume` to re-enable it.

**Authentication**: Required

**Parameters**

* `id` (string, required): Scheduled run ID

**Example**

```json
{
  "id": "018e1d4e-6153-7000-b00f-000000000001"
}
```

***

### schedule\_resume

Resume a paused scheduled run so it fires again on its cron.

**Authentication**: Required

**Parameters**

* `id` (string, required): Scheduled run ID

**Example**

```json
{
  "id": "018e1d4e-6153-7000-b00f-000000000001"
}
```

***

### schedule\_update

Update a scheduled run's cron, timezone, or label. At least one of `cron`, `timezone`, or `label` is required.

**Authentication**: Required

**Parameters**

* `id` (string, required): Scheduled run ID
* `cron` (string, optional): New cron expression (5-field standard cron)
* `timezone` (string, optional): New IANA timezone the cron is interpreted in (e.g. `UTC`, `America/New_York`)
* `label` (string, optional): New label to describe this schedule

**Example**

```json
{
  "id": "018e1d4e-6153-7000-b00f-000000000001",
  "cron": "0 10 * * 1-5",
  "timezone": "Europe/London"
}
```

***

### schedule\_trigger

Fire a scheduled run immediately, out of band (independent of its cron). Returns the launch run ID.

**Authentication**: Required

**Parameters**

* `id` (string, required): Scheduled run ID

**Example**

```json
{
  "id": "018e1d4e-6153-7000-b00f-000000000001"
}
```

***

### schedule\_delete

Permanently delete a scheduled run. This cannot be undone.

**Authentication**: Required

**Parameters**

* `id` (string, required): Scheduled run ID

**Example**

```json
{
  "id": "018e1d4e-6153-7000-b00f-000000000001"
}
```


# MCP gateway

Create a workspace MCP gateway in Tessl and connect to it from any agent with \`tessl mcp proxy\`.

A **workspace MCP gateway** lets Tessl broker connections to an external [Model Context Protocol](https://modelcontextprotocol.io) server for a workspace. You register the upstream server once in the workspace's settings, authorize it (via OAuth where required), and then any workspace member can connect to it through a single stable address: `tessl mcp proxy <workspace>/<mcp>`.

A gateway is useful when:

* You want a shared, authorized MCP server that every workspace member can use without each person re-running an OAuth flow.
* You want to keep upstream credentials (client secret, tokens) inside Tessl rather than copy them into individual agent configurations.
* You want one identifier (`<workspace>/<mcp>`) to reference the same MCP server from a coding agent, a plugin, or a CI environment.

The gateway sits between the agent's MCP client and the upstream MCP server: the agent talks to `tessl mcp proxy` over stdio, the CLI relays JSON-RPC messages to the gateway over HTTP with Tessl auth headers, and the gateway relays them on to the upstream MCP server using the credentials you stored when you registered it.

## Create a gateway in a workspace

Workspace owners and managers can register MCP servers. Members without `manage_mcp_servers` permission see the page in read-only mode.

1. Open the workspace in the Tessl web app and go to **Settings → Integrations → MCP servers**.
2. Click **Add MCP server**.
3. Fill in the form:
   * **Server name** — used as the slug in the gateway path. Use only alphanumerics, hyphens, and underscores (max 63 characters). Example: `linear`.
   * **Upstream URL** — the URL of the MCP server you are wrapping. Example: `https://mcp.example.com`.
   * **Transport** — `HTTP` or `SSE`, matching how the upstream server exposes itself.
   * **Optional OAuth overrides** — leave blank to use the upstream server's discovery defaults. Fill them in if you need to pin a specific client, scope, resource, or authorization server:
     * **Client ID** and **Client secret** — pre-registered OAuth client for the upstream server. The client secret is encrypted at rest.
     * **Scope** — space-separated OAuth scopes (for example `openid profile email`).
     * **Resource URL** — RFC 8707 resource indicator for the upstream API.
     * **Authorization server URL** — explicit authorization server, used when discovery does not point at the right one.
4. Click **Add MCP server**.

The server appears in the MCP servers list with an **Unauthorized** badge. Click **Authorize** to start the OAuth flow against the upstream provider; once the upstream returns successfully, the badge flips to **Authorized** and the server is ready to use.

If credentials change or expire upstream, the server is flagged as **Needs reauth**. Click **Re-authorize** to refresh; existing proxy sessions are unaffected until they reconnect.

## Connect to the gateway with `tessl mcp proxy`

Once a gateway is authorized, any workspace member can connect to it through the CLI:

```sh
tessl mcp proxy <workspace>/<mcp>
```

* `<workspace>` is the workspace slug (the same one used in registry URLs).
* `<mcp>` is the server name you chose when creating the gateway.

For example, with a workspace named `engteam` and an MCP server named `linear`:

```sh
tessl mcp proxy engteam/linear
```

The command runs in the foreground and speaks the MCP protocol over its own stdin/stdout. It is intended to be launched by an MCP client (your coding agent, or any other tool that supports stdio-transport MCP servers) — running it directly in a terminal is only useful for smoke-testing connectivity.

### Authentication

`tessl mcp proxy` authenticates to the gateway as the calling user:

* **Interactive use** — sign in once with `tessl login`; the proxy reuses the same credentials.
* **CI or other non-interactive contexts** — set the `TESSL_TOKEN` environment variable to a Tessl API key for the workspace. See [`tessl api-key create`](/reference/cli-commands#tessl-api-key-create).

### Use the proxy from a coding agent

`tessl mcp proxy` is a standard stdio MCP server, so any agent that supports stdio-transport MCP servers can launch it. Add it to your agent's MCP configuration in the same way you would any other stdio server — see [Custom agent setup](/reference/custom-agent-setup) for the shape your agent expects.

A typical `.mcp.json`-style entry looks like:

```json
{
  "mcpServers": {
    "engteam-linear": {
      "command": "tessl",
      "args": ["mcp", "proxy", "engteam/linear"]
    }
  }
}
```

You can include several gateways at once — give each one a distinct key so the agent can address them individually.

### Use the proxy from a plugin

Plugins can declare MCP servers in their bundled [`.mcp.json`](/reference/configuration#bundled-mcp-json), and a workspace gateway is just another stdio MCP server from the plugin's perspective. Add an entry that invokes `tessl mcp proxy`:

```json
{
  "mcpServers": {
    "engteam-linear": {
      "command": "tessl",
      "args": ["mcp", "proxy", "engteam/linear"]
    }
  }
}
```

When a project installs the plugin, the gateway is wired into the project's agents automatically.

## Related

* [MCP tools](/reference/mcp-tools) — the registry MCP server (`tessl mcp start`) versus the workspace gateway (`tessl mcp proxy`).
* [Configuration files](/reference/configuration) — how `.mcp.json` is shaped and discovered.
* [Custom agent setup](/reference/custom-agent-setup) — how individual agents read MCP server entries.


# GitHub badges

Using GitHub badges is a great way to share your skill and show a score!

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-d645c9fbccaf2b17224be693e613273cec73fd6e%2Fimage%20(19).png?alt=media" alt=""><figcaption></figcaption></figure>

## Usage

To use a badge, navigate to the registry page of your plugin/skill. If the skill is public, there will be a badge displayed on the right hand side of the page. Use the copy button and place the markdown in your README.MD to display the badge on your GitHub page.

## Improving your score

To improve a low score, run `tessl review fix ./my-skill --workspace engteam` to apply automated improvements. See [Check a skill's quality using review](/improving-your-skills/reviewing-skills).

To gate the score in CI – for example, to fail a pull request when a skill drops below a threshold – use `tessl review run` with `--json` and `--threshold`. See [Gate skill quality in CI](/codifying-and-enforcing-your-skill-standards/gate-skill-quality-in-ci).


# Glossary

Key concepts and terminology used throughout Tessl.

This glossary defines the core concepts you'll encounter when working with Tessl.

## Tessl CLI

Tessl's command line tool for installing, updating and managing context for coding agents.

It enables users to:

* Create, install and manage agent skills and Tessl packages across their coding agents
* Publish skills and Tessl packages to the Tessl Registry
* Locally evaluate skills and Tessl packages

## Tessl Registry

Tessl's online registry for discovering and distributing agent context.

It enables users to:

* Discover agent skills and Tessl packages (plugins) and assess their quality (via evals)
* Connect their repositories to Tessl and automatically generate context for them
* Request new skills and Tessl context

## Skill

Agent Skills are folders of instructions, scripts, and resources that agents can discover and use to do things more accurately and efficiently, giving agents access to procedural knowledge and specific context they can load when the agent determines they're relevant (lazy push).

In Tessl, skills are treated as software with a complete lifecycle: versioned, evaluated, and maintained as dependencies and systems change.

See <https://agentskills.io/home> for more information.

## Tessl package (Plugin)

Tessl packages (or plugins) are versioned bundles of reusable, agent-agnostic context that make coding agents more effective. They can (but do not always) contain:

* Skills
* Rules

Like software packages in npm or pip, Tessl packages are versioned, dependency-managed, and can be safely updated as libraries and systems evolve.

## Tessl project

The stable home for Tessl in a Git repository. A Tessl project identifies that repository and acts as the anchor for the eval runs and other repository-connected data attached to it.

## Repository

A Git repository containing your code and history.

## Codebase

The code inside a repository.

## Working directory

The directory, or subdirectory, where you run the CLI.

## Project link

The link stored in `tessl.json` that connects a repository to a Tessl project.

## Rules

Mandatory steering for the agent to always follow, always pushed to the agent's context (eager push).

## Verifier

An LLM-as-judge check that evaluates committed files or a diff against a binary, observable repository invariant. The verifier JSON defines what to judge, while the `verify` block in `tessl.json` defines its file scope and whether findings are advisory or blocking.

See [Verifiers overview](/codifying-and-enforcing-your-skill-standards/verifiers-overview) for guidance on when to use one.

## Evaluations (evals)

Structured testing processes to systematically measure AI system performance, accuracy and reliability. Evaluations are typically contextual - they depend on the type of evaluation and the type of AI system or agent.

Tessl uses evaluations to validate that skills and context actually improve agent behavior, catch regressions as systems change, and ensure updates are safe before they reach production workflows.

The evaluations we run at Tessl typically measure the success rate of agents on real world tasks with and without some context, by:

* Generating a representative set of tasks
* Prompting agents to solve the tasks with and without the context
* Scoring the agent's solution to the tasks using grading criteria (we typically use criteria with multiple binary criteria at Tessl)
* Comparing baseline performance against performance with the context over all tasks


# FAQs

Find quick answers to the most common questions about Tessl.

The following lists a set of questions that we have been asked about Tessl in the past. If you have a different question which isn’t answered here, talk to the community on the [Discord Server](https://discord.com/channels/1233374481364418570/1417176126207426760).

## Getting Started

### How can I install a skill with Tessl?

You can install skills using the Tessl CLI. Search for available skills with `tessl search <skill-name>` and install them with `tessl install <skill-name>`. Tessl automatically evaluates skills to ensure they improve agent behavior.

For detailed instructions, see [Improve your first skill](/introduction-to-tessl/improve-your-first-skill).

## Core Concepts

### What is a Plugin?

Plugins are Tessl packages, which are bundles of reusable context that capture knowledge you want to use across your team and projects. Common plugin content includes:

* Agent skills
* Usage documentation for libraries, APIs, tools, etc.
* Common, repeatable workflows
* Styleguides
* Policies
* ...

### What is the difference between a plugin and a skill?

A plugin is a bundle of agent context. A plugin is a container for context - it's what you install and manage via the package manager.

A skill is one type of context that can be included inside a Tessl package (along with docs and rules).

A helpful analogy is:

* Tessl package/plugin = the npm package
* Skills, rules = different types of files inside that package

Just like npm packages, plugins are versioned and can be updated safely as libraries and dependencies evolve.

For detailed definitions, see our [Glossary](/reference/glossary).

### What is a Tessl project?

A Tessl project is the stable home for Tessl in a Git repository.

It identifies that repository and acts as the anchor for the eval runs and other repository-connected data attached to it.

See [Projects overview](/projects/overview).

### What is the difference between a repository and a Tessl project?

A repository is your Git repo.

A Tessl project is the stable home for Tessl in that repository.

## Platform

### What agents does Tessl support?

Skills are agent and model-agnostic, allowing teams to maintain consistent behavior across tools without locking into a single ecosystem.

Tessl installs skills to the appropriate agent-specific directory:

* **Claude Code**: `.claude/skills/`
* **Cursor**: `.cursor/skills/`
* **Codex**: `.codex/skills/`
* **Gemini**: `.gemini/skills/`
* **Copilot CLI**: `.copilot/skills/`
* **Copilot in VSCode**: `.copilot-vscode/skills/`

Docs: full support for Claude Code, Cursor, Gemini, Codex, Copilot CLI, and Copilot in VSCode.

See the [Supported platforms](/support/supported-platforms#agents) reference for the most up-to-date list of supported agents.

### How does Tessl compare to other marketplaces like skills.sh?

[skills.sh](https://skills.sh) is a directory and an easy way to browse and copy individual skills. Tessl is a fully fledged package manager and registry for agent context: you install, version, and update skills as dependencies rather than duplicating them to your codebase.

Comparing the two:

* **skills.sh** – Browse and copy skills from a catalog; no built-in versioning or dependency management. You manually keep skills in sync.
* **Tessl** – Install skills with `tessl install <skill-name>`; skills are versioned, tracked in a manifest, and updates are controlled and repeatable across your team.

Tessl also adds:

* **Evaluation** – Measure whether skills are structured well and if they improve agent behavior
* **Lifecycle managemenet** – Ensure skills stay correct and up to date as systems change

So Tessl treats skills as software with a full lifecycle (build, evaluate, distribute, optimize), which helps teams move from ad‑hoc agent usage to long-lived, production systems where agent behavior can be inspected, measured, and reasoned about.

## Evaluations

### Why do I need a Tessl project to run an eval?

Evals need a Tessl project so Tessl knows which repository the eval belongs to.

Without that link, the run is just a one-off local action. With a Tessl project, Tessl can keep the run attached to the right repository, find it later, and compare it with future runs.

See [Projects overview](/projects/overview).

### How does Tessl evaluate skills?

Tessl evaluates skills using both task evaluations and conformance reviews:

**Task evaluation:** Measure whether agents can successfully complete real-world coding tasks when using the skill.

* Creates a representative set of coding tasks that require using the skill
* Runs each task with the agent both with and without the skill installed
* Scores each task using task-specific success criteria
* Compares baseline performance against performance with the skill over all tasks
* Reports improvement as a multiplier (e.g., 2× improvement)

**Conformance review:** Assess whether the skill conforms to the Agent Skills specification created by Anthropic.

* Validates that the skill conforms to the official specification at [agentskills.io/specification](https://agentskills.io/specification)
* Checks proper file structure and formatting
* Verifies required frontmatter fields (name, description)
* Ensures the skill follows best practices defined in the specification

## Publishing & Sharing

### How can I publish a skill?

To publish a skill:

* Create an account and workspace on Tessl
* Define visibility (public or private) at the Tessl package level
* Use the Tessl CLI to publish:
  * `tessl skill publish` - If you have a standalone `SKILL.md` file (skill-first workflow)
  * `tessl plugin publish` - If you've created a plugin structure with `.tessl-plugin/plugin.json` that contains a skill (plugin-first workflow)

Published skills are evaluated and versioned, so users can see quality metrics and choose versions that match their dependencies. See [Creating skills](/creating-skills-and-plugins/create-a-skill) for a complete tutorial.

### Can I share these with my team?

Yes! The Tessl Registry has workspaces which let you share Tessl packages for your own libraries with your team members. Skills and context can be kept private to your workspace or published publicly.

This allows teams to onboard agents the same way they onboard developers: with shared assumptions, behavior, and clear expectations about how internal systems should be used.

## Advanced

### Does Tessl support monorepos?

Yes! Tessl supports monorepo workflows. Run `tessl init` at the monorepo root to create `tessl.json` and configure your coding agent. From there, use `tessl search` to find plugins and `tessl install` to add them. You can run `tessl install` from any subfolder to add plugins relevant to that part of the repo — all installed plugins are tracked in the single `tessl.json` at the root.

**Example:**

```sh
# Initialize Tessl at the monorepo root
tessl init

# Search for and install plugins relevant to a subfolder
cd backend
tessl search fastapi
tessl install tessl/pypi-fastapi
```

## Support & Help

### How can I check if Tessl services are experiencing issues?

Visit the [Tessl Status Page](https://tesslstatus.io/) to check the current status of all Tessl services, view any ongoing incidents, and see historical uptime data.

For other support options, see [Giving feedback](/support/giving-feedback).


# Giving feedback

### Contact us

You can get in touch with us if you need some help via the following methods:

* **Email -** <support@tessl.io>
* **Discord -** [#tessl-product](https://discord.com/channels/1233374481364418570/1417176126207426760), a channel where you can quickly reach the team
* **GitHub Issues -** [tesslio/cli](https://github.com/tesslio/cli/issues)
* **CLI -** run `tessl feedback` in your command line to quickly send notes straight to the team!

We really appreciate all feedback you have to give - brief, detailed, complimentary or critical - it helps us to improve.


# Supported platforms

This page lists the supported version. Even if your favorite platform is not listed, it might still work  as we support many industry standards.

## Agents

Tessl supports multiple coding agents through MCP (Model Context Protocol) integration.

| Agent                          | Skills | Rules | MCP |
| ------------------------------ | ------ | ----- | --- |
| **Claude Code**                | Yes    | Yes   | Yes |
| **Cursor**                     | Yes    | Yes   | Yes |
| **Codex**                      | Yes    | Yes   | Yes |
| **Gemini CLI**                 | Yes    | Yes   | Yes |
| **Antigravity**                | Yes    | Yes   | Yes |
| **GitHub Copilot CLI**         | Yes    | Yes   | Yes |
| **GitHub Copilot for VS Code** | Yes    | Yes   | Yes |

When `tessl init` detects or you select any of the agents above (**Claude Code**, **Cursor**, **Codex**, **Gemini CLI**, **Antigravity**, **GitHub Copilot CLI**, **GitHub Copilot for VS Code**), it configures them automatically.

Other agents that support MCP can also be manually configured to work with Tessl. See [Custom agent setup](/reference/custom-agent-setup) for detailed instructions.

For more details on agent installation, see the [init command](/reference/cli-commands#tessl-init).

## Node runtime

* You need to have a modern version of Node.js installed (we use **v22.17.0 or higher**)

## Operating system

* **Supported**: Mac, Linux, Windows

On Windows, skills are linked with directory junctions rather than symbolic links. If you move or rename your project folder, run `tessl install` again to restore the links. See [Skills not found after moving the project folder on Windows](/support/troubleshooting#skills-not-found-after-moving-the-project-folder-on-windows).


# Troubleshooting

The Tessl CLI provides diagnostic capabilities through the `tessl doctor` command that outputs information about the environment and the CLI itself. This information can be helpful to support and in diagnosing issues. Additionally the CLI will provide guidance on how to resolve issues that are detected. The CLI output defaults to human readable output and can be configured to output JSON for reporting automation.

#### Usage <a href="#tessl-doctor" id="tessl-doctor"></a>

```
tessl doctor [--json]
```

**Flags**

* `--json` (boolean, optional): Output diagnostics as JSON for programmatic processing

<figure><img src="https://1171143958-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXbWzspFdXV0HNujz6gJ9%2Fuploads%2Fgit-blob-1e1658387f1301adb959f30882bb095912e28feb%2Fimage%20(84).png?alt=media" alt=""><figcaption></figcaption></figure>

#### **Examples**

```
# Run diagnostics with human-readable output
tessl doctor

# Output diagnostics as JSON
tessl doctor --json
```

## Common Issues

### Authentication Issues

If you're having trouble authenticating:

1. Run `tessl doctor` to check your authentication status
2. Try logging out and back in: `tessl logout` then `tessl login`
3. Check that your browser allows popups for the authentication flow
4. Verify your internet connection

### Installation Issues

If `tessl init` or `tessl install` fails:

1. Ensure you have the required package manager installed (npm, pip, etc.)
2. For installing skills from GitHub, `tessl install <skill> -v` will provide verbose messaging, showing warning details.
3. Run `tessl doctor` to identify configuration issues

### MCP Server Issues

If your agent cannot connect to the Tessl MCP server:

1. Verify the MCP server is configured in your agent's settings
2. Check that `tessl mcp start` runs without errors
3. Restart your agent after configuring MCP
4. Review the MCP configuration in your agent's settings file

### Workspace Access Issues

If you cannot access a workspace or plugin:

1. Verify you're a member: `tessl workspace list-members <workspace-name>`
2. Check your role has appropriate permissions (see [Roles](/administrators/roles))
3. Ensure you're logged in: `tessl whoami`

### Project Issues

If your project appears in the Tessl web app but `tessl install` or `tessl init` in a fresh clone behaves as if no project exists:

1. A teammate likely created the project without committing `tessl.json` to the repository.
2. See [project visible in the web app but missing after cloning](/projects/repairing-projects#project-visible-in-the-web-app-but-missing-after-cloning) for the recovery steps.

### Skills not found after moving the project folder on Windows

On Windows, `tessl install` links each skill into your agent's skills directory using a directory junction. A junction records an absolute path. If you rename or move the project folder, every link still points at the old location and the agent finds no skills.

Run `tessl install` again from the new location to restore every link:

```
tessl install
```

## Getting Help

If you continue to experience issues:

* Run `tessl doctor --json` to capture diagnostic information
* Visit our [FAQs](/support/faqs) for common questions
* [Give feedback](/support/giving-feedback) or report issues to the Tessl team


# CLI Changelog

Latest version = 0.113.0

## 0.113.0

### New Features

* **GitLab merge request reviews**: `tessl code review --pr <IID|URL>` now reviews a GitLab merge request, including its notes and discussions, and selects the merge request automatically in GitLab CI pipelines. `--publish comment` posts a summary and inline discussions, and resolves or reopens them on later rounds. Authentication uses `glab` locally or a CI API token.
* **Linked documents in code review**: When a pull request asks the review to check an external link, such as a dependency's upgrade guide, the review fetches up to 5 requested links and every reviewing agent can read them. HTML pages are reduced to their text, and links the pull request does not ask about are never opened.
* **Linked-source record**: `tessl code review` lists which requested links were fetched and which were not, with the reason, in its terminal output and under `linkedSources` in `--json`
* **Finding trigger events**: `tessl doctor` accepts automation triggers on the Tessl `finding.created` event

### Improvements

* **Check triggers**: `tessl doctor` accepts automation triggers on GitHub `check_suite` and `check_run` events
* **Minimal effort**: Automation actions accept `minimal` as a reasoning effort
* **Verify cost guidance**: `tessl change verify --help` explains how judge calls spend credits and how to bound a run, and the `--max-calls` guard repeats it before a large run

## 0.112.0

### New Features

* **Eval wait timeout** - `tessl eval run --wait-timeout` sets how long to poll each run, in minutes or as a duration such as `90m` or `2h`, and turns on polling without `--wait`. The default wait is now 2 hours instead of 30 minutes. When the limit is reached, the CLI says the run is still in progress and prints the `tessl eval view` command to check it.
* **Variants as JSON** - `tessl eval run --variant-json` takes the variants to compare as inline JSON or a JSON file, with optional `localPath` fixtures uploaded from your machine. It can't be combined with `--arms-json`.

### Improvements

* **Verifier help** - `tessl change verify --help` shows `exclude` and `content` filters in its setup example, and links to the verifier docs and the `tessl/tessl-verify` plugin. `tessl change --help` names its `verify` and `risk` subcommands.

### Bug Fixes

* `tessl agent` retries a turn when the model connection drops with `The socket connection was closed unexpectedly`, instead of ending the turn with an error

### Deprecations

* `tessl eval run --arms-json` still works but prints a deprecation warning. Use `--variant-json` instead.

## 0.111.0

### New Features

* **API key TTL** - `tessl api-key create --ttl <duration>` sets a key to expire after a whole number of hours, days, weeks, months (30 days) or years (365 days), written as `3h`, `2d`, `4w`, `6mo` or `1y`

### Improvements

* **Deprecation notices on install** - `tessl install` warns when a plugin you install is deprecated, shows the owner's reason, and prints the install command for the recommended replacement when one is set. The install still completes. The notice appears when you install a plugin without pinning a version.
* **Full API key expiry** - `tessl api-key create` prints the key's expiry as a full date and time in UTC instead of a bare date.

## 0.110.0

### New Features

* **Jev verification** - `tessl change verify --model jev` can use Jev for pass/fail decisions.
* **Scoped skill search** - `tessl search` accepts `--org` and `--workspace` to limit results to an organization or workspace the current user can access.
* **Agent credit awareness** - `tessl agent` shows session credit use and remaining allowance, with notices as allowance use rises.

### Improvements

* **Verification dry runs** - `tessl change verify --dry-run` is now more compact. Use `--show-files` to inspect matched files.
* **Publication guidance** - Plugin and skill publishing output distinguishes upload acceptance from publication approval and gives clearer steps when publication is blocked.
* **Schedule diagnostics** - Schedule validation warns when a linked workspace lacks an environment named by a schedule, and warns when a schedule names a multi-skill plugin without selecting a skill.
* **Launch resume limit** - A launch run can be resumed for seven days. The CLI help now states that limit.

### Bug Fixes

* **Install sync** - `tessl install` can sync dependencies when an unrelated schedule block is invalid; schedule validation still reports the problem.
* **Verifier result validation** - Verification rejects missing, duplicate, or renamed checklist results and refreshes invalid cached results before reporting a verdict.
* **Global policy reconciliation** - When the organization for a managed global install is ambiguous, the CLI stops before changing installed plugins, rules, or skills.
* **Eval comparison values** - Compact `tessl eval compare` output pairs standard deviation with the mean used for scores, cost, and duration.
* **Fixture path handling** - Local eval fixtures resolve symlinked parent directories correctly while retaining path boundary checks.

## 0.109.0

### New Features

* **Tessl Code Review is out of beta** - a completed review round costs a flat 50 credits, a review stops when the organization has no credits left, and the spend shows up in `tessl org usage`
* **GitHub access on an environment** - `tessl environment create` and `tessl environment update` take `--github-access none|read|write`, which sets the GitHub access granted to runs using that environment. `tessl environment list` and `tessl environment view` show it, and `--env-file` is no longer required when you pass `--github-access`
* **Schedule capacity warnings** - `tessl schedule validate` warns when a manifest declares more schedules than the workspace has active slots for. The command still exits 0 and reports `valid: true`, and `--json` output carries a new `warnings` array
* **`tessl schedule list`** - warns when the linked project's last schedule sync failed, with the time it failed and the reason

### Improvements

* **`@every` schedule expressions** - accept the full Temporal duration grammar rather than only whole minutes or hours
* **Cron rejection messages** - a full month or day name is corrected to its three-letter abbreviation, an out-of-range field names the field and its bounds, and a six-field expression is refused without parser detail

### Bug Fixes

* A cron that fires too often points at the field that makes it too frequent instead of the whole entry
* An invalid schedule key reports the full key rule against that key, rather than losing the message
* `tessl workspace create` asks which organization should own the workspace instead of failing when you belong to more than one, and an organization API key can now create a workspace

## 0.108.0

### New Features

* **`tessl eval run --arms-json`** - supply comparison arms as inline JSON or a JSON file path. Malformed arrays, more than ten arms, non-object entries, and missing or duplicate labels fail before the run starts
* **Readable schedule formats in `tessl.json`** - a schedule accepts raw cron, a closed set of readable shorthand strings, or a structured object, and `schedule` works as an alias for `cron`
* **Agent signals from plugin hooks** - skill activations, MCP tool activations and session availability are captured when hooks fire

### Improvements

* **Dangling skill links in `tessl doctor`** - a skill link whose target has moved is reported as a blocker with `tessl install` as the next action, in both the human output and `--json`. Renaming a project folder on Windows breaks every link at once, and `doctor` previously reported everything healthy
* **Eval comparison tables** - `tessl eval run` renders a compact arm comparison table before the per-arm scenario tables, with score, cost, duration, token and turn values in separate columns. In `--full` mode distributions render vertically and each scenario's criterion table follows its arm table
* **One table style** - every human-readable table shares a single borderless style with bold headers
* **Code Review setup link** - the embedded `tessl/code-review` plugin moves to 0.3.1, so installing the App from `tessl agent` lands on the organization's Code reviews page, which carries connect, repository selection and the two configuration questions in order
* **`tessl schedule list`** - the truncation notice is reworded

### Bug Fixes

* A `PostToolUse` hook accepts an MCP content array as `tool_response`, which several native adapters rejected before the hook script ran
* A Claude Code `SessionStart` model is preserved when the native payload supplies one, and Claude Code `PostToolUse` is no longer expected to carry a duration
* Recursive verification rejects a `childManifests` path that resolves outside the active project root
* Launch run live progress defaults to `metrics.json`, the artifact every recipe produces

## 0.107.0

### New Features

* **Organization selection for Code Review** - `tessl code review` accepts `--org` and `TESSL_ORG`. The selected organization drives model availability, credit preflight, key minting and billing attribution, and stays pinned to the key even if the account default changes later
* **`tessl outdated --global`** - check the plugins installed in `~/.tessl` from any directory, with or without a local `tessl.json`
* **`tessl schedule list --all`** - page through every schedule instead of the first page. Without it, a notice now says how many are shown when more exist

### Improvements

* **Reviewed code state in Code Review output** - `tessl code review` names the selected head and comparison base, and warns when a committed selection leaves uncommitted local edits out of the review
* **`@tessl-reviewer`** - accepted as an alias for `@tessl-code-review` when triggering a review from a pull request comment
* **Tessl Verify model default** - `tessl change verify` and `tessl change verify suggest-scopes` now run on GPT-5.6 Luna. An explicit `--model` still takes priority
* **Eval view statistics** - `tessl eval view` builds its rollups and compact summaries from distributions, pairs spreads with means, labels solution cost as `mean/solution`, keeps the median in `--full` output, and marks a distribution that is incomplete

### Bug Fixes

* `tessl code review --json` emits one JSON document with nothing on stderr, so merged output streams parse cleanly through model retries and command failures
* `tessl eval view` shows why a scenario was excluded by the quality check rather than only that it was
* `tessl eval run` credit estimates count every arm, so a multi-arm run is no longer under-quoted before it starts
* `tessl update` reports every dependency whose version check failed, including private ones, instead of hiding them when another dependency is outdated
* `tessl agent` no longer leaves a stale status line frozen in the scrollback after a turn ends
* Filesystem, network and out-of-memory failures are reported as clear errors instead of surfacing as crashes

## 0.106.0

### New Features

* **Desktop notifications** - the agent rings the terminal bell and fires an OS notification when a turn finishes or a permission prompt appears while your terminal is in the background. Nothing fires while the terminal has focus, and print mode never notifies. Background detection relies on ANSI focus-event mode 1004, so a terminal that does not report focus changes is treated as focused and stays quiet. Turn it off with `tessl config set notifications false`
* **Every agent run ends with a summary** - an interactive `tessl agent` run now closes with one block: the files it recorded writing or editing, and one suggested next action, offered as prompt ghost text you accept with Tab. The file list covers edits made through the edit and write tools, so a run that reports none is saying it recorded no such edit, not that nothing on disk changed
* **`--no-wait`** - `tessl eval run`, `tessl review run` (quality and security) and `tessl scenario generate` take `--wait` and `--no-wait`. With `--no-wait` each submits the work and returns its run id, or ids for an `eval run` that submits several, without polling, so a CI job can start a run and collect the result later with the matching `view` command. It is rejected alongside a gate that needs the run to finish: `--threshold` on a quality review, `--fail-on` on a security review
* **Multi-arm eval runs** - `tessl eval view` detects a run with custom arms and renders one section per arm, each with a rollup line and per-scenario rows carrying score, cost per solution, turns, tokens and agent time. `--full` expands every stats cell to the full distribution: min, q1, median, mean, q3, max and sd. The classic baseline and with-context pair keeps its existing layout, now with compact stats lines. Arms mode never shows lift

### Improvements

* **The GitHub App is the supported way to install Code Review** - the docs and every CLI surface that pointed at the GitHub Action now point at the Tessl Review GitHub App, and the Action is marked unsupported. The tutorial is rewritten around installing the App, enabling a repository, trigger policy, approval, the three comment intents and the `.tessl-code-review.yml` reference; running Code Review in your own CI becomes a sub-page of it. The CLI embeds `tessl/code-review` 0.3.0
* **Deleting your last workspace deletes it** - the old behavior removed the workspace and immediately re-created an empty copy in its place, then explained across six messages that it had been kept. An organization may now hold no workspaces
* **A warning before a workspace name is lost** - deleting a workspace that has published a public plugin permanently reserves its name, and an interactive `tessl workspace delete` now says so while you can still change course, along with pointing out that moving a workspace between organizations is a support request rather than a delete and recreate. A scripted deletion does not show the preview
* **Windows skill installs no longer need Developer Mode** - `tessl install` and `tessl init` create a directory junction on Windows instead of a symbolic link, which a standard user can create without elevation. Installation had been failing outright on devices whose policy blocks Developer Mode
* `tessl skill review` is removed. It has printed a deprecation banner since June and the migration guide's removal date passed in July. Invoking it exits non-zero with "No review ran" and names `tessl review run` and `tessl review fix` as its replacements, and it still parses every flag the old command took, so a pipeline reaches that message rather than a flag-parse error. The flag-by-flag mapping is in [Migrate from skill review](/improving-your-skills/migrate-from-skill-review)

### Bug Fixes

* Ctrl+C works while a spinner is running. `tessl code review` made this visible because its live lens board keeps one spinner up for the whole run, so the command could not be interrupted at all, but every spinner in the CLI was affected whenever nothing had read stdin before the spinner started
* A tool call that fails and is recovered from on the next turn renders on the neutral gray background rather than in a red error box. The error output is still shown. Stack traces from background dependencies no longer reach the terminal
* The permission dialog's options each state their own effect, ordered narrowest to broadest: allow once, allow this pattern for the session, block once, and block with an optional reason for the agent
* Every interactive session has exactly one prompt ghost. Until the conversation starts it offers the splash question, and after every completed turn it offers the next thing to type, in every session rather than only one started with `/skill:<name>`
* `tessl scenario generate` against a repository now fails when a generation completes with no scenarios, matching what the plugin path already did. The exit code carries that verdict only on an invocation that waits for the run, so a CI pipeline gating on it needs `--wait`, since the command otherwise submits and returns before the outcome is known. The human output counts scenarios rather than generations

## 0.105.0

### New Features

* **Live lens board** - `tessl code review` shows a per-lens progress board on stderr while it runs, marking each lens as queued, reviewing, a finding count, failed, or skipped before the reconciling step; `--no-progress` hides it and `--json` implies it
* **Lift as the eval headline** - `tessl eval run` prints a `Lift:` line under each completed comparison run, and `tessl eval view` leads its summary and each scenario with the lift in percentage points, noting how many unscored results were excluded
* **Organization-level project discovery** - `tessl project list`, `tessl project link`, and `tessl project create` discover projects across your whole organization, with `--org` (or `TESSL_ORG`) to pick the organization, `--workspace` for single-workspace behavior, and `tessl project link --path` to match a package project by its repository subpath

### Improvements

* **One Tessl Code Review plugin** - the CLI embeds `tessl/code-review` 0.2.0, which now carries the setup, lens-authoring, and review-response skills alongside the four lenses; the default lenses are repinned to 0.2.0 with unchanged content, and a project that still declares `tessl/code-review-setup` or `tessl/code-review-lens-creator` in `tessl.json` keeps using it until it switches
* **Code review at splash** - the "Review code changes" onboarding workflow installs `tessl/code-review`, sets up review on every pull request, and suggests one custom lens the default lenses would miss, without writing it
* **Grouped review summary** - `tessl code review` ends with a versioned header, a verdict line with must-fix and suggestion counts, findings grouped into must fix and suggestions sorted by severity, and the exact publish command; under `--json` the same summary goes to stderr so CI logs carry it
* **Eval reuse made visible** - `tessl eval run` and `tessl eval view` print how many results were reused from previous runs beside the cost line, so a fast, cheap run no longer reads as stale
* **`tessl project list --json` reports the organization** - the output carries a top-level `org` instead of `workspace` unless `--workspace` or `TESSL_WORKSPACE` pins one

### Bug Fixes

* `tessl code review` against a caller-supplied LLM endpoint with a model-scoped key no longer fails during reconciliation, because the pinned supervisor model is sent by its bare name
* `tessl eval view` floors score percentages instead of rounding them, so the table, averages, and lift agree
* The splash screen's workflow picker moves one step per arrow key press on Kitty-protocol terminals such as Ghostty, Kitty, and WezTerm, where the key release was counted as a second press; a held arrow still scrolls smoothly

## 0.104.0

### New Features

* **Eval cost shown before you spend** - `tessl eval run` prints an estimated cost line and your org's daily eval usage before the run starts, including with `-y`
* **Eval cost in machine output** - `tessl eval run --json` gains `estimatedCredits` and `quota`, and `tessl eval view --json` gains `meta.credits` for the settled run cost

### Improvements

* **Review action beside severity** - every published Code Review finding now shows `Requires changes` or `Advisory` next to its severity, along with why an advisory finding is advisory: low confidence, pre-existing, unclear relationship, or the review mode
* **Run cost reported in CI** - the post-run credit cost line is no longer suppressed on non-interactive streams, so CI and piped runs see the spend

### Bug Fixes

* `tessl api --input -` and `-F key=@-` now read stdin from a redirected file or heredoc, not just a pipe; both refuse an empty stdin or a bare terminal with an error instead of sending no body

## 0.103.0

### New Features

* **Scheduled runs** - `tessl schedule` is now available. You can use it to automatically run skills on a periodic schedule.
* **Environment management** - `tessl environment` can be used to manage environments for `tessl schedule` and `tessl launch skill --cloud`
* **Antigravity support** - first-class support for the Antigravity CLI (`agy`) and the Antigravity IDE, alongside the existing Gemini integration
* **`tessl eval run --wait`** - polls the run to completion and exits non-zero when all runs fail; `--json` always submits without waiting, and a run that outlives the poll window exits 0
* **`tessl api-key delete --yes`** - skips the confirmation prompt, with `-y` as the short form

### Improvements

* **Agent step lines** - each tool call in an interactive `tessl agent` session renders as one status line naming the step and its result, with `ctrl+o` to expand the full output
* **Expand-all hint** - the agent header now advertises the collapse and expand-all shortcut, resolved from the effective keybinding
* **Activity footer** - the footer names the running step, shows an elapsed timer, and keeps the known-total count, then leaves a one-line result carrying the total duration
* **Permission prompts** - the command, target, or path appears on its own indented line, long commands are middle-truncated so the tail stays visible, and bash prompts carry a Why line when the agent states its intent
* **Code Review finding assessment** - the supervisor now assesses likelihood, confidence, and relationship to the change alongside severity, and a deterministic Strict, Standard, or Relaxed policy decides what blocks
* **Code Review severity table** - rows are ordered Critical, Major, Minor, then Nit rather than by insertion order
* **Code Review round notice** - rounds after the first carry a collapsed "About this review" block noting the findings are AI-generated, with a link to the guidance
* **Code Review thread links** - a finding carried forward now links the thread that is carrying it
* **Skill review phases** - the bundled `review-skills-in-repository` skill splits into a Discover phase that reports and stops, and a Review phase entered only when you opt in
* **Skill review scope** - after discovery the skill offers one skill, a prioritized batch, or every discovered skill, each with an expected duration and credit cost, replacing a silent 20-skill cap
* **Skill review output** - the review leads with a ranked list of every reviewed skill, one line each, with per-skill detail on request and coverage counts below it
* **Skill discovery** - discovery runs as a deterministic bundled script rather than an agent file-system walk
* **Clearer command briefs** - `tessl project`, `tessl agent`, `tessl outdated`, and `tessl update` now describe what they do
* **Clearer skill name errors** - an invalid skill name names the specific problem and suggests a corrected name
* **Eval default model notice** - the notice no longer names a model and points at `tessl eval run --list-agents`, since the default is declared per region
* **Project mismatch** - `tessl install` blocks with the relink command when the backend already has a project for this directory, and `tessl init` warns before creating a second one

### Bug Fixes

* `tessl eval view` exits 1 for a failed run, and `tessl eval run` aborts with exit 1 when the scenario count exceeds the guard
* `npm install tessl@X` installs version X instead of whatever the release channel currently advertises
* The agent session no longer clears scrollback and snaps to the top while streaming, so a permission prompt stays reachable
* Code Review keeps successful lens results when a sibling lens fails, and states the reduced coverage instead of discarding the whole review
* Code Review republishes a moved finding at its current anchor and links the earlier occurrence, instead of leaving the only comment on stale code
* Code Review includes verified optional findings in first and explicitly requested full reviews
* `tessl workspace delete` distinguishes a deleted workspace from one emptied in place, and names the plugins to remove when they block deletion
* `tessl scenario generate` no longer blames plugin content or the quality verifier for a zero-scenario result, and reports a timeout distinctly from a failure
* `tessl scenario download` defaults to the plugin root when run inside a plugin
* Fixing path handling bugs in `tessl install` and `tessl init` on Windows
* The agent onboarding editor keeps a custom editor factory and honors the Kitty keyboard protocol
* An ambient `WORKOS_CLIENT_ID` no longer overrides CLI auth

## 0.102.0

### New Features

* **`tessl eval lint`** - the scenario lint command now shows in `tessl eval --help`, and `tessl scenario download` prints a hint that downloaded scenario files can be edited by hand and validated with it
* **Launch activity for pending runs** - `tessl launch skill --cloud --wait`, `tessl launch view`, and `tessl launch list` now show a pending run's phase and last progress time; retrying and recovering read distinctly and are never shown as failed

### Improvements

* **Adaptive review effort** - `tessl code review` now defaults to adaptive effort, picking the right depth for each review; `--effort` with low, medium, or high stays fixed
* **Code Review finding quality** - findings are checked more rigorously before they are surfaced, and severity now better reflects the real consequence of each finding
* **MCP server status** - configured MCP servers now read as ready and connect automatically on first tool use; the footer, status output, and MCP panel share one state model with clearer sign-in guidance
* **Plugin terminology** - user-facing copy now consistently says plugin and plugins; the migrate commands for legacy tile content are unchanged

### Bug Fixes

* Code Review now replies on an existing review thread when a re-review changes its conclusion, instead of leaving its earlier answer as the last word while the check stays red
* `tessl login` now lets a user who belongs to more than one organization choose which one to sign in to on the approve page, instead of failing for members of an SSO-enforced organization with a second membership
* Spinners are suppressed when stdout is not a TTY, keeping piped output, `--json` mode, and agent contexts clean
* `tessl cli update` now shows a clear error pointing at `--channel` when a release channel name is passed as `--target`
* Eval runs that outlive the CLI's poll window are now reported as timed out instead of failed, so a run that completes server-side no longer triggers a false "All eval runs failed"
* The workflow hint in `tessl agent` now steps aside while another UI such as the settings menu owns the prompt row, and no longer appears on `--continue` or `--resume`

## 0.101.0

### New Features

* **`tessl login`** - loopback is now the default transport. Plain `tessl login` uses the loopback authorization-code and PKCE flow, and the device grant stays available with `--device`.
* **Local GitHub App minting** - when `TESSL_GITHUB_APP_ID` and `TESSL_GITHUB_APP_PRIVATE_KEY` are both set, `tessl github token`, `tessl github setup-auth` and the auto-refresh sidecar mint installation tokens directly as that App, so its own bot identity lands on commits and pull requests. With either variable absent, behavior is unchanged.
* **Guided first run** - a new signed-in user is handed off from the web onboarding into `tessl agent` with the review-skills workflow, with in-editor hints for the next action.
* **Context and Findings** - the embedded CLI skill now works in terms of Context and Findings instead of a skill inventory, and points at the current per-workspace Context and per-org Findings endpoints.

### Improvements

* **`tessl code review`** - a finding states what is broken and what a fix has to achieve instead of prescribing a specific fix, and a review no longer blocks on code outside the diff.
* A deliberate, explained reversal of an earlier fix is read as a decline rather than a regression, so the same concern is not raised again.
* A finding is reposted on a silenced thread only when it requests changes, so an optional suggestion is no longer republished every round.
* Local-mint failures show GitHub's own error message without `--verbose`, and report the measured local-versus-GitHub clock difference when the two disagree.
* The `--model` and `--scorer-model` briefs on `tessl eval run` show current model names.

### Bug Fixes

* `tessl login` reports the reason for a WorkOS authentication challenge, such as organization selection or email verification, instead of a bare status code.
* `tessl login --loopback` always runs the loopback transport instead of silently rerouting to the device grant on a host that looks remote. An automatic fallback prints why it happened and which flag overrides it.
* Skill symlinks resolve the physical parent directory before computing their target, so links no longer dangle when an intermediate directory is itself a symlink.
* `tessl launch skill` no longer reports a watch timeout as a launch failure. It stops watching, exits 0, and prints a `tessl launch view <id>` command.
* `tessl eval run` refuses a run started from inside a plugin's `evals/` directory before any credits are spent, rather than silently running baseline only with no plugin context.
* Onboarding hints release the arrow keys when another component, such as the model picker, takes focus.
* The `[PostHog] Timeout while shutting down PostHog` warning is no longer printed on exit.

## 0.100.0

### New Features

* **`tessl project list`** - lists the projects in a workspace with each one's name, ID and source. The workspace is resolved from `--workspace`, `TESSL_WORKSPACE`, the project linked in `tessl.json`, or a picker. `--json` emits a structured list and never prompts
* **A machine-readable verify report** - `tessl change verify --report-json <path>` writes its JSON report to a file, so CI can consume verifier findings without a second run. Stdout is untouched and `--github` annotations still work alongside it. The report now also carries each verifier's stable `id`, in both `--json` and `--report-json`
* **Cloud launch progress** - `tessl launch skill --cloud --wait` shows live progress on the spinner while the run is in flight, instead of a static message. `--json` and `--no-wait` are unaffected

### Improvements

* **Working with this review** - the first Tessl Code Review on a pull request now publishes a collapsed section telling the reader that the findings are AI-generated and not authoritative, which answer to give on each thread (addressed, refuted, or declined), that reviews are incremental and what a later round therefore withholds, and that lenses are configurable and glob-scoped. It is published on the first review only, so a later round does not push the findings further down. `tessl code review run -h` says the same things
* **Settled threads close themselves** - a Tessl Code Review round now resolves the threads whose finding the current head fixes, after replying on them, so what stays open on a pull request is what still needs someone. A refuted or declined finding's thread stays open for the author to close
* **One closing instruction on every round** - every published review round now closes with the same line: mention `@tessl-code-review` to run another review

### Bug Fixes

* **Repository flags accept a GitHub URL** - `tessl github setup-auth --repo` and `tessl github token --repo` accept a full GitHub URL as well as an `owner/name` slug. HTTPS, scheme-less `github.com/owner/name`, and SSH forms all resolve to the same repository, and a non-GitHub host is rejected with the value echoed back
* **A full review says it was full** - a full Tessl Code Review no longer closes by claiming it narrowed itself. A full round now states that the whole change was read and every new finding raised whatever its severity, and keeps the contrast with an ordinary round
* **A live finding is not left collapsed** - a re-review that concludes an earlier finding still applies now reopens that finding's resolved thread, so the update is not left where tooling and readers skip it. A thread whose finding was answered as addressed, refuted, or declined stays resolved
* **A finding reaches its reader when a thread cannot reopen** - the finding is published as its own comment instead of being left where no reader reaches it. A GitHub Actions token may reply to a review thread but may not unresolve one, so the review now asks per thread rather than assuming
* **Mentions are matched case-insensitively** - a mention of `@tessl-code-review` that differs only in case, such as `@Tessl-Code-Review approve`, is now read as a mention. An approval request spelled that way was previously answered with an ordinary review
* **Verify reaches allowlisted dot-directories** - `tessl change verify` discovers files in repository-root dot-directories that a verifier's include globs name explicitly, so an include such as `.github/code-review/*.mjs` matches instead of silently matching nothing. Only allowlisted repository-tooling directories are opened, and only at the repository root

## 0.99.0

### New Features

* **Named approver logins** - `tessl code review --approver <login>` names comment authors permitted to request an approval, beyond the repository owners, members and collaborators who always may. The flag is repeatable, logins are compared case-insensitively, and a bot login is matched exactly as the event payload spells it, `[bot]` suffix included. GitHub reports every app comment with association `NONE`, so before this no bot could pass the guard
* **A refused approval request no longer runs a review** - asking to approve without permission settles as skipped in under a second, rather than spending a full review to answer the opposite of what was asked. The message leads with what was refused and names the route back: approving is limited to owners, members and collaborators, plus any login passed with `--approver`
* **Full review requests** - a commenter who may approve can ask for a full review instead of another round. Its specialists then see the whole change rather than only the commits since the last reviewed head, and a low-severity finding no earlier round raised is raised. Prior findings still reconcile on their existing threads. A request from an author the repository does not trust to approve runs the ordinary round rather than being refused
* **A change-requirement threshold in Code Review profiles** - a repository YAML profile sets `requestChangesAt`, the severity at which a finding starts requesting changes, so a team adopting Code Review on a large existing codebase can gate on criticals alone while a repository with a strict main branch holds minors on every round. It is a profile key rather than a flag, so no caller can weaken the gate without changing the profile the repository committed. The default is now `major` on every round, which means a minor no longer requests changes on a first review
* **Machine-readable output on list and view commands** - `--json` on `tessl whoami`, `tessl config get`, `tessl config list`, `tessl org list`, `tessl plugin info`, `tessl workspace list` and `tessl workspace list-members` prints one JSON document. Every `--json` command now writes one pretty-printed document to stdout and routes hints, progress, warnings and errors to stderr, so stdout stays parseable
* **Sign in with a local browser redirect** - `tessl login --loopback` signs in with a loopback authorization code and PKCE, so the browser hands the session back to the terminal without a code to type. The device code flow stays the default and is unchanged, and `--loopback` falls back to it when the browser is not on this machine or the loopback port cannot bind
* **Find work to automate** - a fourth onboarding workflow in `tessl agent` looks for what you do repeatedly that could run itself. It returns at most five ranked candidates, each with its evidence, its expected value, a suggested trigger and an implementation direction, and expands one on request

### Improvements

* **Whoami describes the API key** - `tessl whoami` in a session authenticated with an API key now shows the current key in use (masked), its scope, its id, and the organization and workspace it belongs to
* **Findings below the threshold on a later round** - a re-review no longer raises a fresh finding below the change-requirement threshold, and a finding an earlier round raised stays on its thread. An approving review that carries findings now states how many of them are optional, so open findings under an approved verdict are not read as blockers
* **Guidance routing** - broad questions about Code Review, Risk and Verify reach the harness design skill, while an explicit request to author a review lens still reaches the lens creator
* **Code Review setup knows the new profile keys** - the bundled `tessl/code-review-setup` moves to 0.3.0, so a project that does not declare the plugin itself gets a setup skill that covers the change-requirement threshold and who may ask the reviewer to approve
* **A rejected CLI version says how to upgrade** - when the backend refuses a CLI as too old, the CLI prints the reason it was given along with how to upgrade, rather than a generic failure. A request that never reaches the backend at all is now reported as a network failure rather than as an API rejection
* **Clearer guidance outside a project** - a command run where no Tessl project can be found says to run `tessl init` from the project root, instead of describing what a project is

### Bug Fixes

* **Spinner frames stay out of JSON output** - `tessl review fix` and `tessl review run quality` stop the spinner before printing the `--json` status payload on the timeout, failed and cancelled paths
* **Code Review resolves the repository's default branch** - the base is read from `origin/HEAD`, falling back to a probe for `origin/main` then `origin/master`, instead of assuming `origin/main`. A repository whose trunk is named something else is told to pass `--base` rather than failing on a ref that does not exist, and an explicit `--base` still wins outright
* **An empty change is skipped rather than approved** - a local review of a clean, up-to-date checkout returns a skipped result carrying `No changes to review.` before any model call, instead of running every lens and publishing `Changes approved` over a change that does not exist
* **Eval runs report what happened** - `tessl eval run` retries a run that is momentarily missing from the poll endpoint just after it was created, rather than treating it as terminal, so completed runs no longer report `All eval runs failed`. When every run really does fail the command exits non-zero, so a pipeline wrapping it sees the failure

## 0.98.0

### New Features

* **Publish a Code Review from the CLI** - `tessl code review --publish <comment|verdict>` posts the completed review to the pull request it reviewed as one native GitHub review: a summary body, an inline comment on each finding GitHub can anchor to a changed line, and a reply on each earlier finding's thread the round reconciled. `comment` states no position on the change, while `verdict` carries the review's own conclusion, approving when nothing requires changes and requesting changes otherwise. With `--json`, the result carries a `publication` receipt naming what was published
* **Reasoning effort is supported configuration** - `--effort low`, `medium` or `high` sets how hard each review lens thinks. A YAML profile can set `effort` for every lens, and any single lens can set its own to think harder or less hard than the rest, so depth is spent where it pays. A lens without one uses the profile's, with neither the model applies its own default, and `--effort` overrides both
* **Ignore globs in Code Review profiles** - a YAML profile takes a top-level `ignore` list of repository-relative POSIX globs bounding which paths any lens reviews, so generated code, lockfiles, snapshots and vendored directories are excluded once instead of negated inside every lens
* **An inline workflow hint replaces the onboarding splash** - `tessl agent` opens with a few lines above the prompt rather than a full-screen welcome. Either arrow key opens the list, selecting a workflow fills the prompt with it ready to send, and `/getting-started` reopens the list
* **Review my skills** - a new onboarding workflow that reviews every skill in the repository. It discovers them, lints them deterministically, reviews each against seven dimensions, and returns a ranked plan tied to evidence. It changes no files, and offers `tessl review fix` as an opt-in follow-up
* **Learn from PR feedback needs no input** - the workflow defaults to the last two weeks instead of asking for a pull request number or a time period, states up front that it changes no files, and returns a ranked, capped set of improvements with its coverage limits named
* **Workspace and organization environment variables** - `TESSL_WORKSPACE` and `TESSL_ORG` set the workspace and organization for commands that take `--workspace` and `--org`, so a script does not repeat the flag on every call. An explicit flag always wins, and a project `.env` file cannot set either one
* **Global installs follow organization policy** - globally installed managed requirements converge on the organization's install policy whenever the CLI contacts the backend, not only on an explicit `tessl install -g`. Security thresholds, minimum release age and source restrictions all still apply, and only policy-owned content is removed

### Improvements

* **Clearer failure messages** - installing over Git, self-update, sign-in and eval artifact uploads report failures written for the reader, rather than passing transport wording such as `HTTP 404` through to the terminal
* **Help reads for its audience** - `tessl --help` prints plain text for a person, with usage, grouped commands and flags. The Markdown reference is served only to recognized agent callers, and covers the top level, a group and a leaf alike
* **Code Review setup** - the bundled `tessl/code-review-setup` moves to 0.2.0. The setup skill documents the profile `ignore` list, and the workflows it writes subscribe to pull request review comments, so a mention left on the diff starts a review. Who may request one moves to an `allowed-associations` input rather than a condition the workflow implements itself, and the `uses:` line references the Action's major tag instead of a commit resolved at setup time, so a repository picks up fixes without editing its workflow

### Bug Fixes

* **Conceded findings settle** - a finding the author agrees is real but defers to tracked follow-up work now reconciles as declined, instead of resurfacing as remaining on every later round
* **Verify separates errors from failures** - `tessl change verify` reports a target it could not judge, such as a base ref it cannot resolve, as a distinct `error` outcome rather than a verifier failure. Those targets are listed under "Could Not Be Verified", left out of the failure breakdown, annotated as notices, and still exit non-zero
* **Launch reports the agent's own outcome** - `tessl launch skill --cloud --wait` exits non-zero when the workflow completes but the agent reported a failure, and `tessl launch view` renders the agent-reported outcome for completed runs

## 0.97.0

### New Features

* **Repository YAML profiles** - `tessl code review --profile <path>` now accepts a repository YAML profile as well as the built-in `standard` profile. A profile routes lenses with ordered repository-relative globs, and each specialist's prompt and read-only diff tools are scoped to the paths its lens matched. The path must end in `.yml` or `.yaml`
* **Approval commands** - a trusted repository owner, organization member, or collaborator can mention `@tessl-code-review` with an explicit instruction to approve the pull request. Code Review returns an approved result without running specialist lenses. Ordinary review requests, ambiguous wording, and classification failures continue through a normal review
* **Agent-facing help** - a bare `tessl`, or an explicit `-h`, from a recognized agent now prints a Markdown description of Tessl and how to drive it, generated from the command registry. Claude Code, Cursor, Codex, and Gemini are detected without configuration
* **Readiness reporting** - `tessl doctor --json` and the MCP `status` tool both return a `readiness` field with a `state`, machine-readable `blockers`, and an ordered `nextActions` list. One shared function computes the field so the two surfaces return consistent guidance. Existing fields and `doctor` terminal output are unchanged
* **API key listings as JSON** - `tessl api-key list --json` prints a machine-readable listing, walks every page so a truncated result cannot appear complete, and includes each key's role
* **API key expiry notice** - `tessl api-key create` prints the key's expiry date and days remaining to stderr. A key piped directly into a secret store still reports its 30-day lifetime while stdout stays clean
* **`rotate-ci-key`** - a new bundled skill reads your pipeline to find which secret supplies `TESSL_TOKEN`, selects a rotation tier based on its access, presents a plan naming the source of each fact, and waits for approval before changing a live credential
* **Antigravity skills** - a global install now also symlinks skills into `~/.gemini/config/skills/`, where Antigravity, Antigravity IDE, and Antigravity CLI look. The existing `.agents/skills` path is unchanged

### Improvements

* **Lens limit** - Code Review accepts up to 8 lenses, up from 5. The lenses active for the current change are resolved before the limit applies, so lenses whose globs match nothing do not count against it
* **Error routing** - 9 more precondition failures name what was found and return the command that resolves it. This covers linting a directory with a `SKILL.md` but no manifest, an unlinked `tessl.json`, an invalid package name, an empty scenario directory, a missing fix-run bundle, and the deprecated skill review path. Raw HTTP statuses no longer reach the user
* **Setup handoff** - `tessl init` now wires agent context (`RULES.md` and `AGENTS.md`) even when no plugins are installed, so an agent has context before anything is installed. `tessl login` resumes the interrupted setup step when there is one, and otherwise points to the first unblocked step. Every MCP tool that requires authentication now says so in its description
* **Rejected key guidance** - a rejected API key now reports where the detected CI provider keeps pipeline secrets and which of its CLIs edits them, across 12 providers with a generic fallback. On GitHub Actions, the failure is attached to the workflow file as an annotation and appended to the run summary. Outside CI, the guidance points to a `TESSL_TOKEN` that may be shadowing a healthy session and should be unset instead of rotated
* **`tessl inventory import` scan scope** - scan scope is read from the integration scan policy configured on the organization integration page and enforced when data is persisted. The command no longer fetches a server-side exclusion list to pre-filter the scan. Only `--ignore-repo` narrows a run locally
* **Lens authoring** - the bundled `tessl/code-review-lens-creator` moves to 0.1.1. The lens-authoring skill now directs its reference reads explicitly, and its evaluation tasks no longer prescribe the answer they score
* **Code Review setup** - the bundled `tessl/code-review-setup` moves to 0.1.5. The setup skill now covers YAML profiles for CLI and Action runs, including lens routing, path scoping, the lens limit, local reference safety, and what happens when no lens matches. It also configures the Action with a repository-relative profile path

### Bug Fixes

* **Login link** - the URL printed when the CLI cannot open a browser now always carries its verification URI. Production builds previously omitted it, leaving SSH and development-container logins with a link the capture page could not use
* **MCP shutdown** - Ctrl-C no longer crashes the MCP server, and its capability warning is gone
* **Supervision context** - a live review refreshes the pull-request conversation after the specialists finish and immediately before supervision, so a reply posted while the review was running is taken into account. The run fails closed if the refresh fails or the base or head moves during the review
* **Reviews with no matching lens** - a review whose profile matches none of the changed paths returns an explicit skipped result without running a model, instead of completing in a way that reads as approval
* **Truncation reporting** - prompt truncation is reported whenever any specialist lens truncates, rather than depending on which parallel lens finished last
* **Review cancellation** - the every-commit workflow template no longer lets a comment on a pull request under review cancel the review in progress, and a push to a draft can no longer cancel a requested review. Cancellation is limited to a non-draft `synchronize` event. Workflows generated before this release keep their original template until regenerated

## 0.96.0

### New Features

* **Tessl Code Review (Beta)** - `tessl code review` now appears in the CLI help and runs a multi-lens review over a code change. With no flags it auto-selects the current pull request in a supported CI environment, or otherwise the complete local change; `--pr <ref>` reviews a pull request, and `--base` / `--head` review an explicit Git range with no pull request required
* **Code Review setup through Tessl Agent** - the tessl/code-review-setup plugin now ships embedded in the CLI, so asking Tessl Agent to set up Code Review installs the review workflow directly; the embedded guidance for the older change review flow was removed
* **Lens authoring through Tessl Agent** - the tessl/code-review-lens-creator plugin also ships embedded, so asking Tessl Agent to create a review lens scaffolds one without an install

### Improvements

* **Change review hidden from help** - `tessl change review` no longer appears in help output; Tessl Code Review supersedes it. The command keeps working for existing callers
* **First run points at setup** - the first CLI invocation shows a one-time hint pointing at `tessl init`, and the install scripts hand off with the same guidance. The hint is skipped in CI and on invocations requesting `--json`
* **Error messages direct the user to the command that resolves them** - user-facing errors now route to the command that fixes the reported state, instead of only describing the failure. Additionally, every missing-argument error states which argument was missing
* **Credit refusals are legible mid-run** - when credits run out in the middle of a run, commands surface a clear credit message across CLI and MCP surfaces instead of a generic failure
* **Skill review MCP tools state their scope** - the review\_run, review\_fix, and review\_view MCP tool descriptions now say they review skills, and point to `tessl code review` for reviewing code changes
* **MCP tool descriptions name neighboring workflows** - the status MCP tool description names the adjacent workflows to run next, so an agent picks the following tool without guessing
* **Skill publish validation** - publish validation reports every independently detectable problem in one pass, and skill validation messages use Tessl terms instead of raw schema output

### Bug Fixes

* **Install with a broken project link** - `tessl install` no longer fails when tessl.json links to a project that cannot be resolved, and errors in that state no longer suggest creating a new project
* **Organization selection in workspace create** - `tessl workspace create` resolves the owning organization before creating the workspace: the new `--org` flag takes an organization name or id, and membership in several organizations prompts for a choice
* **Scenario generation from the current directory** - the source argument to `tessl scenario generate` is now optional and defaults to the current working directory
* **API token identity** - `tessl doctor`, `tessl init`, and `tessl mcp start` no longer print a fabricated <api-key@tessl.io> email when authenticated with a TESSL\_TOKEN API key; they identify the session by the masked token instead
* **Corrupt tessl.json errors name the file** - a JSON parse failure while reading tessl.json now names tessl.json in the error message
* **Live documentation links** - `tessl skill new` and `tessl plugin new` print working docs.tessl.io links in their next-step guidance
* **No memory writes on read-only turns** - Tessl Agent no longer creates or updates .tessl/memory files during turns that make no changes

## 0.95.0

### New Features

* **Delete an API key by name** - `tessl api-key delete <name-or-id>` accepts either the key name or its ID, matching the pattern already used elsewhere. Interactive selection still works when no argument is given
* **Skill removal guidance** - asking to unpublish, archive or delete a skill now explains that skills are published inside a plugin and lists the three real removal paths, and `tessl skill --help` carries the same tip
* **Uncollected file warnings in review** - `tessl review run` and `tessl review fix` now name the skill files that were not sent to the reviewer, so content that scored low because it never reached the judge is visible
* **Out-of-workspace path warnings** - `tessl scenario view` flags generated scenarios whose task or criteria reference a home-directory or absolute path, which can never score above zero because the grader only sees the workspace

### Improvements

* **Credit warnings on stderr** - credit warnings print to stderr instead of stdout, so they no longer sit inside a command's own output. `TESSL_CREDIT_WARNING_STREAM` accepts `off`, `stderr` or `stdout` to override the choice
* **Upgrade path in the agent credit footer** - the out-of-credits and over-limit states now point at `/usage`, where the balance and the upgrade link live
* **Pricing links** - credit and upgrade calls to action point at `/pricing`
* **Credit checks on the MCP surface** - the MCP server now runs the same credit preflight as the CLI, closing a path where a blocked organization could still spend credits
* **Score composition in review output** - review results show how a score was composed, per judge, rather than only a final number
* **Organization default model for agent sessions** - `tessl agent` picks up an organization's configured session model when no `--model` flag and no persisted default are set
* **Review hands off to fix** - after a quality review, `tessl review run` points at `tessl review fix` as the next step when the review found something to act on. The MCP `review_view` tool carries the same signal as a `nextStep` field
* **Fix runs stop at a reachable score** - the default fix threshold drops from 100 to 90, so a run can finish early instead of burning every improve iteration on a target a review almost never hits. The MCP surface no longer holds its own defaults and reports the config the server resolved

### Bug Fixes

* Review scores render against the rubric's own scale instead of a hardcoded denominator of 3, so a 1 to 5 rubric no longer reports the wrong total
* `tessl skill review --json` carries the deprecation notice in its payload, and the post-apply hint points at `tessl review run` instead of the deprecated command
* A logged-out global install of registry content is refused when the global context is managed by an organization policy, closing a path where logging out skipped the organization's managed requirements
* `tessl install -g` labels the resolved policy scope `org:` instead of mislabeling it `workspace:`
* Auto permission mode no longer fails at startup for organizations whose model catalog omits `gpt-5.4-mini`. The classifier falls back to the resolved session model
* Reasoning models in the `gpt-5.6` family route through the OpenAI Responses API, fixing a gateway 400 when a request carried both a function tool and a reasoning effort
* Skill inventory upload accepts `internal` as a repository visibility, so GitHub Enterprise organization-internal repositories are no longer rejected

## 0.94.0

### New Features

* **Credit usage meter** - `tessl org usage` shows your organization's plan, credits used against the allowance, and when the current window started. Pass `--org` to name an organization, `--json` for the raw credit account
* **`/usage` and `/cost` in the agent** - `/usage` shows the same meter without leaving the session, `/cost` shows what the session has cost in credits with the token counts underneath, and a `credits: N left` segment on the footer refreshes each turn
* **Commands report what a run cost** - every command that waits for a run prints the credits it consumed once billing settles, and `... view` commands report the cost of the run they inspect. The figure comes from the server, so it matches what you are billed
* **Credit guardrails on billable commands** - a one-line notice when you are over your allowance but still allowed to proceed, and a hard stop with an upgrade link when you are not. Both are suppressed under `--json` so structured output stays parseable
* **Scoped reviews** - new repeatable `--path` flag on `tessl change review` restricts the review to files matching a pathspec

### Improvements

* **Security grades read Tessl's own levels** - `tessl search`, `tessl tile info` and the security review output now render the Tessl security level and per-finding severity. A clean result reads "Passed", an unscanned one reads "N/A", low-severity findings group into their own section below higher severities, and an unrecognized level renders as-is instead of failing. `--json` keeps the previous fields alongside the new ones
* **Eval fixtures are bounded to the run** - `tessl eval run` and publish-time evals now refuse scenario fixtures and packed symlinks that resolve outside the run's repository or scenarios boundary, so an eval can no longer archive files from elsewhere on the machine. `tessl eval lint` reports the same. Out-of-boundary directory fixtures and symlinks are opt-in via `tessl eval run --allow-unsafe-fixture-paths`, or `--allow-unsafe-eval-fixture-paths` on `tessl skill publish` and `tessl plugin publish`. A scenario `include` must always resolve inside its own scenario, with no override
* **A project's `.env` can no longer redirect the CLI's own state** - `TESSL_GLOBAL_DIR`, `TESSL_BINARY_BASE_URL`, `TESSL_AUTO_UPDATE_INTERVAL_MINUTES` and `TESSL_DISABLE_UPDATE` are ignored when they come from a `.env` file in the working directory. Values set in your shell are unaffected
* **Workspace resolved from a plugin manifest** - working inside a plugin directory that has `.tessl-plugin/plugin.json` but no `tessl.json` no longer prompts for `--workspace`; the workspace is read from the plugin name
* **Declared skills scanned as real files** - `tessl inventory import` now scans skills declared in `tile.json` / `plugin.json` the same way it scans any other `SKILL.md` in the repository, so they carry their body and frontmatter instead of being recorded as bare references. Their names now come from frontmatter (or the containing directory) rather than being prefixed with the plugin name, and a declared skill whose file is missing from the scanned tree is no longer recorded at all
* **`workspace_list` reports what you can do** - the MCP tool now returns `allowedActions` per workspace, so a caller can filter to the workspaces it has permission to act on before trying

### Bug Fixes

* Failing commands print one error line. Previously the same message appeared twice, with two different cross glyphs
* `tessl change verify --recursive --github` now emits GitHub Actions annotations for every root. Warning-level findings from a recursive run previously only appeared in the raw job log
* Out-of-credits and other expected failures from the `review_run`, `review_fix` and `uninstall` MCP tools are reported as errors rather than crashes
* The MCP `workspace_list_members` tool explains that your role cannot view members, instead of failing generically
* A cached version entry dated in the future is treated as expired, so it can no longer suppress the update check indefinitely
* The path-containment check no longer misreads directory names that begin with two dots, and anchors relative targets on the boundary root

## 0.93.0

### New Features

* **Severity grading on review findings** - `tessl change review` now grades every finding nit / minor / major / critical by default; pass `--no-grade` to turn it off
* **Finding attribution** - each `tessl change review` finding names the reviewer skill that produced it
* **Explicit review range** - new `--head` flag on `tessl change review` to review a specific commit range instead of the inferred one
* **Publish-access filter** - new `--has-publish-access` flag on `tessl workspace list` to show only workspaces you can publish to
* **Plugin Creator in Tessl Agent** - the agent now ships with the Plugin Creator skills for authoring plugins

### Improvements

* **Symlink guard for MCP config** - the CLI warns and asks for confirmation before writing MCP configuration through a symlink that points outside your project
* **Local-execution notice** - installing a plugin that ships an MCP command which runs on your machine now tells you at install time
* **Global installs respect `--agent`** - the `--agent` scope is honored when installing context globally
* **`CLAUDE_CONFIG_DIR` support** - installing global Claude Code context now honors `CLAUDE_CONFIG_DIR`
* **Clearer permission prompt** - the session-scope option in the agent permission prompt has a clearer label
* **Clearer MCP review tools** - `review_run`, `review_view`, and `review_fix` describe what they do and what to expect
* **Keys revoked on logout** - `tessl logout` revokes the device's LLM keys

### Bug Fixes

* Fixed concurrent auto-updates leaving behind a dangling symlink
* Fixed a race when two installs ran at once by staging npm installer downloads
* `tessl change verify` now fetches the diff base itself in shallow and CI checkouts instead of failing
* Install sync no longer fails when a skill entry is missing from the manifest
* Search results now render when ranking data is unavailable
* Review scores now use the rubric scale reported by the API instead of assuming a fixed scale

### Removals

* `tessl context library` and its `query_library_docs` MCP tool have been removed. They were deprecated and have no replacement.

## 0.92.0

### New Features

* **Hooks in plugins** - Plugins can now declare cross-agent hooks via `hooks` / `nativeHooks` in `plugin.json`. See the [plugin configuration docs](https://docs.tessl.io/reference/configuration#hooks).

### Improvements

* **Signed release verification** - CLI installation and self-update now verify against a cryptographically signed release manifest before extraction.

## 0.91.0

### New Features

* **Agent-readiness report** - run `/agent-ready` in the `tessl` agent to scan your repository and surface what to improve.
* **`tessl launch resume`** - resume an interrupted or completed cloud launch run.
* **Signed release verification** - the installer verifies the release signature before installing.

### Improvements

* **Agent works without native search tools** - uses `fd` / `rg` when available and falls back to a basic code implementation when they are not installed.
* **Verify cache relocated** - now stored at `~/.tessl/verify-cache`, so cached results are shared across projects instead of living in each checkout.
* **Auto-detect workspace** - `tessl review run` / `review fix` read the workspace from `tessl.json` instead of always prompting.
* **`tessl launch list`** now shows the workspace and the run's creator.
* **`tessl eval list`** consolidates injected context into a single column.
* **Credit errors link to top-up** - an out-of-credits error now points to the upgrade / top-up flow.
* **Explicit, persistent agent setup** - `tessl init` now records selected agents in `tessl.json` for reproducible installs across machines. Re-run `tessl init` on an existing project to update the list.
* **Clearer permission prompts** - prompts are deduped per tool call, bash paths are classified by read/write/delete, external and sensitive-path actions route through the auto classifier, and auto-mode prompt wording is combined.

### Changes

* **Separate `--agent` and `--model` flags** - compound `--agent <agent>:<model>` tuples are now split into `--agent <agent> --model <model>`. The old form still works but is deprecated and warns.

### Bug Fixes

* Installer validates the remote release version before installing.
* Generic hook command/args aligned to the `exec` / `shell` forms.

## 0.90.0

### Breaking Changes

* `tessl review <target>` (without `run`) no longer runs a review - it now shows the `review` command group's help instead. Use `tessl review run <target>` (or `tessl review run quality <target>`).

### New Features

* **`tessl review run quality` / `tessl review run security`** - `tessl review run` is now a subcommand group: `quality` (the default, unchanged behavior) scores a skill against a rubric, and `security` runs a Snyk-powered security scan - replacing the standalone `tessl security-review` command. `tessl review list`, `view`, and `retry` now work across both kinds from a single unified history, with a `--fail-on` flag mirroring `--threshold` for security scans. `tessl review run <target>` continues to work exactly as before - it's shorthand for `tessl review run quality <target>`.
* **Clone skills from GitLab and Bitbucket sources** - Skill installation and inventory scanning now support GitLab and Bitbucket repository sources, not just GitHub.

### Bug Fixes

* Fixed `tessl agent` ignoring a saved default model from `settings.json` on startup.
* `tessl agent` - Fixed session approval matching for external paths when multiple patterns are involved.

## 0.89.0

### New Features

* **`tessl agent` out in Open Beta** - It helps you build and optimize your software factory one workflow at a time.
* **`tessl change` commands to aid with automated code reviews**
  * **`tessl change risk`** - Assess PR/diff risk and decide whether human review is required.
  * **`tessl change verify`** - Verify code against configured verifiers.
  * **`tessl change review`** - Review the current diff with reviewer skills and emit structured review data.
  * Use `tessl agent` to set them up!
* **`tessl launch skill`** - Run skills using your preferred agent in order to automate parts of your workflow with skills.
* **`tessl mcp proxy`** - Connect to MCP servers through an MCP gateway set-up in a workspace’s settings.

### Improvements

* **Clearer error when `--skill` can't apply** - `tessl install --skill <name>` now fails fast with an actionable message when given zero sources or more than one source, instead of silently installing nothing.
* **Friendlier model-lock message on eval runs** - Selecting a non-default model on a Free plan now produces a single, clear message explaining how to proceed, instead of a raw `403 Forbidden`.

### Bug Fixes

* Fixed relative `file:` sources (e.g. `tessl install file:../my-source --global`) failing to resolve; they now resolve against your current working directory as expected.
* Fixed a single unsupported entry in a plugin's `.mcp.json` failing the whole file at publish and install time; unsupported entries are now skipped and the rest of the plugin installs.

## 0.88.2

### Bug Fixes

* **npm package** - fixup README.md file

## 0.88.1

### Bug Fixes

* **Global plugin MCP servers** - `tessl install --global` now writes a plugin's MCP server config to each agent's real user-level config.

## 0.88.0

### New Features

* **Plugin MCP Servers** - Plugins can now declare MCP servers in a bundled `.mcp.json` at the package root.
* **Publish Through Symlinks** - `tessl skill publish` and `tessl plugin publish` accept an opt-in `--resolve-symlinks` flag that copies a symlink target's bytes into the published tarball (within a safe boundary), so authors can reuse files from elsewhere in the repo.

### Improvements

* **Workspace Scoping for Reviews** - `tessl review view` and `tessl review retry` now accept `--workspace` / `-w`, and `--last` resolves the most recent run within that workspace.
* **Publish OIDC Warning** - When publishing from GitHub Actions without an OIDC token permission, the CLI now warns instead of failing opaquely.

### Bug Fixes

* Fixed `tessl skill review --threshold 0` incorrectly emitting a "Skill validation failed" error.

## 0.87.0

### Breaking Changes

* **Skill review now requires a Tessl account.** Both `tessl review` and the older `tessl skill review` now require authentication: sign in with `tessl login`, or set a `TESSL_TOKEN` API key in the environment for CI. Reviews run on Tessl's servers, so anonymous review runs are no longer supported.

### Deprecations

* **`tessl skill review` is deprecated - use `tessl review` instead.** `tessl review` (alias `tessl review run`) is the replacement: an async, server-side review with companion `view`, `list`, `retry`, and `fix` commands. The old command still runs for now but prints a deprecation warning on every run and will be removed in a future release. Replace `tessl skill review <path>` with `tessl review <path>`.

### New Features

* **`tessl review` is now fully launched as an end-to-end workflow** - review runs are first-class in the CLI with `run` as the default subcommand, and companion commands to view, retry, and fix reviews directly from the terminal.
* **Flexible reviewer selection** - you can target a specific reviewer with `--review-plugin` or use your workspace's default reviewer configuration.

### Improvements

* **Richer review output for debugging and automation** - `tessl review view` now renders dynamic judges per run, and review JSON includes reviewer/plugin identity metadata.
* **Clearer `tessl eval view` output** - run labels are shown and scenarios are sorted for easier reading.
* **More informative `tessl eval run` start output** - pre-run context details are surfaced before execution.

### Bug Fixes

* **Review command reliability fixes** - `tessl review list`, `view`, and `retry` now resolve workspace names correctly, and `--json` without `--workspace` now returns a clear error.
* Fixed completed scenario counts in multi-solution eval runs.
* Fixed evaluator failures caused by repeated plugin installation arguments.

## 0.86.0

### New Features

* **Customise the eval-scenarios run when publishing** - added `--eval-scenarios <scenario-search-path>` to `tessl plugin publish` / `tessl skill publish`. The default is `./evals` in the directory of the plugin being published.

### Improvements

* **Default eval model** is now DeepSeek v4 Flash.
* **`tessl eval list --context-type`** - filter eval runs by the type of context injected into scenarios for the "with context" variant.
* **Richer `tessl eval view`** - clearer variant labels, an injected-context block, scenario paths, and repo/plugin provenance.
* `tessl eval run` echoes the scenario search directory and count before starting.
* Commit-fixture uploads are grouped and deduped to remove some cases where redundant context was uploaded.
* Install source policy: git-source allowlist, with denial messages that name the offending source.
* You're prompted to accept pending invites before a personal org is created.

## 0.85.0

### Breaking Changes

* **tessl eval run --agent is now single-valued** - Previously `--agent` was repeatable and each value created a separate eval run. It now accepts a single agent; run the command once per agent to compare.
* **Removed legacy eval-run context flags** - `--variant`, `--context-ref`, and `--context-pattern` have been removed from `tessl eval run` in favour of the new `--context` selection (see below).

### New Features

* **Skill Inventory** - Skill Inventory gives you a living map of every skill in your GitHub org - what exists, where it's used, and where you're duplicating effort. Run the initial import with `tessl inventory import` and it scans your repos, collects your skills and manifests, and gives you a snapshot of your whole inventory. Run it again any time - each scan is diffed, so you get a living view of what's new, unchanged, and removed over time.
* **Context Selection for tessl eval run** - A single `--context <path>` front door now picks the context under test, with `--context-commit` to source it from a commit and `--skill` (repeatable) to narrow a local plugin to specific skills.
* **Control Baseline by Default** - `tessl eval run` now always runs a control variant to compare against, with `--skip-baseline` to opt out.
* **Secrets injection for plugin evals** - `tessl eval run` now accepts `--env-file <path>`. Variables in the file are encrypted client-side and delivered into the eval sandbox, where they are available to `setup.sh` scripts and the agent. See [Passing secrets to an evaluation](/improving-your-skills/evaluate-skill-quality-using-scenarios#passing-secrets-to-an-evaluation).
* **--child Flag for Nested Projects** - tessl project create now requires `--child` to create a project nested inside an existing one, and blocks with a clear explanation when a parent project is detected in an ancestor directory.
* **tessl version** - Bump a plugin's version locally without publishing, like `npm version`. Pass `patch`, `minor`, or `major` to increment the current version following semver, or an explicit version (e.g. `tessl version 2.1.0`) to set it directly. Updates the plugin manifest in place; an optional path argument targets a directory other than the current one.

### Improvements

* **Clearer Install Output** - `tessl install` now shows only the dependencies you explicitly requested on the success line; incidental re-installs are summarised as a dim `Synced: N count`.
* **tessl scenario generate workspace resolution** - Workspace is now resolved from your linked Tessl project (`tessl.json`), the `--workspace` flag, or an interactive prompt — instead of being derived from the tile manifest name, which caused confusing 403 errors when the manifest used the `local/ prefix`. The `--workspace` flag is now accepted for tile-based generation.

## 0.84.0

### New Features

* **Bootstrap Project Create** - `tessl project create` now works in directories that don't yet have a `tessl.json`, bootstrapping it as part of the create flow.
* **Personal GitHub Account in Skill Inventory Import** - `tessl skill inventory import` can now authenticate with a personal GitHub account, not just org-level credentials.
* **Plugin Marketplace Discovery** - Tessl now classifies `.claude-plugin/marketplace.json` as a first-party manifest, picking up plugins from that location.

### Improvements

* **Warning for Large-Scale Eval Runs** - `tessl eval run` now prompts a confirmation before launching large eval batches.
* **Plan-Entitlement-Gated Model and Agent Selection** - model and agent selection respects plan entitlements with clearer messages when a selection isn't available.
* **Age-Gated Registry Sources Fallback** - registry sources blocked by release-age policy can now resolve to an older fallback source rather than hard-blocking.

### Bug Fixes

* Fixed `tessl install` to fast-fail on private or missing git repositories instead of silently succeeding with a hidden failure.
* Fixed the judge-results display to show the correct score denominator in both CLI and frontend.

### Windows installer (new distribution channel)

Signed Windows installers are now available for direct download. The Inno installer wraps a signed `tessl.exe` bundled with `TESSL_DISABLE_UPDATE` (winget will manage updates):

* <https://install.tessl.io/installers-windows/0.84.0/TesslSetup-0.84.0-x64.exe>
* <https://install.tessl.io/installers-windows/0.84.0/TesslSetup-0.84.0-arm64.exe>

The signed package will be installable directly via `winget install tessl.tessl` as soon as it will be approved by Microsoft

## 0.83.0

### New Features

* \*\*Jujutsu Repo Support - `tessl project repair` now detects and handles `jj`-managed repositories alongside Git.
* Finer Eval Run Controls - `tessl eval run` adds two independent opt-outs: `--skip-forced-context-activation` and `--skip-scoring`. Use them on their own or together to control whether the run forces context activation and whether the rubric scorer fires.

### Improvements

* Solver and Scorer Flag Deprecation - The `--solver` and `--scorer` flags on tessl eval run are now hidden but still functional, with a one-line deprecation warning; prefer the new `--skip-*` flags.

### Bug Fixes

* \*\*Removed a vendor-native plugin-manifest ingestion path that could pick up stale manifests outside the expected resolution flow.

## 0.82.0

### Improvements

* **Install Policy Enforcement** - `tessl install` now honors org-configured release-age and commit-age policies. For registry sources, the CLI automatically falls back to an older version that satisfies the policy (with a clear reason in the install output) or hard-blocks if no version qualifies. For git sources, it resolves to the most recent commit old enough to satisfy the minimum commit-age policy instead of installing HEAD.
* **Policy-Aware `tessl outdated`** - Versions blocked by a release-age policy are now annotated as `[release age policy]` in the update table, and git sources show the latest age-compliant commit as the recommended update so you can see what will (and won't) be installed before running `tessl update`.

## 0.81.3

### Improvements

* **Eval Failure Reasons** - `eval run` and `eval view` now display the reason when a run fails (e.g. quota exceeded), instead of a bare failure marker
* **Eval Default Model Notice** - A one-time notice informs you that the default eval model is now GLM 5.1, with guidance on selecting a different model
* **Resilient Eval Polling** - `eval run` now rides out transient server errors and network blips while polling, instead of reporting a completed run as failed
* **Clearer Validation Errors** - Manifest schema errors are now rendered in a readable format instead of a raw JSON dump
* **MCP Tool Updates** - The `new_tile` MCP tool is renamed to `new_plugin`, and the `outdated` tool now accepts a `projectDir` parameter to check a specific directory

### Bug Fixes

* Fixed `plugin pack` failing with a missing `tile.json` error for plugins created with `plugin new`; migrated plugins also no longer pack from the stale legacy manifest
* Fixed `eval run` evaluating the stale legacy `tile.json` instead of `.tessl-plugin/plugin.json` when both manifests are present
* Publishing a migrated plugin now errors if `tile.json` declares `private: true` but `plugin.json` omits it, preventing a private plugin from accidentally going public
* Fixed broken documentation links shown after `plugin new` and `skill new`
* Removed misleading advice pointing at `tile.json` from the "no content" validation error

## 0.81.2

### Bug Fixes

* **`scenario generate`: Clearer Error When No Manifest Found** — Running `tessl scenario generate` in a directory with no manifest previously crashed with a confusing `ENOENT: tile.json` error. It now shows a clear message pointing you to add a `.tessl-plugin/plugin.json`.

## 0.81.1

### Bug Fixes

* Fixed `skill publish` incorrectly triggering the import wizard when `plugin.json` already exists
* Fixed `skill publish --bump` not writing the bumped version to `plugin.json`, causing the old version to be published
* Fixed `skill publish` pre-publish version checks reading from `tile.json` instead of `plugin.json`

## 0.81.0

### New Features

* **Tiles are now Plugins** - This release completes the switch from Tiles to Plugins. The CLI now uses "plugin" across every command, help listing, flag description, and message. For more information on how to migrate from Tiles to Plugins, [there is a guide in the documentation](https://github.com/tesslio/monorepo/tree/main/apps/docs/use/tile-to-plugin-migration.md).
* **Full moderation status in `plugin info`** - `plugin info` now shows the complete moderation state for a published version (pass, pending, skipped, fail, or error).

### Improvements

* **`ls` shortcut** - `tessl ls` now works as a shortcut for `tessl list`, mirroring the existing `i` → `install` shortcut.
* **Workspace-scoped uploads** - Eval-related uploads (scenario tarballs, plugin archives, repo archives) are now scoped to the active workspace, so teammates in the same workspace can access uploads created by other members.

### Bug Fixes

* Eval run links now point directly to workspace-scoped URLs, skipping the redirect members previously hit when clicking through.
* `scenario generate` now works in plugin-only directories (a `.tessl-plugin/plugin.json` with no `tile.json`), which previously failed with a "tile.json not found" error.
* The CLI no longer fails to start when its log file can't be written (e.g. in sandboxed or read-only environments) — file logging is now best-effort.

## 0.80.0

### New Features

* **Eval Enablement Checks** - `tessl scenario generate`, `tessl tile publish`, and `tessl skill publish` now pre-flight check whether the target workspace has evals enabled. Scenario generation fails fast with a clear message; publish still succeeds but cleanly skips the eval upload with a single info line.

### Improvements

* **Cleaner Publish Output** - `tessl tile publish` no longer prints validation warnings twice (once from pre-publish validate, once from pack).
* **Clearer Forbidden Errors** - When creating a project fails because the workspace doesn't exist or you lack permission, the CLI now shows an actionable message instead of a generic `403 Forbidden`.
* **Skill Install Paths** - Skills are no longer written under `.gemini/skills`; installation routes through `.agents/skills`, and any legacy `tessl:*` / `tessl__*` entries left in `.gemini/skills` are cleaned up automatically.

### Bug Fixes

* `tessl tile new` now preserves the rule description you provide instead of overwriting it with a `TODO` placeholder.
* Generated `RULES.md` now ends with a trailing newline.

## 0.79.1

### Improvements

* **Clearer Guidance for Missing Project Links** - When a linked Tessl project can't be found or isn't accessible, the CLI now surfaces actionable next steps with specific `tessl project create` and `tessl project repair` commands instead of a raw 404. The `tessl eval run` help text now also explains that eval runs are saved to a Tessl project.

## 0.79.0

### New Features

* **OpenHands agent support (alpha)** — `tessl init`, `tessl install`, and `tessl run` now recognise OpenHands. Auto-detection follows the standard project → home → PATH ladder; OpenHands is excluded from the interactive picker while in alpha but can be selected explicitly via `--agents openhands`.
* **`tessl search --global`** — install search results into `~/.tessl/` without requiring a project to be initialised. Short form `g` available.
* **`tessl eval run --list-agents`** — print the supported `<agent>:<model>` catalogue (with the default marked) without running an eval. Supports `--json` for scripting.
* **`tessl project` -** Tessl now supports workspace-backed projects in the CLI, giving your local work a persistent project identity for evals and other project-based workflows. Use `tessl project create` to create a project, `tessl project link` to connect an existing one, and `tessl project repair` to fix broken project links.
* **`tessl eval run --scorer-agent`** — override the agent and model used for the score step (as `agent:model`), while the solver agent stays unchanged. `tessl eval view` shows the override under a `Scored by:` line.
* **`tessl eval run --quality-check`** — opt-in flag that asks the backend's LLM judge to filter out misleading scenarios (low feasibility, rubric leakage, low value) before solve and score. Excluded scenarios show up under a red `✗ Excluded by quality check` line in `tessl eval view`. `tessl tile publish --with-scenario-quality-check` enables the same filter for the eval that runs as part of publishing.

### Improvements

* **Simpler `tessl skill import` / `skill new`** — no longer prompts for workspace or visibility; defaults to `local` workspace and `private` visibility. Pass flags explicitly to override.
* **Scenario config (`scenario.json`)** — declarative working-directory setup for evals. Declare `fixtures` to install (commit or directory), `include` paths to copy in verbatim, and `setup` scripts to run after install. Conventional defaults (`resources/` auto-included, `setup.sh` auto-run) mean most scenarios need no `scenario.json` at all. `tessl scenario generate` now produces a `scenario.json` for every generated scenario, plus any supporting input state (e.g. `resources/`) when the source commit needs it. Setup scripts are off by default per workspace — contact Tessl to enable.
* **Cleaner install error messages** — local paths missing the `file:` prefix now surface a clear validation error instead of a generic parse failure.
* **Softer security-review copy** — surfaces showing a per-skill security status now display "No security review available." instead of "Security review has not been run yet."
* `tessl skill review` **includes referenced skill content** — skill reviews now include all referenced content and scripts for logged-in users.

### Bug Fixes

* **Install exits non-zero on failure** — `tessl install` and `tessl install --global` now reliably propagate tile install failures as command failures (non-zero exit code).
* **`tessl scenario generate <relative-path>`** — relative paths like `tiles/foo` are now correctly routed to the tile flow when the directory exists locally, instead of misrouting to the repo flow and erroring on missing `-commits`/`-prs`.
* **`tessl tile lint`** — `@decorator` tokens in markdown headings and YAML frontmatter are no longer treated as hard-link references and no longer produce false-positive lint warnings.
* **MCP install bootstrap message** — the "Initialising new tessl project at …" message no longer prints during bulk sync installs that don't actually write `tessl.json`.
* **Experimental agent detection** — explicit project-level usage of experimental agents (e.g. OpenClaw workspace markers) is now respected ahead of the experimental gate, and the default install flow no longer force-enables experimental agents.

## 0.78.0

### New Features

* **Activation Solver for Evals** - `tessl eval run --solver=activation` measures which skills activate for each scenario, and `tessl eval view` renders the results in a per-scenario activation table
* **Set Project Name on Init** - `tessl init` accepts a new `--name` flag to set the project name in `tessl.json` at creation time
* **Target Org on Workspace Create** - `tessl workspace create` now accepts a `--org-id` flag to associate a workspace with a specific organization
* **List Organizations** - New `tessl org list` command displays the organizations you belong to
* **Scenario Quality Checks for Evals** - `tessl eval run --quality-check` will filter out scenarios that would give misleading results, based on a quality assessment that looks for things like “criteria leaking into the task” or “task duplicating information from the skill it is meant to exercise”

### Improvements

* Clearer Eval Run Status - Pending and in-progress eval and generation runs render as yellow "In Progress" with elapsed time, instead of an ambiguous "Pending"
* Better Invalid Manifest Errors - tessl eval run against a directory with a malformed tile.json now reports the manifest error directly with a hint to fix it, instead of falling through to the scenarios flow
* Removed Eval Compare - tessl eval compare has been removed. Use tessl eval view and tessl eval list instead
* Global Install from Search - `tessl search` now supports `--global` so selecting a result installs it to `~/.tessl/` instead of the current project

### Bug Fixes

* Fixed tile rule creation to safely handle prompts containing `/` so they no longer create unintended nested paths in the generated rule files

## 0.77.0

### New Features

* **Experimental Windows Support** - The Tessl CLI is now available on Windows as an experimental release. Install it with PowerShell:

  ```powershell
  irm https://install.tessl.io/scripts/latest.ps1 | iex
  ```
* **Eval Setup Scripts** - Eval scenarios support a new `setup` field in `scenario.json` — an ordered array of script paths that run after fixtures and includes are installed, before the solver agent starts.

### Improvements

* **Cleaner Top-Level Help** - Simplified top-level CLI help.

## 0.76.0

### New Features

* **Eval Progress Bars** — `eval run` and `eval view` now show real-time per-scenario progress bars with colour-coded status (complete, active, queued)
* **Directory Fixtures in Evals** — Scenarios can now declare `include` paths for directory-based fixtures, alongside existing commit-based fixtures.
* **"latest" Dependency Version** — Use `"latest"` as a dependency version in `tessl.json` to always resolve the most recent published version
* **Uninstall Individual Skills** — `tessl uninstall --skill <name>` removes specific skills from a dependency instead of removing the entire tile
* **Verifiers in Tile Packing** — The `verifiers/` directory is now included when packing tiles (when evals are enabled)
* **Failed Security Review Visibility** — Security reviews that failed to run are now shown as "failed" instead of appearing as "pending"

### Improvements

* **Project Name from Directory** — `tessl init` now derives the project name from the directory basename instead of using a generic default
* **Archive Without Version** — `tessl archive` now works without a version suffix, allowing you to archive all versions of a tile at once
* **Eval Agent/Model Display** — `eval view` now shows the agent and model for all eval run types
* **Workspace Commands** — `-user` flag renamed to `-username` , `-user` still works with a deprecation warning
* **Help Text Corrections** — Improved descriptions for `install --skip-init`, `tile pack`, `skill import`, and `config set`

### Bug Fixes

* Fixed eval scenario path resolution when base directory is "."
* Fixed eval git diff ignoring user's custom diff tool configuration
* Fixed object metadata serialization in eval debug output

### Breaking Changes

* **Removed `--project-dependencies`** — The `--project-dependencies` flag has been removed from `tessl install` and `tessl init`. Dependency scanning and automatic tile matching are no longer part of the install flow. Use `tessl search` to find relevant tiles instead.

## 0.75.0

### New Features

* **API Key Management** — New `api-key` command group with `create`, `list`, and `delete` subcommands for managing workspace-scoped API keys. Supports both interactive and non-interactive (scripting) modes
* **Auto-Select Single Workspace** — When you're a member of only one workspace, the CLI now skips the workspace selection prompt and selects it automatically

### Breaking Changes

* **`auth token` Removed** — The `auth token` command has been removed. Use the new `api-key create`, `api-key list`, and `api-key delete` commands instead for workspace-scoped API key management

## 0.74.0

### New Features

* **Machine-Readable Search**: `tessl search --json` flag outputs results in JSON format for scripting and automation
* **Skill Review Threshold**: `tessl skill review` now supports a `--threshold` option: when the score is below this threshold, the command will fail - helpful for CI use cases
* **Scores in Search & Info**: Search results and `tessl tile info` now display quality scores and security review details

### Improvements

* **Piped Token Output**: `tessl auth token` now suppresses the spinner and hints when output is piped, making it easier to use in scripts
* **Better Error Display**: Failed operations now render a red cross indicator for clearer error visibility
* **Search Column Alignment**: Interactive search results now have properly aligned columns
* **Remove Steering to Use `query_library_docs` MCP tool**: It was getting in the way of skill activation from some users. You can manually opt-in to install the rules yourself with `tessl install tessl/cli-setup`

### Bug Fixes

* Fixed skill review API calls failing due to stale authentication tokens
* Fixed eval commands resolving git repo root incorrectly when run from subdirectories

## 0.73.0

### **New Features**

* **Security Reviews on Install** - `tessl install` and `tessl update` now show security review results before installation. CRITICAL/RISKY findings require confirmation to proceed, ADVISORY findings show an advisory after install, and unscanned items display a warning. Use `-dangerously-ignore-security` to bypass prompts in CI/scripted contexts
* **Commit-Pinned Installs** - `tessl install` now accepts `github:<repo>@<sha>` and full GitHub commit URLs, allowing you to install a skill at a specific commit. A post-install hint directs to `tessl update` for future updates
* **Publish `--bump` Flag** - `tessl tile publish` and `tessl skill publish` now accept `--bump patch|minor|major` to auto-increment the version when the current version already exists in the registry
* **`.agents/skills` Directory Support** - Install skills in `.agents/skills` alongside other requested agents since that’s the folder many tools are aligning with

### **Improvements**

* **Grouped Search Results** - `tessl search` now groups results by type (Tiles / Skills) with section headers when both types are present

### **Bug Fixes**

* Fixed `tessl skill new` showing interactive prompts even when all required flags (`-name`, `-description`, `-workspace`) are provided
* Fixed MCP server processes becoming orphaned when the parent process disconnects (stdin closes)

## 0.72.0

### **New Features**

* **Pre-Publish Validation & Dry Run** - Tile and skill publish commands now validate permissions, workspace, and version availability before publishing, with clear pass/fail output. Use `--dry-run` to check without actually publishing.
* **Security Scores in Search** - Search results now display security assessment levels (Passed, Caution, Risky, Critical) alongside tile scores

### **Improvements**

* **Smarter Eval Scenario Generation** - Existing scenarios are now considered when generating new ones, avoiding duplicates
* **Large File Exclusion in Eval Archives** - Files over 10MB are automatically excluded from repo archives during eval runs
* **Eval Commit Ref Validation** - Commit refs are validated before archive creation, with clear error messages for missing refs

## 0.71.0

### New Features

* **Support for Copilot in VSCode** — Skills, rules, and our MCP server can be automatically set-up for vscode
* **Eval Run Labels** — New `--label` / `-l` flag on `tessl eval run` lets you attach a short description to a run. Labels appear in `tessl eval list` and the Tessl web UI, making it easier to identify what you were testing across multiple runs.
* **Eval Variant Selection** — New `--variant` flag on `tessl eval run` lets you run only a specific variant. Pass `--variant without-context` to run the baseline only (useful for iterating on scenario quality without affecting the with-context comparison), or `--variant with-context` to skip the baseline.
* **Local Repo Eval Runs** — Project evals can now run against local git repos without SCM integration; the CLI archives and uploads the repo automatically
* **Smarter Scenario Generation** — `tessl eval generate` now avoids duplicating existing scenarios when generating new ones; `--count` specifies how many new scenarios to add

### Bug Fixes

* Allow installation of skills from unknown dot folders in GitHub repositories (e.g., `.agent-skills/`)

## 0.70.1

### Improvements

* **Non-Interactive Mode for Evals & Scenarios** — `tessl eval run` and `tessl scenario generate` now print the run/generation ID and URL immediately when running non-interactively, instead of polling for completion
* **Clearer Help Text** — `tessl eval run` and `tessl scenario generate` now show which flags are mode-specific, and invalid flag combinations are rejected with helpful messages

### Bug Fixes

* **Fixed install --watch-local exiting prematurely** — the process now stays alive until Ctrl+C and updated output to show relative paths rather than absolute ones
* **Fixed installation from `file:`** **source to exclude .git directories** - It now installs the same files that would be captured by `tessl tile pack` or `tessl tile publish`

## 0.70.0

### New Features

* **Local Tile Watching** — New `--watch-local` flag on `tessl install` watches local file-source tiles for changes and automatically reinstalls tiles, streamlining the tile development workflow
* **Search Score Display** — `tessl search` results now show aggregate quality and impact scores with color-coded badges
* **Scenario Generation from PRs** — New `--prs` flag on tessl scenario generate accepts PR numbers as an alternative to `--commits`
* **Multiple Scenario Downloads** — `tessl scenario download` now accepts multiple space-separated IDs (e.g., `tessl scenario download id1 id2`)
* **Eval Run Repeats** — New `--runs / -n` flag on `tessl eval run` to repeat each agent configuration multiple times
* **Eval Polling for Tile Evals** — Tile evals now poll for completion instead of returning immediately, with Ctrl+C to detach gracefully
* **Eval JSON Output for Tile Evals** — `--json` flag on `tessl eval run` now works for tile evals, outputting run ID, tile name, and scenario count
* **Eval Agent Flag** — New `--agent` flag (format: `agent:model`) replaces `--model` for tile evals, aligning with project eval conventions
* **Eval Cost Metadata** — `tessl eval view` now displays cost, runtime, turns, and token usage below each solution
* **Eval Agent/Model Display** — `tessl eval view` stats now show the agent and model used for each solution
* **`--last` for Eval Debug & Retry** — `tessl eval debug` and `tessl eval retry` now support `--last` for quick access to the most recent eval run
* **Repository Metadata in tile.json** — Tiles now support an optional repository URL field in their manifest

### Improvements

* **Tile Cache Protection** — Tile authoring commands now warn when operating on paths inside `.tessl/tiles/`, preventing accidental edits that would be overwritten on the next install
* **Cleaner Help Text** — Removed meaningless `--no-{flag}` negated boolean flags from all commands, improved flag capitalization, and reordered commands for logical grouping
* **Consistent Scenario Terminology** — "Generation run" renamed to "generation" throughout the CLI for simpler, more natural language
* **Scenario Generate Table Output** — `tessl scenario generate` results now render as a formatted table with Scenario ID and Commit ID columns
* **Smarter Binary Install** — Skips re-downloading binaries that already exist and pass validation; corrupt binaries are automatically cleaned up

### Fixes

* Fixed CLI crashes when output streams disconnect unexpectedly (EPIPE handling)
* Fixed `unpublish` help text to reflect the correct 2-day timeframe (was showing 2 hours)

## 0.69.0

### New Features

* **Scenario Generation Commands** - New `tessl scenario` command suite for generating and managing test scenarios. Generate scenarios from tiles or repository commits (`tessl scenario generate`), download them to disk (`tessl scenario download`), and track generation runs (`tessl scenario list`, `tessl scenario view`)
* **Global Tile Installation** - Install tiles globally with `tessl install --global` to make documentation and skills available across all your projects.
* **Eval Variant Comparison** - `tessl eval run` now supports running evaluations with multiple agent variants and context patterns, with automatic detection of tile vs. project scenario sources

### Improvements

* **Project Directory Hints** - `tessl init` and `tessl install` now display a helpful hint when the target project directory differs from your current working directory

### Bug Fixes

* Fixed a crash when piping CLI output to another command (e.g., `tessl list | head`) by gracefully handling broken pipe signals
* Fixed markdown link validation incorrectly parsing non-markdown files during tile linting

## 0.68.0

### New Features

* **GitHub Copilot CLI Support** — Tessl now recognizes GitHub Copilot CLI as an agent, with automatic detection, rules wiring via `AGENTS.md`, skill installation, and MCP configuration
* **Search Type Filtering** — `tessl search` now accepts a `--type` flag to filter results by content type (skills, docs, or rules)
* **Skill Review Auto-Accept** — `tessl skill review --optimize` now supports a `--yes / -y` flag to skip the confirmation prompt and auto-apply improvements

### Improvements

* Automatic Binary Cleanup — Old CLI binary versions are now automatically cleaned up after updates, keeping only the current version and one rollback
* Short SHAs for Git Tiles — `tessl list` and `tessl doctor` now display short SHAs for git-sourced tiles instead of full commit hashes
* Command Detection Timeout — Command lookups now have a 1-second timeout to prevent indefinite hangs during agent detection

### Breaking Changes

* `tessl skill search` Removed — Use `tessl search` --type skills instead

### Bug Fixes

* Fixed `tessl skill review --optimize` failing with a raw 401 error when not authenticated — now shows a clear login prompt
* Fixed `unpublish` help text to reflect the correct 2-day window (was incorrectly showing 2 hours)

## 0.67.0

### **New Features**

* Skill Optimization - `tessl skill review --optimize` now applies feedback from the review to generate an updated SKILL.md file
* Evaluation Type Support - `tessl eval list` now displays all evaluation types (tile, repo, skill) with a new "Type" column and `--type` flag to filter by evaluation type
* Filter Evaluations by Status - `tessl eval list` now supports a `--status` flag to filter runs by status (pending, in\_progress, completed, failed)

### **Bug Fixes**

* Fixed `tessl skill review` returning exit code 0 on validation failure — it now correctly returns a non-zero exit code, enabling proper CI/CD integration
* Fixed `tessl api --input` - sending incorrect Content-Type when piping JSON via stdin

## 0.66.0

### New Features

* **`outdated` command** — Check installed tiles for newer versions, showing current, safe update, and latest version info
* **`update` command** — Update tiles to newer versions. Use `--yes` for all, and `--force` to include breaking updates

## 0.65.0

### Bug Fixes

* **Windows Compatibility for Generated Files** - Fixes generated file and directory names containing colons (`:`) which are invalid in Windows file paths. Tile files committed to git repositories can now be cloned on Windows without errors. Existing installations are migrated automatically

## 0.64.0

### New Features

* **Local Tile Evaluation** - Run evals against your tiles directly from the CLI with `tessl eval run` and get a fast feedback loop for improving your tiles
* **Eval Results in Terminal** - View eval results with per-scenario breakdowns, variant comparisons, and scoring summaries using `tessl eval view`
* **Eval Run History** - List recent eval runs filtered by workspace, tile, or author with `tessl eval list`
* **Eval Retry** - Re-run failed evals with `tessl eval retry`

## 0.63.4

### Improvements

* **Internal** - Stability improvements

## 0.63.3

### New Features

* **Local Path Support for Tile Info** - `tessl tile info` now accepts a local directory path as an argument and defaults to the current directory when no argument is provided, making it consistent with other tile commands like `publish` and `lint`

### Improvements

* **Better Publish Messages** - After publishing tiles or skills, the CLI now displays the published resource URL and a note about moderation timelines

### Bug Fixes

* Fixed MCP configuration failures when using `npx tessl` by ensuring the tessl binary is accessible via the PATH during `init`

## 0.63.2

### Bug Fixes

* Fixed circular reference issues when `CLAUDE.md` and `AGENTS.md` are symlinked

## 0.63.0

### Improvements

* The CLI is now distributed as a native binary instead of a bundled Node.js package

## 0.62.2

### New Features

* **Optional Authentication** - You can now use `tessl init`, `tessl install`, and `tessl search` without logging in. Public tiles are accessible to everyone, while private tiles will show helpful hints to log in when needed

### Bug Fixes

* Fixed `--skill` flag not working correctly when installing a single skill from a local tile with multiple skills

## 0.62.1

### Improvements

* **Internal** - Stability improvements

## 0.62.0

#### New Features

* Unified Search — `tessl search` now returns both tiles and skills in a single view, with visual badges showing content types (\[docs], \[skills], \[rules]) and skill provenance
* Tile Evaluations — New tessl eval command group for evaluating tiles:
  * `tessl eval run [path]` — Run evaluations on local tiles
    * `--force` flag to re-run all evaluations including previously solved cases
  * `tessl eval list` — View recent eval runs with status
  * `tessl eval view-results <id>` — View detailed results with color-coded scores and assessment tables
    * `--last` flag to quickly view most recent eval results
  * Git metadata (branch, commit, dirty state) automatically attached to eval runs
* Tile Info — New `tessl tile info <name>` command to view tile metadata, verification status, and moderation status
* Skills in MCP Context — The `query_library_docs` MCP tool now includes installed skills when building knowledge context

#### Improvements

* Redesigned Help for `tessl -h` — Commands are now grouped by category with improved formatting and readability
* Publish Workspace Context — Publishing now shows the target workspace name before uploading

#### Bug Fixes

* Fixed `--skill` flag being ignored when installing skills from registry tiles
* Fixed re-installing tiles not updating the manifest with new skill selections
* Fixed tile lint incorrectly flagging files as orphaned when linked via `@path.md` includes

## 0.61.4

### Improvements

* **Install `--yes` Flag Behavior** - The `--yes` flag now auto-selects all skills when installing from GitHub, even in interactive mode. Previously it only worked in non-interactive environments.
* **Non-Interactive tile new** - The tessl tile new command can now run fully non-interactively when all required flags are provided, making it easier for scripts and agents to use.

### Fixes

* **GitHub Skill Installation Bugfixes** - Address various edge cases that prevented some skills being installed from GitHub
* **GitHub Skill Installation Respects Pinned Version** - Fixed an issue that meant we always installed a skill from a repository's trunk rather than the commit sha in `tessl.json` when doing `tessl install`

## 0.61.3

### Improvements

* **Faster git installs** - Git dependencies are no longer re-downloaded on every install when already at the correct version

## 0.61.2

### Improvements

* **Windows Platform Detection** - The CLI now detects Windows and provides a clear error message directing users to use WSL instead
* **Lenient GitHub Skill Installation** - Skills from GitHub with missing referenced files or minor schema issues now install with warnings instead of failing completely
* **Better Warning Display** - Installation warnings are now grouped by tile name with count summaries (e.g., "3 content warnings in github:user/repo")
* **Verbose Install Output** - Added `--verbose` (`-v`) flag to `tessl install` and `tessl skill search` commands to display detailed warning messages during installation

### Bug Fixes

* Fixed skill validation warnings showing absolute temp directory paths instead of relative paths
* Fixed installation failures when GitHub skills have invalid optional frontmatter fields (e.g., `allowed-tools` as array instead of string)

## 0.61.1

### New Features

* **Skill Commands** - Added a complete set of dedicated skill management commands: `tessl skill new`, `tessl skill import`, `tessl skill lint`, `tessl skill publish`, `tessl skill review`, and `tessl skill search`. Skills are now first-class citizens with their own command group for easier discovery and management.
* **GitHub Installation Support** - Install tiles and skills directly from GitHub repositories using `tessl install github:user/repo` or full URLs. Supports branch selection, subdirectories, and interactive skill selection. Git sources are pinned to commit SHAs for version stability.
* **Skill Search** - Discover available skills across the Tessl ecosystem with the new interactive `tessl skill search` command. Search returns both external skills from GitHub and tiles containing skills, with interactive selection and installation.
* **Skill Review** - Evaluate skill quality with `tessl skill review`, which validates [SKILL.md](http://skill.md/) files and provides LLM-based scoring across multiple dimensions with actionable suggestions for improvement. Supports both local paths and GitHub URLs.
* **Non-Interactive Mode** - Commands now auto-detect TTY availability and work in CI/CD pipelines and with AI agents. When required flags are missing in non-interactive environments, commands provide clear error messages pointing to available options.
* **Selective Skill Installation** - Choose specific skills when installing tiles using the new `include.skills` manifest field in tessl.json. Reduces clutter in agent directories by only activating the skills you need.
* **Codex Support** - Full support for Codex as a core agent alongside Claude Code, Cursor, and Gemini. Includes project detection, MCP configuration, steering, and skills synchronization.
* **Gemini Production Ready** - Gemini agent is no longer experimental and fully supported in production workflows.

### Improvements

* **Simplified Skill Naming** - Skill directories now use cleaner names (`tessl:<skill>` instead of `tessl:<workspace>:<tile>:<skill>`) and are symlinked rather than copied for better performance and consistency.
* **Automatic Eval Publishing** - Evals are now published by default when running `tessl tile publish` or `tessl skill publish`. Use `-skip-evals` to opt out. Published evals automatically trigger evaluation runs.
* **Better Skill Discovery** - Improved skill detection uses a three-step process (root check, priority directories, full scan) with optimizations that reduce scan time and exclude build/cache directories.
* **Streamlined Publishing** - The `tessl skill publish` command no longer asks redundant "will you share this?" prompts since publishing inherently means sharing.
* **Improved Help Text** - Updated `tessl init --agent` to list all supported agents (claude-code, codex, cursor, gemini) and clarified that `tessl install` accepts both registry tiles and GitHub URLs. Added `tessl i` as a shortcut alias.
* **Relative Paths in Manifest** - When creating tiles with `--install`, the CLI now stores relative paths in tessl.json instead of absolute paths, making projects portable across machines and directories.
* **Smarter CLI Setup** - The tessl/cli-setup tile is now only installed when your tiles have documentation, reducing overhead for steering-only projects.
* **Flexible Tile Content** - Tiles can now contain any file type, not just markdown files, allowing for configuration files, code examples, and other documentation formats.

### Bug Fixes

* Fixed skill review score display showing `/4` instead of the correct `/3` scale
* Fixed status command handling of non-semver versions for git-based and file-based tiles
* Fixed orphaned file detection for tiles with root-level skills - README.md and other root files are no longer incorrectly flagged as orphaned
* Fixed skill publish command to accept [SKILL.md](http://skill.md/) file paths directly instead of only directory paths
* Fixed file source tiles being incorrectly deleted as orphaned on repeated installs when dependency key differed from tile name
* Fixed skill frontmatter validation warnings during installation from external sources like GitHub
* Fixed `tessl skill new` output messages to reference correct tile directory paths
* Fixed skill selection in GitHub repos to require explicit choices, preventing accidental installation of all skills from large repositories

## 0.60.0

### New Features

* **Project Mode (Managed vs Vendored)** - Choose how tile content is managed in your project:
  * **Managed mode** - Tile contents are gitignored like `node_modules` (default for existing projects)
  * **Vendored mode** - Tile contents are committed to your repository (default for new projects)
  * Switch modes anytime by setting `"mode": "managed"` or `"mode": "vendored"` in `tessl.json`

### Improvements

* **Simplified Local Tile Dependencies** - Local tiles (using `file:` or `link:` sources) no longer require a `version` field in `tessl.json`. The CLI automatically reads versions from installed `tile.json` files. Existing manifests with both `version` and `source` are automatically migrated to the new format.

## 0.59.0

### New Features

* **`.tileignore` Support** - Exclude files from tile validation and packaging using gitignore-style patterns.

## 0.58.1

### Improvements

* **Tile Commands Auto-Detect** - `tessl tile pack`, `tessl tile lint`, and `tessl tile publish` can now be run directly from your tile directory without explicitly specifying the path

### Bug Fixes

* Squashed some bugs that were hiding in the codebase. They know what they did.

## 0.58.0

### New Features

* **Context Cost Analysis (Experimental)** - The `tessl doctor` and `tessl tile lint` commands now display estimated token costs for tile content loaded into agent context windows. This helps you understand which tiles contribute most to context usage and identify oversized content before publishing.
* **HTTP Proxy Support** - The CLI now works in proxied network environments by automatically detecting and using HTTP/HTTPS proxy settings from environment variables (`http_proxy`, `https_proxy`, `HTTP_PROXY`, `HTTPS_PROXY`). The CLI respects `no_proxy`/`NO_PROXY` bypass rules.

### Improvements

* **Enhanced Python Dependency Scanning** - Improved Python dependency detection with support for extras notation (e.g., `openai[aiohttp]>=1.52.2`) across requirements.txt, pyproject.toml, and `uv tree` output. The scanner now also automatically falls back to `pip3` when `pip` is not available, improving compatibility with macOS and Linux distributions.

### Bug Fixes

* Fixed Go package PURL validation to require the `v` prefix in version strings (e.g., `v1.9.3` instead of `1.9.3`), ensuring pkg.go.dev documentation links work correctly.
* Fixed false "broken link" warnings during `tessl tile lint` when markdown files contain URI schemes like `mailto:`, `tel:`, `ftp:`, or `data:`.

## 0.57.0

### New Features

* **Monorepo Subfolder Support** - The `tessl install --project-dependencies` command now scans dependencies from your current working directory, enabling you to run it from monorepo subfolders in an initialized project (e.g., `tessl init && cd backend && tessl install --project-dependencies`)

### Improvements

* **Smarter Documentation Tree** - The context tool now only includes files that are transitively linked from a tile's documentation entrypoint, filtering out orphaned files and making documentation discovery more focused

### Bug Fixes

* Fixed markdown link validation to properly handle anchors (e.g., `./guide.md#section`). The validator now validates both file existence and anchor validity, with clearer error messages for broken links

## 0.56.2

### Improvements

* **Internal** - Stability improvements

## 0.56.1

### Improvements

* **Observability Enhancements** - Improved telemetry infrastructure for better diagnostics and system monitoring

## 0.56.0

### Breaking Changes

* **Node.js 18 No Longer Supported** - The CLI now requires Node.js 20 or later. Node.js 18 reached end-of-life in April 2025 and is no longer supported. The CLI will now fail with a clear error message if you attempt to run it on Node.js 18.

### Improvements

* **Only Auto-Init Outside Existing Projects** - The `install` and `search` commands no longer automatically initialize a project when run in a subdirectory of a Tessl project directory. This prevents accidentally creating unwanted projects when running commands after changing directories. To initialize a project, explicitly run `tessl init` first.

### Bug Fixes

* Fixed an issue where the `init` command's failed to identify and install relevant tiles.

## 0.55.1

### Improvements

* **Faster Auto-Updates** - The CLI now checks for updates every 3 hours (down from 24 hours), ensuring you get critical fixes and improvements more quickly. You can still adjust the interval with `TESSL_AUTO_UPDATE_INTERVAL_MINUTES` or disable it by setting it to `0`.

## 0.55.0

### New Features

* **Updated Workspace Roles** - Workspace member roles have been updated to: `member`, `publisher`, `manager`, and `owner` (the `viewer` role has been removed)

### Bug Fixes

* Fixed authentication issue where API token authentication (`TESSL_TOKEN`) would fail after token refresh attempts, causing authenticated API calls to fail

## 0.54.0

### New Features

* **Tile Manifest Validation** - The `describes` field in tile manifests is now validated to ensure it contains a valid PURL (Package URL) format. Invalid PURLs or unsupported package types will show helpful warnings during `tessl tile lint`, `tessl tile pack`, and `tessl tile publish` operations

### Improvements

* **Project Directory Detection** - Improved project directory discovery logic allows the CLI to better locate your `tessl.json` even when running commands from subdirectories within your project

### Bug Fixes

* Fixed missing newline after `tessl list` output for better terminal formatting

## 0.53.0

### New Features

* **Go Modules Support** - Dependency scanner now detects Go projects and extracts dependencies from `go.mod` files
* **Auto-Init on Search** - When selecting a tile from search results, the CLI now automatically initializes the project if needed (creates `tessl.json`)
* **Dynamic Terminal Sizing** - Interactive prompts now adapt to your terminal height for better readability
* **Escape Key Cancellation** - Press Escape to cancel select/checkbox prompts

### Improvements

* **Smarter Context Tool** - When no documentation is found, the tool now suggests running a search to find relevant tiles
* **CI Environment Detection** - Auto-update and version checks are now automatically disabled in CI environments

### Bug Fixes

* Fixed tile installation failures when system temp directory is on a different filesystem (handles cross-device moves)

## 0.52.2

No changes in this release

## 0.52.1

### Improvements

* Homebrew installation now available for stable releases
* MCP server stability improvements

## 0.52.0

### New Features

* Added `tessl tile lint` command to validate tiles before publishing

### Bug Fixes

* Tile upgrades now properly clean up old files when installing new versions
* Fixed markdown link validation to skip code blocks in tile documentation

## 0.51.0

* MCP Tool Improvements
  * Renamed `get_library_context` tool to `query_library_docs` and updated descriptions for better clarity
  * Updated cli-setup steering tile version to reflect tool rename and improve adoption
* Tile Management
  * Added `tessl list` command to display installed tiles
* CLI UX
  * Improved CLI message spacing and copy for better readability

## 0.50.6

* Added the ability to `archive` and `unarchive` workspaces
  * Archiving a workspace allows you to non-destructively remove workspaces you no longer wish to use or see. Archiving a workspace also archives all tiles in that workspace. This means that users can still install those tiles if defined in their manifest, but will not otherwise see them in search etc.
  * Unarchiving, as you’d expect, shows a workspace once again. However, it does *not* unarchive tiles within the workspace, to avoid potentially unarchiving content that was previously archived before the workspace was.
* Fixed a bug when authenticating in multiple machines that would result in our MCP tool `get_library_context` only working on the last machine you authenticated.

## 0.50.5

We added a new `tessl tile pack` command which validates and builds a tarball for a tile. It can be useful to validate things before doing `tessl tile publish`.

Also fixed a bug where the CLI would fail to run when who signed up without a first or last name.

## 0.50.4

Fix `tessl tile publish` requiring an absolute path

## 0.50.3

This release marks a significant shift in focus. We've rebuilt the CLI from the ground up as an **Agent Enablement Platform**, pausing work on the Tessl Framework to concentrate on our registry functionality and agent integration.

We really appreciate all the feedback we have received so far. Please, keep it coming.

### What's Changed

#### Getting Started is Now Simpler

When you run `tessl init` in your project, it now automatically detects your dependencies and pulls relevant tiles from the registry. For teams already using tessl, new developers only need to install the CLI and grant MCP server permissions—everything else happens automatically when the MCP starts up.

#### Smarter Agent Integration

Instead of agents searching through generated documentation, they now use our new `get_library_context` MCP tool to query tiles directly. This requires logging in to tessl, but makes context retrieval much more efficient. The MCP tools also include `login` (for in-session authentication) and `status` (for checking project state).

Note: `KNOWLEDGE.md` is no longer generated since agents now pull context on-demand.

#### Command Reorganization

We've streamlined the command structure. Changes to existing commands:

* `tessl registry install|search` → `tessl install|search`
* `tessl registry sync` → `tessl install --project-dependencies`
* `tessl registry publish|unpublish|archive` → `tessl tile publish|unpublish|archive`
* `tessl version` → `tessl --version`

#### Release Channels & Auto-Updates

You can now choose between `latest` (default, stable) and `beta` (early access) channels. The CLI checks for updates hourly and auto-updates transparently (max once per 24 hours). Configure the interval with `$TESSL_AUTO_UPDATE_INTERVAL_MINUTES` or set it to 0 to disable.

#### New Commands

* `tessl doctor` - diagnose authentication, manifest status, detected agents, and dependencies (add `-json` for full dependency details)
* `tessl uninstall` - remove tiles from your project

### What's Removed

**The Framework functionality is no longer included.** If you need the full framework features, v0.28.0 remains available (though development is paused).

Currently, automatic setup only supports Claude Code and Cursor. We'll add other agents based on requests—in the meantime, you can manually configure the MCP with any agent.

***

**Note:** This is a major rewrite (v0.30.0 → v0.50.2). Existing workflows will need adjustment.

*(Updated Nov 27, 2025)*

***

## 0.28.0

### New Features

* Invoking `setup agent` via `npx` will now configure mcp to use tessl via `npx`, ensuring smoother setup for users without a global install.
* Added integration with Cursor IDE for automatic generation and application of steering rules from tiles.
* Added `tessl registry uninstall` to easily uninstall tiles.

### Improvements

* Improved error messages for the `add-member` command to enhance clarity.
* Improved `tessl status` output to better reflect post-generation changes.
* Aligned arguments for `tessl registry unpublish` and `tessl registry archive`.

### Bug Fixes

* Fixed detection of direct dependencies in the pip detector.
* Resolved issue where unsupported package managers showed unnecessary user-facing messages.
* Extended display name support to include PyPI and Maven packages.
* Removed outdated compatibility code for `specs.json`.

## 0.27.0

### New Features

* **New `tessl setup project-info` Command**: CLI-only users can use this command to add details that Tessl tools require to `AGENTS.md`.
* **Framework Initialization**: you can choose “full-framework” (closed beta, login required) or “registry only” when running `init`.
* **Steering Rules**: Tiles can now define agent behavior rules via a new `steering` section in their manifests. These rules are automatically aggregated into a `RULES.md` file.
* **Local Usage Spec Installation**: Added support for installing tiles from local directories via a new `source` field in `tessl.json`.

### Improvements

* Simplified usage spec installation paths by removing version info from the directory structure.

### Bug Fixes

* Fixed `config list` to return empty results instead of throwing errors when no config file is present.

## 0.26.0

### New Features

* Selecting Cursor during `tessl init` or via `tessl setup agent` will now automatically install rule files into `.cursor/rules/tessl/framework` as part of project setup, improving Framework adherence.

## 0.25.0

### New Features

* **Improved Tile Installation UX:** Tiles suggested during install/update are now selected by default, making the default path simpler for users.
* **Improved Help for `tessl init`:** The `-help` output for `tessl init` now lists available agents, making it easier to discover options.
* **Non-Interactive Shell Support:** CLI installation no longer blocks in non-interactive shell environments.

### Improvements

* **Better Logging Behavior:** In non-interactive mode, JSON logs are now correctly sent to stderr, preserving standard piping behavior.
* **Help Text Visibility:** Tile help text remains visible even when tiles are deselected, improving clarity during installation.

### Bug Fixes

* **Maven Dependency Detection:** Fixed issues with the Maven dependency detector to ensure accurate detection.
* **Logging Flushing Reliability:** Addressed a flakiness issue by ensuring that stdio is flushed on close, not exit.
* **Ink Render Handling:** Corrected usage of Ink’s rendering logic to prevent hanging or exit-related issues.
* **Security:** Updated `pino` logger to remove a transitive vulnerability.

## 0.24.0

### New Features

* `tessl registry sync` to synchronize tiles from the registry.
* `tessl setup framework` to switch between the full Tessl framework and “registry-only” mode.
* `tessl stamp` to manually sync spec/code hashes after intentional edits so tessl status stops flagging them.
* `tessl registry search` now matches by full tile name (workspace/name), making it easier to find your own tiles.
* `tessl registry list` added to browse available tiles.
* `tessl document` adds tests to spec by default.

### Improvements

* `tessl status` surfaces an explicit error if bootstrap hasn’t been run.
* Tool calls will fail if agent bootstrap process has not been run, with guidance to fix.
* Added --print-project-cache flag for getting the path to the project cache.
* Removed interactive tool picker removed to simplify workflows.
* AGENTS.md now includes clearer “bootstrap” hints and wording tweaks to reduce confusion about when/why bootstrap should run.

### Bug Fixes

* MCP commands now authenticate correctly in all call sites.
* Reduced noisy output during `tessl registry sync`.

### Compatibility

* Minimum Node.js version is now **>= 22.0.0**.

## 0.23.0

### New Features

* Added support for **GitHub Copilot**.
* `tessl edit` now supports code files that have an associated `@describe` spec. Changes are made via the spec.

### Improvements

* `tessl whoami` command now includes your user ID.
* Reduced the chances of "token expired" errors, by proactively refreshing tokens when commands are run.
* Test commands will now time out if no output is received for a set period, preventing hangs.
* Registry commands now prompt for initialization if the workspace isn’t already set up.
* Unused `.tessl/usage-specs` directories are now removed during sync if the corresponding entries are removed from `tessl.json`.
* Test generation is now controlled by a unified `generateTests` param, which always skips locked tests.
* Improved `list` command usability, to make it easier for agents to select the right parameters.
* `tessl registry publish` **s**kips uploading irrelevant `.md` and `.gitignore`d files.

### Bug Fixes

* `tessl status` no longer shows duplicated test paths.
* Resolved issue where interactively selected tools weren’t able to run.
* Fixed Goose setup, so that correct `.gooserules` configuration is used.

### Deprecations

* Removed deprecated annotations like `@describes`, `@generates`, and `@satisfied-by`.

{% hint style="warning" %}
**⚠️ Be sure to check out the changes in** [**0.22.0**](#0.22.0) **if you're coming from an older version**
{% endhint %}

## 0.22.1

### Bug Fixes

* Fixed a project language detection issue that would occasionally cause errors across different commands.

## 0.22.0

{% hint style="warning" %}
**⚠️ Upgrading for users who installed via `npm`**

If you have already installed tessl via npm, you'll need to:

* Run `npm uninstall -g tessl-alpha` (if you have a version earlier than `v0.21.0`)
* Run `npm i -g @tessl/cli` to update to the latest version.

The `update` command will work as expected from `v0.22.0` onwards.
{% endhint %}

{% hint style="info" %}
**Camel case flag support has been removed**

Camel case flag support has now been fully phased out, all command flags should be provided in kebab case.
{% endhint %}

### **New Features**

* `init` now offers the option to set up an agent and MCP integration when initializing a project for the first time.

### **Improvements**

* Support information is now listed in help for logged-out users.
* Tool parameters are now displayed before model parameters in tool-level help.
* Links are now supported in kitty terminal.

### **Bug Fixes**

* Fixed the rules file targeting in Gemini CLI agent setup.
* `feedback` is now gated to authenticated users, unauthenticated users can request support via email (<support@tessl.io>).

## 0.21.2

### **Bug Fixes**

* Fixed MCP server not allowing invocation of install or search tools when not logged in.

## 0.21.1

### **Improvements**

* Running tools with spec-registry-only context will now fail early and remind you to install the Tessl Framework before continuing.
* Added ability to exit `registry search` with the Escape key.
* Search results now link to the **spec registry** page instead of individual package registries.

### **Bug Fixes**

* Fixed **Ctrl+C** handling with the experimental `observe` command.
* Fixed a deprecation warning on `registry install`.

## 0.21.0

{% hint style="warning" %}
**⚠️ Upgrading for users who installed via `npm`**

We have now published to public npm! This means that if you installed Tessl using `npm`, auto update with `tessl update` will not work for you for this version. To update, you’ll need to run:

```markdown
npm uninstall -g tessl-alpha
npm install -g @tessl/cli
```

{% endhint %}

{% hint style="warning" %}
**⚠️ Re-initializing your projects**

With this release, you’ll need to re-initialize any previously-created Tessl projects by running `tessl init` inside those directories.
{% endhint %}

***

### Breaking Changes

* Removed test generation from the `build` tool and introduced a new `build-tests` tool
* The spec format has been updated to allow capabilities to contain requirements that don’t have `[@test]` links:
  * `create` and `edit` specs will only add `[@test]` links if explicitly asked to do so in the `--prompt`

### **New Features**

* Our spec format now supports distinguishing between three types of tests:
  * `locked` tests: capture functional requirements provided
    * Denoted by `{ .locked }` next to the `[@test]` link or in a header containing `[@test]` links.
    * You can manually mark tests as locked or use `tessl test` to do it by passing `--lock-tests`.
  * `impl` tests: cover implementation details that are tested to identify changes in behaviour disconnected from a spec’s functional requirements.
    * Denoted by `{ .impl }`.
    * They are generated by `tessl document --include-impl-details` .
  * `draft` tests: are untagged and could have been added manually or using `edit` or `create`
    * You can use `tessl test --lock-tests` to automatically lock all draft the tests from a spec when all tests pass.
* Added flags to `build-tests` tool to select which kind of tests to generate `--generate-draft-tests`, `--generate-locked-tests`, and `--generate-impl-tests`.

### New Commands & Tools

* Added new `registry install`, `registry search`, and `registry sync` commands for working with Usage Specs.
* Added a new `test` tool, to run all of the tests for a given spec.
* Added `generate-tests` tool to add tests to a given spec.
* Added experimental `observe` tool to observe MCP tool invocations.

### Spec Registry

* Unauthenticated users can now access a subset of Tessl’s features to add Usage Specs to their projects.
  * Running `init` while logged out will set up `AGENTS.md` and framework files relating to the Tessl Spec Registry.

### Global Config

* `config` commands now support the `--global` flag, for updating the global Tessl config.
* The global config now supports a `shareUsageData` key to allow for analytics opt-out.

### **Improvements**

* Cleaner output for `build` and `build-tests` with a final summary listing successful and failed specs.
* Spec discovery for `build` is smarter: only builds specs with @generate links that actually need code updates.
* Diagnostics suggestions now correctly use positional parameters (e.g., `tessl build <spec>`), and `status` accepts \<spec> as positional.
* Registry-only flows link to the Knowledge Index; usage spec manifest split into tessl.json (structured) and `KNOWLEDGE.md` (narrative).
* Tessl MCP tools can now log detailed output to a file to reduce their output and avoid saturating agent context when the agent is instructed to do so.
* Added `--code` and `--tests` filters to `list` command for finer control.
* Made `test` tools `spec` argument positional in CLI commands.
* `status` now splits diagnosed issues into errors and warnings and includes errors regarding spec parsing.
* `config set` now **validates keys** before setting.
* Updated spec syntax information in our system prompts:
  * Added context about our content tags: untagged (draft), `locked` and `impl`.
  * Removed un-used `.dependencies` and `.dependency` tags.
* Updated Tessl framework content with clearer guidance on `tessl test`, `status`, and `fix` behavior.
* `version` output is now cleaner and includes a hint to update if a new version is available.
* The `--spec` argument in `build` is now positional.
* Exposed `modelName` parameter in MCP tools

### **Bug Fixes**

* Spec selection no longer appears to “infinite scroll.”
* `infer-spec` no longer swallows errors.
* Fix loop now receives test results properly.
* Reporter failures print helpful context (e.g., missing file paths) to the terminal.
* Prevent generation of tests when using edit on code files.
* Test-runner name generation validates input and throws on invalid paths.
* Tessl framework files are now filtered from context provided to tools to prevent context poisoning.
* Fixed issue where **login could not be cancelled** while polling.
* Fixed truncated console logs when running in **non-interactive/piped mode**.
* Fixed `edit` tool so context files are properly passed.
* Fixed dependency verification to use correct `@use` path resolution.
* Prevented whitespace-only messages from being sent to models.
* Pass test results through fix loop iterations correctly.
* Fixed an incorrect path in `.tessl/.gitignore` for session data storage.
* Fixed `init` missing from global help.
* Reject invisible characters from LLM interactions.

## 0.19.0

{% hint style="warning" %}

> This release includes breaking changes that require migration for existing projects.\*\*

To migrate your existing projects using `TESSL.md`, run the following steps:

1. Remove old system prompt directory: `rm -rf .tessl/system-prompt`
2. Run `tessl init` to initialize the new framework.
3. Migrate any custom content from `TESSL.md` to `AGENTS.md`
4. Remove the old file: `rm TESSL.md`
5. Re-configure any existing agent integrations: `tessl setup agent`
   {% endhint %}

***

### New Features

* Adopted [`AGENTS.md`](http://agents.md/) standard (replacing `TESSL.md`) for unified AI agent configuration across your development workflow.
* **Improved Dependency Handling**
  * `verify-deps` now runs automatically after code generation in the build process.
  * New `specifyMissingDependencies` flag can auto-update your spec with missing dependencies in `tessl build` and `tessl edit`.
  * New `never` option added to generation mode to skip file generation entirely.
* `tessl status` enhancements:
  * Highlights whether a spec includes `@generate` or `@describe` annotations.
  * Added dirtiness diagnostic to detect out-of-sync code/test files.
  * Added diagnostic for missing `AGENTS.md` entries in `tessl status`.
* **Improved Spec & Test Management**
  * Rewritten `document` tool with more intuitive behavior and support for complex specs.
  * Re-initializing a project now regenerates framework files.

### Improvements

* Removes `setup language` command; tools that require test commands now have a `--test-command` flag.
* Removed description upon tool invocation.
* Increased latest version check timeout in `update` command to reduce false negatives.

### Bug Fixes

* Logging in from an agent now automatically updates the MCP server so you can continue without restarting.
* Fixed tool summaries not showing in CLI output.
* CLI session data no longer prevents process exit after command completion.

## 0.18.1

### **New Features**

* **`build` tool** now has a post-generation fix loop to automatically resolve issues - *we will be tweaking how we decide which test command to run in the coming week*
* New **`status`** checks for orphaned tests, mixed implementation links & missing required files
* Added support for **agent-native planning**: agents now create and maintain **`.plan.md`** files automatically during planning.

### **Improvements**

* **Removed `--recursive` option** from status command for consistency with other commands
* **Replaced** `--rebuild-tests` with `--generate-tests` to `build` to generate only missing, outdated or always (default is now `outdated` rather than `missing`)
* **Added** `--generate-code` to `build` which behaves like `--generate-tests` but for code files (default is `outdated`, but is likely to change)
* **Generated code now includes self-hashing** for integrity checking and better status reporting on modified files
* Removed **timestamps from generated code tags**
* Improved **logging and error messages** across tools
* Improved **exception messages** to include more debugging context
* Better **editor detection and logging** in planning workflows
* Clarified descriptions for several commands & tools
* Tools invoked via MCP now return more information about their run, improving feedback visibility for agents
* The `build` tool now returns a failure exit code when the fix loop fails, ensuring accurate signaling of build status.

### **Bug Fixes**

* Fixed **update** command in Linux when using npm
* Fixed **test generation messages** saying “tests” instead of “test files”

### Deprecations

* Removed ability to edit plan files directly via the `edit` tool; users now receive a clear error if they attempt to do so.
* Made the `plan` tool private to reduce confusion and encourage agent-driven planning workflows

## 0.17.1

### Bug Fixes

* Fixes creation of the Claude subagent instructions directory as part of `setup agent`

## 0.17.0

### New Features

* Enabled tessl status to list all specs by default, providing broader project insights.
* Updated tessl status to include sync checks that detect whether a spec's code and tests are up to date.

### Improvements

* Updated build and document tool descriptions for clarity and accuracy.
* Made the -spec parameter more explicit and consistently required in tessl status help output.
* Renamed build tool's iterations parameter to maxIterations for clarity.
* Renamed @build links to @generate to reflect intended behavior.

### Bug Fixes

* Fixed bash execution in the update command to work reliably across Mac and Linux.
* Corrected error handling for expired keys.
* Made terms and conditions check respect a global directory set via environment variable.
* Fixed a crash in tessl status when a non-existent or empty spec file is provided.
* Resolved incorrect logging of issue counts in single-spec status output.
* Improved handling of file paths in the document tool to normalize inputs like ./path/to/file.


# Web Changelog

Release notes for the Web and API surfaces.

## 2026-10-03

### New features

* The `publish_plugin`, `validate_plugin_for_publish`, `list_plugin_files`, and `get_plugin_file` tools on the remote MCP server now support incremental plugin publishing: agents can apply ordered put, edit, and delete changes to an existing plugin version without re-uploading every file.
* The `archive_plugin` and `delete_plugin` tools are now available on the remote MCP, allowing publishers to archive or permanently delete plugins without leaving their coding environment.
* Projects have been moved to the Org level.
* Workspace settings no longer shows a separate Source control section; Linear and other workspace integrations now appear together under a single Integrations section alongside MCP servers.
* The sidebar navigation is reorganized into three labeled groups: Work, Manage, and Resources. The workspace selector is removed from the sidebar; a Workspaces entry under Manage links to a list page, the Activity page gains a workspace selector, and workspace Settings is accessible from each workspace card.
* Org admins now have a setting to enable or disable automatic quality reviews dispatched during inventory scans using the `automaticQualityReviewsEnabled` field. Security scans and indexing continue regardless of this setting.

### Bug fixes

* Code Review via the GitHub App now correctly reads pull-request links. The link-selection model is included in every App-managed run key, and documentation pages up to 2 MB are fetched and converted to text rather than being refused for size.
* The Last changed timestamp on the managed-requirements policy editor now correctly updates when a newer policy is saved.
* Workspace publishers can now archive, unarchive, deprecate, and undeprecate plugins from the plugin page. Previously only managers could perform these actions.
* The Automations entry is no longer shown in the workspace sidebar; existing Automations routes remain accessible via direct links.
* Skill activations now include file reads of installed `SKILL.md` files via the Read tool, `cat`, `head`, and `sed`, so eval activation counts accurately reflect all skill usage.

## 2026-09-28

### New features

* Organizations can now run up to 1,000 eval scenarios per day, up from 300.
* Workspace managers can now mark a plugin as deprecated with an optional reason and replacement. Users viewing a deprecated plugin see a warning banner on its registry page.
* The findings panel now detects and reports `SKILL.md` files that exceed 500 lines, explaining what to move to supporting files and providing remediation guidance.
* Users can now manually trigger a schedule sync for a project from the project Schedules tab. The sync re-reads and validates the `tessl.json` on the default branch and reconciles schedules, with progress polling and status feedback in the UI.
* Credit usage is now broken down by platform charges and AI token costs.
* GPT-6 Luna (`gpt-6-luna`) and GPT-6 Sol (`gpt-6-sol`) are now available as model selections for LLM key issuance and agent workflows.
* Automation trigger conditions now support recursive `not`, `any`, and `none` operator objects and a wildcard path projection for list members, enabling trigger rules that match or exclude events based on nested data.
* Eval run pages now show a redesigned layout with responsive result and metric cards, scenario comparisons and detail drawers, setup data, persisted run selection via URL, and an Activation section showing per-variant and per-scenario breakdowns of how often agents loaded each skill. The updated view is available in the workspace eval run view, public run links, and plugin eval tabs.

### Improvements

* Linear webhook deliveries are now processed exclusively through the factory trigger intake. The legacy Linear task-delegation path is removed: existing workflows built on it no longer function and must be rebuilt using factory triggers via `POST /experimental/webhooks/linear`. The `PATCH /integrations/:integrationId` and `GET /integrations/:integrationId/linear/metadata` endpoints reflect the updated integration shape.
* The deprecated `gpt-5-codex` and `gpt-5.2-codex` model IDs are no longer accepted for eval runs or agent workflows.
* The default eval model is now DeepSeek V4.1 Flash (`deepseek-v4.1-flash`), replacing DeepSeek V4 Flash, and the default review model is now GLM 5.3 (`glm-5.3`), replacing GLM 5.2. Several other deprecated model routes are removed from the catalogue.
* Workspaces can now create up to 100 active schedules by default, up from the previous limit of 20.
* When a schedule sync fails, the notifications are sent to the user who edited the schedule last, rather than routing to the workspace administrators
* Repo-defined scheduled runs now correctly reconcile the declaring manifest before installing and running the target skill.
* Launch runs can now be filtered by exact label via `GET /experimental/launch-runs`, enabling more reliable deduplication of run creation attempts.
* Skill quality and security scores are now correctly preserved across synchronizations when a skill is unchanged, preventing score regressions.
* Switching workspaces from the sidebar now keeps users on the same screen when the destination workspace supports it, carrying over stable view state.
* Users can now delete projects from the project overview page.
* Quality skill reviews no longer generate duplicate review runs when the `SKILL.md` content differs only in line endings or boundary blank lines, reducing redundant credit consumption.
* Organizations that spent their Free credit allowance and then upgraded to Team now correctly receive their full Team credit allowance.

### Bug fixes

* Disabling a skill review quality score policy (setting it to OFF) now correctly saves and displays as disabled rather than being silently converted to a score of 0.
* Visitors see a loading spinner and an accessible status message while login navigation is in progress, and the return destination is preserved correctly if the current page changes before login resolves.

## 2026-09-21

### Breaking changes

* The `snapshot` field is removed from repo-defined schedule entries in `tessl.json`. Schedules that declare `snapshot` are now rejected by schema validation. The field had no effect on schedule execution.

### New features

* Azure DevOps integration (Microsoft Entra ID) is now generally available to all entitled organizations. Connect an Azure DevOps organization to discover repositories, scan skills, rescan on demand, and manage scan policy from org integration settings.
* The login page now shows a region selector that navigates users to the correct regional login and remembers their choice in local storage.
* A new `dryRun: true` setting in `tessl.json` puts trigger automation in observation mode: conditions are traced, action fields are resolved, and each trigger logs whether it would have fired, without claiming deliveries or starting runs.
* Tessl Code Review credit usage per completed round has changed in both regions. See <https://tessl.io/pricing/> for current details.
* Org and workspace publish policies support an optional minimum skill quality score. When set, publishing a plugin version is blocked until every skill meets the configured threshold.
* The MCP server detail page shows a live 'Available tools' panel with per-tool descriptions, manual refresh, re-authorization prompts, and distinct error states for unavailability and protocol errors.
* Context improvement proposals can now target any Markdown file in the repository instead of only `AGENTS.md` and `CLAUDE.md`. Proposals go through an editor review step before being published, improving accuracy and placement. Repositories blocked by an unfinished proposal run are automatically released on the next dispatch.
* Eval run tables now show a skill activation column at the variant, scenario, and repeat levels: which skills were activated, the activation rate, and each activated skill's name in the expanded view.
* Eval runs using Codex CLI now capture per-turn tool call and token data, enabling turn-level analysis previously only available for Claude runs.
* Org admins can enable a 'Global install only' toggle in the managed dependencies policy editor to install managed plugins once per machine without per-project declarations.
* When Code Review fails because the organization has no credits, a comment is automatically posted on the pull request explaining the situation and linking to the pricing page.

### Improvements

* 'Enable reviews' on a repository row immediately saves the repository and opens settings pre-configured, without a second toggle inside the drawer.
* Scheduled runs are more reliable: duplicate fires are rejected, and a schedule targeting a deleted or archived workspace records a durable configuration failure instead of retrying indefinitely.
* `file:` skill references in nested `tessl.json` manifests now resolve relative to the manifest's own directory rather than the repository root, so multi-manifest repositories can use short relative paths without breakage.
* The integration scan history list on org settings shows 10 scans per page with a 'Load more scans' button, replacing the previous flat list.
* Quality scores and review results now reflect the selected context version: a version with no completed review returns null scores rather than stale scores from another version.
* Untracked findings now require complete first-party reference evidence before being reported; incomplete or indeterminate archive evidence is treated as unknown.
* Skill context occurrences now persist display name and description correctly on both fresh indexing and re-observation of unchanged file versions.

### Bug fixes

* Launch run resumes now expire seven days after the workspace was saved; attempts after the window receive HTTP 410. Launch runs retain their original run ID after sandbox recovery.
* The schedule detail page shows a human-readable description (e.g. 'Every Friday at 9:00 AM') instead of the raw cron expression; the raw string is still accessible on hover.
* Repo-defined schedules missing project links are automatically repaired after the next manifest sync. Schedules created from trigger-fired runs no longer inherit the original trigger event data.
* The Schedules breadcrumb on a launch run detail page now links to the owning project's Schedules tab. `GET /experimental/launch-runs/{id}` includes `scheduledRunId`.
* Multi-manifest repositories isolate each project's verifiers and context to the exact `tessl.json` that defines them.
* GitHub App accounts can request Code Review approval for pull requests they authored, restoring the address-then-approve workflow.
* Repository indexing and review dispatch follow source identity through renames and transfers.
* Authenticated users visiting private registry detail pages are no longer incorrectly redirected before authentication resolves.
* When creating an environment with conflicting GitHub access settings, the error message uses plain language and explains what to change.
* Eval runs uploading a local directory no longer fail with a fingerprint mismatch during staging.
* Launch sandboxes use a pinned, protected image, preventing run failures caused by the image being replaced during execution.
* Failed-scan alerts on the Home page link directly to the affected SCM provider's integration page and open retryable rows on arrival. Scan history appears inline in integration settings without requiring manager permissions.

## 2026-09-14

### New features

* Projects are now available at the organization level. Browse projects across your organization, filter them by their backing workspace, and open a project to view its Context, Findings, Evals, and Schedules.
* New `GET /v1/orgs/{orgId}/users` and `GET /v1/orgs/{orgId}/users/{userIdOrName}` list and look up the members of an organization you belong to, with the same response shapes as the user routes they replace. `GET /v1/users` and `GET /v1/users/{userIdOrName}` are deprecated in favor of them and remain fully supported. `GET /v1/workspaces/{workspaceIdOrName}` now includes `orgId` for callers who can view the workspace.

## 2026-09-03

### Breaking changes

* `GET /experimental/tasks` renames its link filters from `filter[link.type]` and `filter[link.value]` to `filter[linkType]` and `filter[linkValue]`. The dotted names are removed with no alias. The old spellings worked only because this route read those two query keys by hand; the shared filter parser matches field names with word characters only, so a dotted key never reaches a route that relies on the parser. The rename aligns these filters with that parser's vocabulary, so filter field names are camelCase throughout.

## 2026-09-02

### Breaking changes

* `GET /experimental/tasks/by-link` is removed. `GET /experimental/tasks` now accepts `filter[link.type]` and `filter[link.value]`, which return every task carrying the reference, newest first, with the same paging as any other task list. Each filter narrows on its own and the two compose.
* `tessl task by-link` is removed. Use `tessl task list --link-type` and `--link-value`.

### Features

* Several tasks can now link the same external reference. Previously a given pull request URL or Linear issue belonged to exactly one task per workspace, so when a later task reported the same reference it was silently dropped and never showed up in a lookup. A pull request worked on across several review rounds now lists every task that touched it.

## 2026-08-26

### New features

* Organizations now have their own Home, Context, and Findings pages that roll up every workspace in the org, so you can see context health, quality bands, and security findings across the whole organization in one place. Signed-in users now land on org Home by default, and the sidebar logo navigates there. Inventories owned directly by the org, with no workspace, now appear on these pages too.
* The organization Insights page shows organization-wide activity: active users, plugin install counts sourced from a nightly snapshot (total, daily series, and top plugins), and per-window timeframe labels on each activity graph. The `GET /experimental/orgs/{orgId}/insights` response carries an `installs` object and `window.installsThrough`.
* New scheduled runs get a plain-English schedule builder (every N hours, daily, weekly on chosen days, or monthly) with a time-of-day and a Timezone dropdown, keeping raw cron entry behind a toggle for expressions the builder cannot model. The Skill, Repository, and Environment fields become searchable comboboxes, the Agent and Model controls populate from the supported-agents endpoint, and the form links out to environment setup.
* The scheduled-run detail page shows the schedule author, a linked and summarized Skill, and each run's output as a pull request link or summary. The scheduled-runs table shows a plain-English schedule with its timezone, a Name and Description, and the last run's outcome. Scheduled runs accept an optional `description` on `POST` and `PATCH /experimental/scheduled-runs`.
* Launch runs can be cancelled through the new `POST /experimental/launch-runs/{id}/cancel` endpoint. Cancellation is idempotent, tears down the sandbox, and marks the run with a distinct `cancelled` status. Launch-run reads gain a derived `outcome` (succeeded, no-action, or failed with a reason) so an agent-reported failure is distinguishable from a completed run, plus a live `progress_phase`, `progress_message`, and `liveness` projection.
* The native remote MCP server gains a discover, validate, publish, and poll workflow for skill-backed plugins through `validate_plugin_for_publish`, `publish_plugin`, and `get_plugin_publish_status`, an optional pre-publish skill quality review through `review_skill` and `check_review_status`, and a read-only `list_workspaces` tool for workspace discovery. `validate_plugin_for_publish` now reports every detectable publishing problem in one call. The endpoint serves both the current MCP protocol (`2026-07-28`) and the legacy version (`2025-11-25`), and every authenticated tool call produces an audit event.
* Organizations can run and track a Claude marketplace sync at org scope through `POST /v1/orgs/{orgId}/claude-marketplace/sync`, `GET /v1/orgs/{orgId}/claude-marketplace/sync/latest`, and `GET /v1/orgs/{orgId}/claude-marketplace/sync/overview`. The integration settings page shows a live sync overview with per-plugin status, versions, Quality and Security columns, and run history, plus an Automatic sync toggle (`syncEnabled`) that pauses scheduled sync while keeping manual sync available.
* Workspace owners can delete eligible plugins from the plugin detail page through a Delete plugin action and confirmation, without Tessl-admin support. Policy rejections surface the exact reason.
* New `GET /v1/whoami` reports what a request is authenticated as, for both user sessions and API keys, discriminated on `data.type`. `GET /v1/users/me` is deprecated in favor of it.
* Feature availability can now be gated by region from a committed policy driven by the `TESSL_REGION` environment variable: routes marked `regionalFeature` return `404` where a feature is withheld, and the frontend hides it. The policy ships empty, so nothing is withheld yet.

### Improvements

* The BYO LLM gateway is now the *Customer Managed Gateway* throughout the product. It uses a high, medium, and low model tier selection instead of per-feature model dropdowns: `byo-provider` responses drop `featureDefaultModels` and carry `tierModels` instead, and enabling a gateway now requires a model for every tier. A gateway can be created with a connection and no models while routing is off, so you can save the connection first and add models later. The stored credential shows as a masked, read-only value with a Replace control.
* Linear can no longer be connected at organization level and is managed at workspace level only. The org-level Linear connect, callback, metadata, and config routes under `/v1/orgs/{orgId}/integrations` are removed, and Linear no longer appears in org integration listings. Existing workspace-level Linear connections keep working. In a hoisted organization with more than one source-control installation, a workspace's own connection is now preferred over the org's.
* The launch-run, scheduled-run, and task-run creation endpoints accept a GitHub repository as a bare `owner/repo` slug, an HTTPS URL, a scheme-less link, or SSH shorthand, normalize it to a canonical `https://github.com/owner/repo`, and reject unparseable or non-GitHub values with a `400`. These endpoints also let internal callers select the standard or Docker-capable sandbox image. Creating a scheduled run from an inline spec now requires an `environmentId` and rejects the request with a `400` up front instead of failing later at trigger time.
* A cloud launch that names no GitHub-token source (no uploaded env file and no workspace environment carrying a token) is now rejected with a `400` before the run, workflow, or 100-credit platform fee is created, and the error names both remedies. Checkout is blocked with a `503` unless the whole payment-to-credit pipeline is configured, so a card is never charged against a pipeline that cannot grant credits.
* Org read endpoints now include the organization `slug` on `GET /v1/orgs/{orgId}` and `GET /v1/users/{userId}/orgs`, and `/orgs` URLs accept an org id or slug with the slug canonical. `GET /experimental/launch-runs` and scheduled-run fire history include the configured `mcpServers`, `outcome`, `summary`, `pr`, and `triggerSource` fields. Workspace environments can carry MCP servers, which are applied to launch, scheduled, and task runs.
* Paginated remote MCP list tools now render an explicit page count that states it is not the total and include the exact `nextCursor`, so agents can page through every result. `list_organization_skills` and `get_skill` discover skills contained in published plugins and accept an optional `workspaceId` filter.
* Publishing a passing plugin now automatically starts finding enrichment for eligible security findings without blocking, delaying, or failing the publish. Creating an inventory now requires selecting a GitHub organization. The target-repository picker searches across every connected GitHub organization instead of one, and the scan-scope picker shows only repositories already in the policy with a searchable combobox to add more.
* The organization Home Most Installed chart ranks plugins by 30-day installs from the same source as the Skill Installs sparkline, so the two panels agree. The Active users tile is replaced by a New members tile that counts members by join date. Context score badges open a popover with the full quality, impact, and security breakdown. The Home CLI box gains a package-manager selector (`npm`, `yarn`, `pnpm`, `bun`) that updates the shown command and remembers the choice.
* During the Code Review beta, review rounds consume no credits while every round is still recorded in the usage ledger and shown at 0 credits. Support staff can grant 500 credits from the admin Grant Credits dropdown, and can reset a user's onboarding status from the admin user page.

### Bug fixes

* Free-tier organizations now reset their credit balance on a rolling monthly window anchored to their signup date, with month-end clamping, and the public credit status exposes an explicit `resetsAt`. Paid-plan organizations with no billing ledger are no longer given the free-tier allowance and resolve as unlimited instead of being wrongly blocked. Team checkout now succeeds for billing addresses outside the US and Canada that carry a state code, and legitimate Canadian territory addresses are accepted.
* A scheduled run's `nextRunAt` is now recalculated and stored when its cron or timezone changes or when a paused schedule resumes, so it is no longer blank after those operations. A completed launch run whose agent reported failure shows a distinct Agent Failed badge instead of reading as Completed.
* The "View in Context" link on a finding now lands on that finding's own Context item rather than an empty page; the findings response exposes `groupId` and the Context endpoint accepts `filter[groupId]`. Context tables gain sortable column headings and a repository filter. The org Home context-health bands are sourced from the context model over the same scope as the Context page, so the bands sum to the Total context figure.
* Charts on organization pages that had stopped drawing now render again, the org usage range no longer triggers a refetch loop, and several organization Home and Insights states now show static placeholders or a Try again control instead of flashing skeletons or asking for a full reload.

### Features

* The workspace sidebar now has an **Activity** item that replaces the separate **Eval Runs** and **Reviews** items, showing eval runs and reviews in one timeline.
* Workspace **Members** moved out of the sidebar and into **Settings**, where it is a card on the workspace Settings page.
* The organization switcher moved from the account menu at the bottom of the sidebar to a dropdown at the top of the sidebar. Organization **Members**, **Settings**, and **Admin Keys** open from that dropdown.
* The sidebar has a new collapsible **Resources** section with **Registry**, **Documentation**, and **Community**. The **Blog** link was removed.
* Search is now an icon in the sidebar header. Click it, or press `cmd-K` (`ctrl-K` on Windows and Linux), to open the search popover.

## 2026-07-31

### Features

* `POST /experimental/tasks`, `GET /experimental/tasks/{id}`, and `GET /experimental/tasks/by-link` responses now include a `links.web` field with the task's URL in the web app.

## 2026-07-24

### Features

* `GET /v1/workspaces/{workspaceNameOrID}/skill-inventories/{id}/findings` now returns a `repoActivity` field (`active`, `quiet`, or `dormant`) reflecting how recently a finding's repositories were pushed to, and accepts a `repoActivity` query parameter to filter results by one or more of these buckets.
* The canonical home banner on skill pages now shows a colored status badge (In sync, Stale, or Modified), plus when that status was last confirmed, matching the detail already shown in the "Also appears in" panel.
* `GET /experimental/tasks/{id}/runs` responses now include a `summary` field on completed runs when one is available, giving a short description of what the run did.
* The task timeline now shows a short summary of what a completed run did, alongside its existing status and links.

### Bug Fixes

* The Eval Runs pages no longer show an unrelated default-model notice banner at the top; pages now open directly with the heading and results table.
* Eval run detail pages now show an "Activation Run" label above the run heading for runs that measure activation, and the Skills Activated column shows "None" when a solution completed with confirmed zero activations; previously this looked identical to a run still in progress.
* The stale copy-state label on skill pages now reads "Last in sync {date}" instead of the contradictory "In sync until {date}", so the date shown accurately reflects when the copy last matched its source.
* Skill listings on the registry now correctly hide skills whose current review failed, and no longer hide skills based on stale historical review results for identical content.

## 2026-07-17

### Features

* Connected GitHub App integrations now show separate Manage and Remove actions in integration settings, in place of a single Remove button: Manage opens the installation's settings on GitHub, and Remove opens a confirmation dialog before disconnecting.

### Bug Fixes

* The Ignored repos picker on the inventory scans page now searches your connected provider directly, so repositories beyond the first page of previously scanned repos can be found and ignored.

## 2026-07-09

### Features

* The eval runs list now shows a single Injected Context column in place of the separate Type and Subject columns, plus a new Scenarios column showing where each run's scenarios live.

### Bug Fixes

* Removed an inconsistent focus-ring outline from breadcrumb links; other interactive elements are unaffected.

## 2026-07-08

### Features

* The eval run detail view now labels variants "Baseline" and "With context" (replacing the misleading "Without context" label) and shows a new Injected Context panel describing exactly what fixture, plugin, directory, or commit a run injected.

## 2026-07-06

### Bug Fixes

* Skill-overload warnings no longer count skills sitting in plain source directories that no agent actually loads; only genuinely root-loaded skills count toward the threshold.

## 2026-07-03

### Features

* Billing settings now show the Team plan's monthly price inline: free organizations see it next to the Upgrade to Team button before checkout, and Team organizations see it next to their plan name.
* `GET /v1/orgs/{orgId}/billing/subscription` now includes `monthlyPriceUsd` and `upgradePriceUsd` fields. `monthlyPriceUsd` is the current plan's monthly price (`null` for free or custom-priced plans); `upgradePriceUsd` is the Team plan's upgrade price (`null` once the org is already on a paid plan).
* Workspace member pages now support setting a default role that's automatically granted to every member of the workspace's organization, on top of any individual membership, useful for organizations that provision users via SSO.
* `PATCH /v1/workspaces/:id` now accepts an `orgDefaultRole` field. When set, every member of the workspace's owning organization is granted that role on the workspace (capped at `publisher`); omit it or set it to `null` to remove the default.

### Bug Fixes

* The org billing page's credit usage display now matches the sidebar meter: over-limit usage reads "You've used your credit allowance" instead of a negative remaining figure, and fractional credit amounts are floored consistently.

## 2026-07-02

### Features

* New `DELETE /v1/projects/:projectId` endpoint soft-deletes a project: it's removed from normal reads and its name and source binding are freed for reuse, while eval-run and scenario-generation history is preserved.

### Breaking Changes

* `GET /v1/orgs/{orgId}/credits` **no longer returns** the `bundles` field by default. Pass `?include=bundles` to include the purchasable credit-bundle catalogue in the response, as before.

## 2026-07-01

### Features

* Claude Sonnet 5 (`claude-sonnet-5`) is now available as an agent model for eval runs.
* The workspace Inventories Scans page has been rebuilt as a dashboard, with summary cards for estate growth, findings backlog, and scan health shown above the scans table.

## 2026-06-29

### Features

* Organizations can now subscribe to the Team plan and manage billing from organization settings: pay by card through a hosted checkout, get a monthly credit allowance, and buy additional credit bundles whenever you need them. Cancelling reverts the organization to the free plan at the end of the billing period.

### Bug Fixes

* Requests to `GET /v1/tiles/:workspaceName/:tileName/versions/:version/files/:filePath` with a malformed percent-encoded file path now return `404` instead of `500`. File paths containing a literal `%` are also served correctly.

## 2026-06-27

### Breaking Changes

* Review and fix runs that select a custom reviewer plugin or a non-default model **now require** a paid plan. An explicit reviewer or model override on `POST /experimental/review-runs` or `POST /experimental/fix-runs` now returns `403` for an organization without the entitlement; previously such requests were accepted or silently fell back to the default. Free organizations continue to run against the default reviewer and model with no change.

## 2026-06-25

### Features

* Workspace members can now start skill reviews, security reviews, and review-fix runs. Previously these required the publisher role or higher; running a review is now available at the member level.

## 2026-06-19

### Features

* Org install policies now support a git source allowlist. Under Source restrictions, admins can configure which git hosts and repository path prefixes are permitted; installs from any unlisted git source are blocked at evaluation time. Sources are specified as `github.com/org` or `github.com/org/repo` patterns.
* The install policy `sourceRestriction` rule now accepts a `gitSourceAllowlist` field: an array of `{ host, path }` objects. When present, policy evaluation blocks any git-sourced install whose source does not match a listed host and path prefix.

## 2026-06-18

### Features

* Skill inventories can now be deleted from the inventory list. Workspace managers and owners see a delete action with a confirmation dialog; deleted inventories and their scans are immediately hidden but the workspace can be re-scanned at any time.
* New `DELETE /v1/workspaces/:workspaceName/skill-inventories/:id` endpoint removes a skill inventory and hides it and all associated scans. Requires the `manage_integrations` permission.
* The inventory estate view now includes an Ignored repos panel for managing which repositories are excluded from future scans. Ignoring a repo purges its existing scan data and prevents it from being re-ingested on subsequent imports.
* New `GET`, `POST`, and `DELETE /v1/workspaces/{workspaceName}/skill-inventories/{id}/ignored-repos` endpoints let workspace admins manage which repositories are excluded from inventory scans. Ignoring a repo cascades a full data purge of its existing findings.
* The Inventories item now always appears in the workspace sidebar when the feature is enabled, even before any imports have been run. The empty state and failed-scan messages now show the `tessl inventory import` command.
* Install, publish, and dependency policies can now be set and viewed at the org, workspace, and project levels from a unified policy editor. The editor shows the effective merged policy with per-field provenance badges indicating whether each value is set at the current level or inherited from a parent.
* `GET /v1/policies/{type}/{id}/effective` now accepts a `?provenance=true` query parameter. The response includes a `policySource` field with per-field attribution showing which hierarchy level set each effective value.
* New users with pending org invitations are now shown them during sign-up, before a personal workspace is provisioned. Accepting an invite joins the invited org directly; skipping provisions a personal workspace as before.
* Eval runs now default to `deepseek-v4-flash` as the solver model (previously `glm-5.1`). Runs without an explicit `model` override pick up the new default; the scorer default remains `claude-sonnet-4-6`.

### Bug Fixes

* The plugin registry no longer shows stale installed-plugin content. Registry overview pages now always display the plugin's own declared skill documentation instead of falling back to vendored internal files.
* The managed requirements search dropdown in the policy editor now overlays the page correctly instead of pushing the save button and other form content down.
* Sending an org invitation now immediately refreshes the pending invitations list without requiring a page reload.

## 2026-06-17

### Features

* The inventory scan detail page now shows which first-party skills were added to or excluded from the index, with timestamps for added skills and reasons for excluded ones. A Re-index first-party skills button triggers a fresh indexing pass from the same page.

### Bug Fixes

* Skills submitted from repositories whose default branch is not `main` no longer get stuck in a sync error loop. The skill's source branch is now correctly recorded at submission time.

## 2026-06-11

### Features

* Review Runs now show what was reviewed: a registry-linked badge for tile skills, or the skill name for direct CLI review runs.
* Review run objects now include a `subject` field in their metadata: `{ type: "tile_skill", tileRef, registryUrl }` for tile-triggered reviews, or `{ type: "skill", name }` for CLI-initiated reviews. Runs created before this change omit the field.

### Bug Fixes

* Buttons rendered as links (navigation CTAs and similar) no longer show an underline on hover.

## 2026-06-04

### Bug Fixes

* Skill-name validation no longer rejects names containing `anthropic` or `claude` as substrings. Skills such as `claude-api` that were previously blocked now validate and score correctly.
* tile quality scores now include completed review run results. Previously, review run scores were missing from the quality calculation.

## 2026-05-28

### Features

* File downloads for tile versions with failed, errored, or pending moderation now return `403` with a `moderation_hidden` error code. Inline docs and steering are omitted from those version objects on `GET /v1/tiles`, `GET /v1/tiles/:ws/:tile/versions`, and `GET /v1/tiles/:ws/:tile/versions/:version`.
* tile pages now show a placeholder instead of inline docs and steering when a version is under moderation review, has failed moderation, or encountered a moderation error.
* Public tiles now enforce a platform-level critical-severity security threshold: publishing a version with critical-severity findings blocks moderation and prevents the version from becoming available, regardless of workspace security policy settings.
* Claude Opus 4.8 (`claude-opus-4-8`) is now available as an agent model for eval runs.

### Bug Fixes

* Security severity levels in moderation failure messages now display as 'Critical', 'High', 'Medium', and 'Low' rather than internal identifiers.
* Trend indicators on tile analytics now show 'New' when there is no baseline period to compare against, instead of an incorrect percentage.

## 2026-05-27

### Bug Fixes

* Eval-run detail pages now live under workspace-scoped URLs (`/workspaces/:workspace/eval-runs/:id`), so the sidebar shows the correct workspace navigation. Existing `/eval-runs/:id` links redirect automatically for workspace members; public run links continue to work without authentication.

## 2026-05-22

### Features

* `moderationStatus` and `moderationError` are now included in the version objects returned by `GET /v1/tiles`, `GET /v1/tiles/:ws/:tile`, `GET /v1/tiles/:ws/:tile/versions`, and `GET /v1/tiles/:ws/:tile/versions/:version`. `moderationStatus` is one of `pass`, `fail`, `error`, `pending`, or `skipped`; `moderationError` carries a safe, human-readable message when present. The deprecated `moderationPassed` boolean is unchanged; `moderationStatus` is the recommended replacement.
* The moderation status banner on tile pages now shows three distinct states: failed moderation (red), moderation error (orange), and review in progress (blue), each with copy tailored to the outcome.

## 2026-05-21

### Features

* Security review is now a required stage before a published tile version becomes available. After publishing, a version stays in `pending` moderation state until the security scan completes; a failed or errored scan results in a `fail` verdict and prevents the version from being accessible.
* tiles now show a pending moderation state immediately after publishing while security review runs; the version becomes available once review completes, and a failed scan keeps it offline.
* Workspace member pages now include a short description explaining that org membership is required before a workspace role can be assigned, with a direct link to the org members page.

### Bug Fixes

* The error toast shown after a failed skill submission now displays a close button and auto-dismisses.

## 2026-05-14

### Bug Fixes

* Publishing to a workspace where you lack the publisher role now returns `401 Unauthorized` with a message directing you to ask a workspace manager or owner (previously returned `404 Not Found`).

## 2026-05-13

### Features

* tiles and skills with no eval coverage now show an adjusted score: 80% of the review-based score at zero evals, ramping to full weight at three or more. Search ranking and score badges reflect this change.

## 2026-05-12

### Features

* The organization switcher has moved into the user menu with keyboard navigation. The workspace list now shows all workspaces across organizations, with an org badge when you belong to more than one.

### Breaking Changes

* The `allowUrlSources` and `allowFileSources` fields **have been removed** from the managed-project policy; `PUT /v1/workspaces/{workspaceId}/managed-project-policy` now returns an error if either field is sent. Existing policies are migrated automatically.

### Bug Fixes

* The workspace selector in the 'Add member to organization' form now correctly requires a selection before submitting.

## 2026-05-11

### Features

* `GET /v1/workspaces/:id` now accepts a workspace name in place of an ID.

## 2026-05-08

### Features

* Workspace settings now include toggles for public publishing, security reviews, and eval runs, accessible to workspace managers and above.
* New `GET`, `PUT`, and `DELETE /v1/workspaces/{workspaceId}/managed-project-policy` endpoints for managing workspace dependency-pinning policies.
* tiles in workspaces with security reviews disabled now show a disabled state instead of Pending on the security tab.

## 2026-05-07

### Features

* Starting an eval run or generating scenarios now returns 400 for workspaces with evals disabled.

### Bug Fixes

* Workspace created during onboarding now appears in the sidebar immediately without a page reload.

## 2026-05-06

### Features

* New /changelog page summarizing what shipped on Web and API each week.
* Onboarding now captures role and use case during signup.

### Bug Fixes

* UUID validators relaxed to accept any valid hex format.

## 2026-05-05

### Features

* Refreshed copy on workspace and org role descriptions to make permissions easier to understand at a glance.
* Creating a workspace now requires the org-admin permission.

### Bug Fixes

* Org and workspace selectors stay populated after a page refresh.

## 2026-05-01

### Features

* API endpoints are now tagged as `public` or `experimental` in the OpenAPI spec.

### Breaking Changes

* Editing workspace settings now requires the `edit_settings` permission (manager or higher).

## 2026-04-30

### Features

* Workspace Projects are now available to all customers.
* Eval-result emails redesigned with a clearer score breakdown.
* HTML files are now permitted in tile publishes.

## 2026-04-29

### Breaking Changes

* Legacy user-scoped API keys no longer accepted; switch to workspace- or org-level API keys.

### Bug Fixes

* Eval-run table rows are now real links so command-click opens them in a new tab.

## 2026-04-28

### Features

* Refreshed the favicon set across the web product.

## 2026-04-27

### Features

* Org admins can now change member roles.
* Org admins can now remove members from the organization.

## 2026-04-24

### Features

* Pending org invitations can now be listed, resent, and revoked from the UI.
* Related tiles and skills are back, now powered by semantic search.

### Bug Fixes

* Pending eval runs are now labeled "In Progress".

## 2026-04-23

### Features

* Added an organization switcher above the workspace selector.
* Workspace owners can now delete empty workspaces.
* Organizations can now be renamed directly from the UI.
* A single invitation can now add a user to multiple workspaces in an organization.
* Workspace managers can now browse organization members when adding workspace members.
* Eval-run page now renders activation results from agent runs.

## 2026-04-21

### Features

* Eval-run progress now renders as a per-scenario segmented bar.

## 2026-04-16

### Features

* New workspace settings page with a Block Public Tiles toggle.
* Email notifications are now sent on eval completion.

## 2026-04-15

### Features

* tile publishing now uses workspace-level API tokens.

## 2026-04-13

### Features

* tile lists can now be sorted by score, security, and impact.
* API-key authentication now accepted on `GET /users/me`.

## 2026-04-09

### Features

* Codex agent now available for eval runs.

## 2026-04-08

### Features

* Username now visible in the user dropdown menu.
* Login and signup pages refreshed with a new tessellated logo background.

## 2026-04-07

### Features

* Improved the error messages when tile moderation fails.

### Bug Fixes

* tiles with no score now show a Pending badge instead of a 404.

## 2026-04-02

### Features

* Authentication-provider buttons now use a consistent "Continue with" label.
* Removed the in-product tile-generation flow and the Request Docs CTAs.

## 2026-04-01

### Features

* Directory fixtures now supported in the V2 eval-run API.

## 2026-03-31

### Features

* Custom login and signup screens replace the previous hosted auth experience.
* CLI device-auth flow added so `tessl login` works from the terminal.

## 2026-03-30

### Features

* New API-key management UI rolled out.
* Failed security reviews now appear in the UI.
* Install CTA is now hidden on tiles and skills with critical-severity security findings.
* Email invitations are now rate-limited.


# Sharing Usage Data

Tessl is committed to our users’ privacy, and protecting your data is our top priority.

The nature of Tessl’s product requires it to see and participate in AI exchanges, and that may include sensitive and precious data. We take great care to earn the trust of handling such data, and want to be clear and transparent to our users about this.

#### Opt out of sharing telemetry and usage data

Tessl collects telemetry and usage data from its CLI and cloud service usage, and uses this to improve product quality, guide roadmap priorities and troubleshoot user issues. This may include conversations, code snippets and file contents used to guide Tessl actions.

All data is encrypted at rest and well protected, and you can ask to delete this data at any time.

You can easily opt out of data collection at any time by following the steps [here](/reference/configuration#opting-out-of-telemetry). Doing so will ensure Tessl doesn't see your data for local actions, and doesn't persist it for cloud data. Opting out will not disable or hinder your AI usage, but may make it harder to troubleshoot issues with support.

#### Tessl collaborates with highly trusted cloud LLM providers

Tessl partners with the top AI labs to service our users. We take great care to only use providers with a strong security posture, and while we pass some data to those services as part of our product’s operation, we do not grant those partners any rights to your data.

The most notable partners we work closely with are OpenAI, Anthropic and Google.

#### Tessl uses your data to make our products better for you

The sole purpose of data collection is to understand you, our users, and improve our products for you. We do not sell or monetize your data, and have absolutely no intent to do so.

We reserve the right to train on data collected from our free tier usage, but have never done so to date. Instead, we focus our AI tuning on public data and purposely created synthetic data sets.

If you have any questions or feedback, please email us at <privacy@tessl.io>. We may agree to custom enterprise terms for usage data on a case by case basis - please contact us to discuss this at <legal@tessl.io>. For more information on how we collect, use, disclose and process your personal data, see our full [Privacy & Cookie Policy](https://tessl.io/policies/privacy-cookies)


# Open Source Attribution

The Tessl Registry has been enriched by insights from many open source projects. While no code has been copied, we acknowledge the broader open source community whose work has inspired and informed these specs. All licenses and rights remain with the original project contributors.


