> ## 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`**

```ts theme={null}
const repos = await mesa.repos.list({ limit: 50 });

for (const repo of repos.repos) {
  console.log(repo.name, repo.default_bookmark, repo.head_change_id);
}
```

## Options

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

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

<ParamField path="tags" type="RepoTagFilter | string | undefined">
  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 an object where each key is a tag name:

```ts 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 object are implicitly combined with the logical `$and` operator. The filter above is equivalent to:

```ts 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:

```ts 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 object 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.

```ts 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:

```ts theme={null}
const 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 TypeScript SDK serializes filter objects 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:

```ts theme={null}
const tags = JSON.stringify({
  $and: [{ environment: 'prod' }, { $not: { lifecycle: 'archived' } }],
});

await fetch(`/v1/acme/repos?tags=${encodeURIComponent(tags)}`);
```

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:

```ts theme={null}
import { repo } from '@mesadev/sdk';

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

const fs = await mesa
  .fs({
    layout: {
      '/skills': repos.map((r) => repo(r.name, { mode: 'ro' })),
    },
    authors: [{ name: 'Workspace Agent', email: 'agent@example.com' }],
  })
  .mount();
```

## Response

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

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

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

### Repo

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

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

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

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

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

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

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

<ResponseField name="tags" type="Record<string, string>">
  Repository tags.
</ResponseField>

### UpstreamConfig

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

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