Managing repositories

A repository lives in a workspace, and its slug is unique inside that workspace, not across every workspace. The commands use production. district login --staging pins staging for every later command, so the rest of the guide needs no environment flag.

Create a repository

district repo create creates the repository and prints its slug, its repo_ id, and its HTTPS git remote URL. Without --slug, district-api derives the slug from the name. A slug that a live repository of the workspace already uses is refused with the API's message.

$ district repo create "My App" --clone
created my-app (repo_01K4…)
remote https://…/repo_01K4….git
cloning into ./my-app

--clone clones into ./<slug> without asking. Without --clone, an interactive session asks Clone into ./<slug>? [Y/n], and an empty answer clones. A session with no terminal prints the git clone command to run instead.

The clone never asks for a password. Without the credential helper, git fails at once, and the message names the fix: district git-credential install.

Choose the workspace

A caller who can open exactly one workspace does not name one: district repo create and district repo list use that workspace. A caller who can open several gets an error that lists them. Run the command again with the workspace to use:

district repo create "My App" --workspace ws_01K4…

List repositories

district repo list prints a header row, then one row per repository of one workspace, most recent activity first. It does not read a clone: the list belongs to a workspace, not to the directory where the command runs.

$ district repo list
SLUG       REPOSITORY                       NAME       KIND        CREATED
my-app     repo_01K4X0J8W1QZ5B7N3G2R6TQ9YD  My App     hosted_app  2026-08-25T09:00:00Z
shopfront  repo_01K4X0J8W1QZ5B7N3G2R6TQ9YE  Shopfront  hosted_app  2026-08-24T09:00:00Z

A workspace with no repository prints nothing and exits 0. --first <n> reads n repositories, from 1 to 100; the default is 30. --json prints the rows as one JSON array. Each row includes the description, the default branch, and the remote URL. A repository that belongs to an applet is not listed: the applet owns it and deletes it with itself.

Clone an existing repository

district repo clone resolves a repository by repo_ id or by slug and clones it into ./<slug>, or into a directory you name:

district repo clone my-app          # by slug, into ./my-app
district repo clone repo_01K4…      # or by id
district repo clone my-app ./work   # into a directory you name

A slug that several workspaces hold asks for --workspace <id>. For plain git tooling, use the URL form: district repo url prints the remote URL and nothing else, which command substitution can read. The URL names the repository by id, not by slug, and does not change.

git clone "$(district repo url my-app)"

Delete a repository

district repo delete hides a repository, its remote and every branch it holds for 14 days. The slug stays held. A restoreDeletion call with the printed deletion ID brings them back until the printed time. The purge after the printed time deletes the repository and its remote. The command prints the resolved repository and the environment, then asks Delete <slug>? [y/N].

$ district repo delete my-app --yes
deleted my-app (repo_01K4X0J8W1QZ5B7N3G2R6TQ9YD, deletion del_01K4…, restorable until 2026-09-08T09:00:00Z)

The repository argument is required, and the clone in the current directory never names it: a command line that named nothing would otherwise delete the remote of the checkout you are standing in. A slug that several workspaces hold asks for --workspace <id>.

In a script, --yes skips the question. A session with no terminal needs the flag, because it can answer no question.

A repository that a hosted app, a board or a routine still uses is refused with the API's message, which names the resource to delete first. district app delete removes the app. A member with resource:delete in the workspace can delete a shared repository; only the owner can delete a private one. Every other caller is told the repository was not found.