> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mesa.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# repos.list()

> List repositories in an organization.

List repositories using cursor pagination.

**Required scope: `read`**

```python theme={null}
repos = await mesa.repos.list(limit=50)

for repo in repos.repos:
    print(repo.name, repo.default_bookmark, repo.head_change_id)
```

## Options

<ParamField path="cursor" type="str | None">
  Opaque pagination cursor from a previous response.
</ParamField>

<ParamField path="limit" type="int | None">
  Maximum number of repositories to return. The server maximum is `100`.
</ParamField>

<ParamField path="tags" type="str | dict[str, object] | None">
  Filter results by repository tags. See [Tag Filters](#tag-filters) below.
</ParamField>

### Tag Filters

Filter the repository list by the tags on each repository. Pass a dictionary where each key is a tag name:

```python theme={null}
await mesa.repos.list(
    tags={
        "kind": "memory",  # kind equals "memory"
        "environment": "prod",  # environment equals "prod"
        "region": ["us-east-1", "us-west-2"],  # region is either value
    }
)
```

A repository matches when every key matches: multiple keys in one dictionary are implicitly combined with the logical `$and` operator. The filter above is equivalent to:

```python theme={null}
await mesa.repos.list(
    tags={
        "$and": [{"kind": "memory"}, {"environment": "prod"}, {"region": ["us-east-1", "us-west-2"]}],
    }
)
```

Combine `$and`, `$or`, and `$not` to build more complex queries:

```python theme={null}
await mesa.repos.list(
    tags={
        "$and": [
            {"workload": {"$in": ["sync", "index"]}},
            {"$or": [{"environment": "prod"}, {"environment": "staging"}]},
            {"$not": {"lifecycle": "archived"}},  # excludes archived repos
        ]
    }
)
```

Operator names start with `$` and are case-insensitive (`$and`, `$AND`, and `$AnD` are equivalent). The `$` prefix is reserved for operators, so a tag name can never start with `$` — but plain `and`, `or`, and `not` are ordinary tag names.

#### Value Operators

Tag values match exactly by default. Wrap the value in an operator dictionary for other comparisons:

| Operator       | Example                                           | Matches                            |
| -------------- | ------------------------------------------------- | ---------------------------------- |
| `$eq`          | `{"environment": {"$eq": "prod"}}`                | Exact tag value                    |
| `$in`          | `{"region": {"$in": ["us-east-1", "us-west-2"]}}` | Any listed tag value               |
| `$contains`    | `{"owner": {"$contains": "agent"}}`               | Tag value contains literal text    |
| `$starts_with` | `{"region": {"$starts_with": "us-"}}`             | Tag value starts with literal text |
| `$ends_with`   | `{"owner": {"$ends_with": "@platform"}}`          | Tag value ends with literal text   |
| `$exists`      | `{"archived": {"$exists": False}}`                | Tag key presence or absence        |

`$exists` checks only whether the tag key is present, regardless of its value. To match a specific value, use an exact match instead.

```python theme={null}
await mesa.repos.list(
    tags={
        "archived": {"$exists": False},
        "lifecycle": {"$eq": "active"},
    }
)
```

#### Complex Composition

This example selects active production-like skill or memory repos that are owned by platform teams and located in US regions:

```python theme={null}
result = await mesa.repos.list(
    tags={
        "$and": [
            {"environment": {"$in": ["prod", "staging"]}},
            {"region": {"$starts_with": "us-"}},
            {
                "$or": [
                    {"kind": "skill", "owner": {"$contains": "agent"}},
                    {"kind": "memory", "owner": {"$ends_with": "@platform"}},
                ]
            },
            {"$not": {"lifecycle": "archived"}},
            {"archived": {"$exists": False}},
        ]
    }
)
```

Matching semantics:

* Everything is case-insensitive: operator names, tag keys (normalized to lowercase), and tag value comparisons.
* `$and` matches when all child filters match, `$or` when any child matches, and `$not` negates its child filter.
* `$contains`, `$starts_with`, and `$ends_with` match literal text — `%`, `_`, and `\` in the search text are escaped, not treated as wildcards.
* Filters are limited to depth `8`, `64` total clauses, `32` items in `$and`, `$or`, and `$in` arrays, and `256` characters per tag key or value.

The Python SDK serializes filter dictionaries and sends them to the API. Invalid filters return `400 INVALID_REQUEST` from the server.

For direct REST calls, pass the filter as a JSON-encoded string in the `tags` query parameter:

```python theme={null}
import json
from urllib.parse import quote

tags = json.dumps({"$and": [{"environment": "prod"}, {"$not": {"lifecycle": "archived"}}]})
url = f"/v1/acme/repos?tags={quote(tags, safe='')}"
```

Legacy comma-separated string filters (`"environment:prod,team:core"`) are still accepted but deprecated.

Map listed repositories into a layout to mount the filtered set:

```python theme={null}
from mesa_sdk import repo

result = await mesa.repos.list(tags={"$and": [{"environment": "prod"}, {"kind": "skill"}]})

async with mesa.fs(
    layout={"/skills": [repo(r.name, mode="ro") for r in result.repos]},
    authors=[{"name": "Workspace Agent", "email": "agent@example.com"}],
).mount() as fs:
    ...
```

## Response

<ResponseField name="next_cursor" type="str | None">
  Cursor for the next page, or null when no more results remain.
</ResponseField>

<ResponseField name="has_more" type="bool">
  Whether another page of results is available.
</ResponseField>

<ResponseField name="repos" type="list[Repo]">
  Repository objects.
</ResponseField>

### Repo

<ResponseField name="id" type="str">
  Repository ID.
</ResponseField>

<ResponseField name="org" type="str">
  Organization slug.
</ResponseField>

<ResponseField name="name" type="str">
  Repository name.
</ResponseField>

<ResponseField name="default_bookmark" type="str">
  Default bookmark name.
</ResponseField>

<ResponseField name="head_change_id" type="str">
  Current change ID at the default bookmark tip.
</ResponseField>

<ResponseField name="upstream" type="UpstreamConfig | None">
  Configured upstream remote, or null when no upstream is configured.
</ResponseField>

<ResponseField name="created_at" type="datetime">
  Creation time.
</ResponseField>

<ResponseField name="tags" type="dict[str, str]">
  Repository tags.
</ResponseField>

### UpstreamConfig

<ResponseField name="url" type="str">
  Upstream Git remote URL.
</ResponseField>

<ResponseField name="auth_kind" type="'token' | 'username_password' | None">
  Stored upstream authentication kind, or null when the upstream is public.
</ResponseField>
