Metadata-Version: 2.5
Name: git-anvil
Version: 0.4.0
Summary: Create isolated multi-repository workspaces for engineering tasks
Author-email: Felix Scherz <felixwscherz@gmail.com>
Requires-Python: >=3.11
Requires-Dist: typer>=0.12
Description-Content-Type: text/markdown

# Anvil

```
                  _ _ 
                 (_) |
  __ _ _ ____   ___| |
 / _` | '_ \ \ / / | |
| (_| | | | \ V /| | |
 \__,_|_| |_|\_/ |_|_|
```

Create isolated multi-repository workspaces for engineering tasks.

## Installation

From PyPI (the published package is still named `git-anvil`):

```bash
uv tool install git-anvil
```

From GitHub:

```bash
uv tool install git+https://github.com/felixscherz/anvil
```

Both methods install the `anvil` command. The PyPI package keeps its existing distribution name because [`anvil` belongs to another project](https://pypi.org/project/anvil/).

## Getting started

### Create a workspace

Provide a target directory and one or more repository specifiers. Each specifier can be a local path to an existing Git repository or any remote URL accepted by `git clone`.

```bash
anvil create --target /tmp/workspaces/feature-abc \
  ~/repos/my-service \
  git@github.com:org/other-service.git
```

To override the inferred branch name, pass `--branch` (`-b`):

```bash
anvil create --target /tmp/workspaces/feature-abc --branch my-feature \
  ~/repos/my-service
```

Anvil will:

1. Infer the branch name from the target directory name (`feature-abc`), or use the value of `--branch` when provided.
2. For local paths — create a `git worktree` on a new branch at the tip of the default branch.
3. For remote URLs — clone the repository and check out a new branch.
4. Write a manifest to `/tmp/workspaces/feature-abc/.anvil/manifest.json`.
5. Write an `AGENTS.md` at the workspace root describing the workspace layout for coding agents. It points to `.anvil/manifest.json` as the source of truth for the current repositories, and is removed on `clean`.
6. Record the workspace in the nearest dedicated `.anvil/workspaces.json` inventory.

Example output:

```
Creating Anvil workspace: /tmp/workspaces/feature-abc
Branch: feature-abc
  + my-service -> /tmp/workspaces/feature-abc/my-service
  + other-service -> /tmp/workspaces/feature-abc/other-service

Created Anvil workspace: /tmp/workspaces/feature-abc
Branch: feature-abc
  - my-service -> /tmp/workspaces/feature-abc/my-service
  - other-service -> /tmp/workspaces/feature-abc/other-service
```

### Clean up a workspace

```bash
anvil clean /tmp/workspaces/feature-abc
```

`anvil remove` is an alias for `anvil clean`. Both remove the workspace from `workspaces.json` after cleanup.

If you are inside the workspace (or any subdirectory of it), the path can be omitted - Anvil walks up from the current directory until it finds an `.anvil/manifest.json`:

```bash
cd /tmp/workspaces/feature-abc
anvil clean
```

Anvil reads the manifest, prints a summary, and prompts for confirmation before removing everything.

```
Anvil workspace: /tmp/workspaces/feature-abc
Branch: feature-abc
Repositories (2):
  - my-service (worktree) -> /tmp/workspaces/feature-abc/my-service
  - other-service (clone) -> /tmp/workspaces/feature-abc/other-service
Remove Anvil workspace at /tmp/workspaces/feature-abc containing 2 repositories? [y/N]
```

Skip the prompt with `--yes`:

```bash
anvil clean /tmp/workspaces/feature-abc --yes
```

### Add a repository to an existing workspace

```bash
anvil add --target /tmp/workspaces/feature-abc ~/repos/another-service
```

Or, from anywhere inside the workspace:

```bash
cd /tmp/workspaces/feature-abc
anvil add ~/repos/another-service
```

Anvil reads the branch name from the existing manifest (`feature-abc`) and creates the new repository on that same branch. The manifest is updated in place with the new entry appended. Rollback applies only to repos added in the current run - existing workspace members are untouched. `clean` and `run` likewise discover the workspace from the current directory when the workspace path / `--workspace` is omitted.

### List workspaces

Create a dedicated `.anvil` directory in a parent of your workspaces, then run `anvil list` from anywhere beneath it:

```bash
mkdir -p /tmp/workspaces/.anvil
anvil list
```

Anvil walks upward from the current directory to find the nearest dedicated `.anvil` directory. A workspace's own `.anvil/manifest.json` directory is skipped. If there is no dedicated directory, Anvil uses `~/.config/anvil/workspaces.json`. For `create`, `add`, and `remove`, inventory selection starts at the workspace's parent directory, so explicit workspace paths update the inventory that contains them. The file records each workspace path, branch, and repository names; `add` refreshes its entry.

## Notes

- The branch name is derived from the basename of `--target`. `/tmp/workspaces/feature-abc` → `feature-abc`. Pass `--branch`/`-b` to override it; the value is sanitized the same way as the inferred name.
- `anvil add` reads the branch name from the existing manifest - the target directory name is irrelevant.
- `add`, `clean`, and `run` infer the workspace by walking up from the current directory when the workspace path / `--target` / `--workspace` is omitted. `create` always requires `--target` because it creates a new workspace.
- The same branch name is created in every repository in the workspace - this is expected and correct.
- If any repository fails during `create` or `add`, Anvil rolls back only the repos created in that run.
- Anvil will refuse to proceed if the target is non-empty, if a derived branch already exists, or if two repositories share the same derived name.
