# Repositories

**A project's repositories are the code its agents work in.** A project can carry several, and every task targets exactly one of them. Each repository brings its own default branch, its own setup command, and its own verification command, so a frontend that runs `npm test` can sit beside a service that runs `pytest` without either settling for the other's answer.

A project created without one of yours gets a managed repository instead — see [Projects without a GitHub repository](#projects-without-a-github-repository).

## Prerequisites

- A project in your Workspace.
- `manage` access on the project. Every write below refuses a client collaborator, including one with steer access.
- For a GitHub-backed repository, the hlix GitHub App installed on the owning account — see [GitHub](/integrations/github/).

## What a repository carries

| Field | What it does |
| --- | --- |
| `owner` / `name` | Repository identity. **Not** editable, because changing it would re-target every task that already ran against it. To point at another repository, detach this one and attach the other |
| `label` | An optional display name, for a project with several repositories |
| `defaultBranch` | The branch tasks are cut from when nothing else says which. Per repository — a frontend on `main` beside a service on `develop` is normal |
| `setupCommand` | A shell command run in every fresh task checkout **before** the coder starts. Up to 4,000 characters |
| `verifyCommand` | The build or test command run **after** the coder's turn. See [Verification](/coders/verification/) |
| `isPrimary` | The project's default repository for the surfaces that need one. Exactly one per project |

## Attach a repository

```bash
curl -sS -X POST "$HLIX_BASE_URL/v1/api/projects/$PROJECT_ID/repos" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID" \
  -H 'content-type: application/json' \
  -d '{"owner":"acme","name":"storefront","cloneUrl":"https://github.com/acme/storefront.git","defaultBranch":"main"}'
```

Expected result: `201` and the repository, with `isPrimary` set to `true` when it is the project's first.

The first repository attached becomes the primary. Move the flag later with `POST /v1/api/projects/{id}/repos/{repoId}/primary`, which demotes the current primary and promotes the new one in one step — there is no way to end up with two, or none.

**Never put a credential in a clone URL:** Send a bare `https://github.com/owner/name.git`. A URL carrying a username or token is stored as written, and Git echoes the URL in its error messages, so the credential would end up in logs. hlix authenticates through the GitHub App installation; the clone URL identifies the repository and carries no credential.

## Set the setup and verification commands

```bash
curl -sS -X PATCH "$HLIX_BASE_URL/v1/api/projects/$PROJECT_ID/repos/$REPO_ID" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID" \
  -H 'content-type: application/json' \
  -d '{"setupCommand":"npm ci","verifyCommand":"npm test"}'
```

Expected result: `200` and the updated repository.

`setupCommand`, `verifyCommand`, `defaultBranch` and `label` are the only patchable fields, and `null` clears either command. The update is strict: a field this endpoint does not apply is **rejected** with a `400`, so a `200` always means the change happened.

The dashboard exposes the same fields under **Project → Settings**, one row per repository, plus a project-wide verification default. See [The dashboard](/running/dashboard/#project-settings).

### Setup runs before the coder; verification runs after

```text
fresh checkout → setupCommand → the coder's turn → verifyCommand
```

A failed setup command fails the task **before** the coder runs, and is safe to retry: the checkout is recreated on the next attempt. Both commands are bounded at 10 minutes each.

Both commands run for every coder alike. The setup command inherits the project's resolved [secrets](/configure/secrets/), so an install against a private registry can authenticate.

## Projects without a GitHub repository

Every project has at least one repository. A project created without one of yours gets a **managed repository** that hlix hosts for it: tasks clone it, push their branches to it and review reads their diffs from it, exactly as with a connected repository. It appears in the project's repositories as the primary one, with its own setup and verification commands.

The managed repository belongs to the project: it cannot be detached, and it is deleted with the project. Attach a GitHub repository whenever you want the work there as well.

A project-wide `verifyCommand` applies to every repository that does not set its own:

```bash
curl -sS -X PATCH "$HLIX_BASE_URL/v1/api/projects/$PROJECT_ID" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID" \
  -H 'content-type: application/json' \
  -d '{"verifyCommand":"npm test"}'
```

Expected result: `200` and the updated project.

## Where a new project's code starts

A project created without a GitHub repository gets an HLIX Repos repository whose first commit is a **starter**: a small TypeScript project skeleton, one app per folder (Bun workspaces and Turborepo), with the app the brief asks for already in `apps/`, shared code in `packages/shared`, an `AGENTS.md` of project rules, and one passing end-to-end test. The first cycle builds on it instead of scaffolding from nothing.

| The brief asks for | The starter holds |
| --- | --- |
| A web application | `apps/web` (TanStack Start, Tailwind CSS) and `apps/api` (Hono), the web app calling the api through a typed client |
| A static or marketing site | `apps/site` (Astro, Tailwind CSS) |
| An API with no screens | `apps/api` (Hono) |
| A phone app | `apps/mobile` (Expo) |
| Anything else, or it is not clear | The skeleton with no app yet |

A stack you name wins: say "Next.js" or "Django" and the project starts from the bare skeleton rather than a starter you did not ask for. The project's preview and verification commands are filled in from the starter at creation, and the stack shown on the project names what the starter holds. A connected GitHub repository is never written to; it starts from whatever it already contains.

## Detach a repository

```bash
curl -sS -X DELETE "$HLIX_BASE_URL/v1/api/projects/$PROJECT_ID/repos/$REPO_ID" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID"
```

Expected result: `200` and `{"message": "Repository detached."}`.

Detaching removes the attachment, not the repository. Work already delivered to it stays where it is. A project always keeps at least one repository, so detaching the last one — or the managed repository — answers `409`.

## If attaching or updating a repository fails

- **`{"error":"This repository is already attached to this project."}` with `409`.** Nothing to do — it is already there.
- **`{"error":"This repository is already attached to another project in this workspace."}` with `409`.** A repository belongs to exactly one project per Workspace. The response carries the holding `projectId`; detach it there first.
- **`{"error":"Repository not found"}` with `404`.** The repository id is not one of *this* project's. A repository in another project answers `404` through this project's URL.
- **`{"error":"Project not found"}` with `404`.** The project id is not in the Workspace named by `X-Organization-Id`. A project in another Workspace is not-found, never forbidden.
- **`{"error":"No fields to update"}` with `400`.** The patch body was empty.
- **A `400` naming a field you sent.** Identity fields (`owner`, `name`, `cloneUrl`) are not patchable, and an unknown key is refused rather than ignored.
- **A `403` on any write.** These all need `manage` access on the project.
- **Tasks fail with a branch error.** ``Branch "develop" not found in acme/storefront. Check that the branch exists and the GitHub token has access.`` — the `defaultBranch` names a branch that is not there, or the installation cannot see it.
- **Tasks fail naming a missing GitHub App installation.** The account that owns the repository has no hlix GitHub App installation in this Workspace. Install the hlix GitHub App on that account. See [GitHub](/integrations/github/).

## Where to go next

| If you want to… | Read |
| --- | --- |
| Understand the verification gate in full | [Verification](/coders/verification/) |
| Connect the GitHub account | [GitHub](/integrations/github/) |
| See the sandbox these commands run in | [The Coding Workspace](/concepts/coding-workspace/) |
| Bring an existing folder in | [Move an existing project to hlix](/guides/bring-an-existing-project/) |

## Next steps

[Verification](/coders/verification/)
  [GitHub](/integrations/github/)
  [Environments](/configure/environments/)
  [Delivery policy](/configure/delivery-policy/)