> For the complete documentation index, see [llms.txt](https://docs.tessl.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tessl.io/reference/configuration.md).

# 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.

```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. See [Setting up agentic code review](/tutorials/setting-up-agentic-code-review.md) for the end-to-end workflow.

### 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.md).

### 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.md) 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
```

## 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.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.tessl.io/reference/configuration.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
