Multi-repo · one session

One agent session across multiple repositories

A contract lives in api. Its client lives in web. It is one change, but git sees two repos and your agent sees one of them.

$curl -fsSL ivar.run/install | sh
acme/hall: billing-currency4 repos
  1. $ git clone git@github.com:acme/hall.git && cd hall
  2. $ ivar sync
  3. reading ivar.json: 4 repos, default branch main
  4. api bare clone · worktree main · setup script ok
  5. web bare clone · worktree main · setup script ok
  6. shared bare clone · worktree main
  7. infra bare clone · worktree main
  8. claude-code and opencode config materialised
  9. ready in 41s; nothing was checked out twice
  1. $ ivar feature create billing-currency --repos api,web
  2. api worktree cut · feat/billing-currency
  3. web worktree cut · feat/billing-currency
  4. · shared, infra read-only: ivar feature promote shared
  5. $ ivar session start billing-currency
  6. view dir ~/acme/.ivar/sessions/billing-currency
  7. skills api--openapi-contract · web--storybook · commit-style
  8. context AGENTS.md → api/AGENTS.md, web/AGENTS.md
  9. edit api/openapi.json Money.currency: string → enum
  10. edit web/src/api/billing.ts ↳ from api/openapi.json; same session, no handoff
  1. $ ivar feature deliver billing-currency
  2. api pushed feat/billing-currency
  3. web pushed feat/billing-currency
  4. acme/api#412 Money.currency becomes an enum
  5. acme/web#288 regenerate billing client
  6. part of acme/api#412
  7. 2 pull requests, one feature. Merge order is still yours.
api, web promotedshared, infra read-onlyivar v0.0.0 · claude-code

01The view directory

One directory on one branch for every repo your feature touches

A hall is a directory that owns a set of repos, described by a committed ivar.json. Each repo has one bare clone. Every working copy, including the default branch, is a git worktree from that clone. A change here changes that repository. You do not copy repos or remember another sync step.

~/acme/.ivar/sessions/billing-currency2 / 4

On disk

billing-currency/
├── api/          worktree · feat/billing-currency
│   └── openapi.json  modified
├── web/          worktree · feat/billing-currency
│   └── src/api/billing.ts  modified
├── shared/ read-only until promoted
├── infra/ read-only until promoted
├── skills/
│   ├── api--openapi-contract
│   ├── web--storybook
│   └── commit-style   hall-wide
└── AGENTS.md     → api/AGENTS.md, web/AGENTS.md

ivar.json: committed to the hall

{
  "repos": [
    { "name": "api",
      "url": "git@github.com:acme/api.git" },
    { "name": "web",
      "url": "git@github.com:acme/web.git" },
    { "name": "shared",
      "url": "git@github.com:acme/shared.git" },
    { "name": "infra",
      "url": "git@github.com:acme/infra.git" }
  ],
  "providers": ["claude-code", "opencode"]
}

Committed. git pull && ivar sync gives the next person all four at once.
4 bare clones4 worktrees · 0 duplicate checkoutsworktrees, not clones
same branch
A feature uses one branch across many repos. You do not keep four branches aligned or pin a submodule SHA.
bare once
Nothing is checked out into the bare clone. A second feature adds a directory, not another clone. Both features can stay live at the same time.
committed
The hall is a git repo. ivar.json lives in its history, so a pull request can review the setup like any other change.

02The guard

The filesystem stops writes before a prompt can

Repos you have not promoted have their write bits cleared: mode & ~0o222, applied recursively. The kernel refuses an agent's attempt to enter shared. The harness hook only explains how to promote it.

claude-code: billing-currency2 / 4
  1. edit shared/src/money.ts
  2. ✗ EACCES: permission denied, open 'shared/src/money.ts'
  3. shared is read-only in this feature.
  4. Run this to make it writable:
  5. ivar feature promote shared
  6. $ ivar feature promote shared
  7. shared worktree cut · feat/billing-currency
  8. write bits restored · 3 of 4 promoted
  9. edit shared/src/money.ts ok: deliberate and recorded in the feature
mode & ~0o222recursive, on every unpromoted worktreeenforced by the kernel

03Deliver

One feature, one pull request per repo, with links between them

ivar feature deliver pushes each promoted worktree and opens a pull request in its repo. It then adds the links, because the URL exists only after the pull request. Reviewers no longer need to reconstruct which changes belong together.

The link says part of, not depends on. ivar models co-belonging rather than dependency. It does not sequence merges or decide whether the client can land before the contract. Reviewers make that call.

billing-currency: opened by ivar feature deliver3 PRs
  • api#412Money.currency becomes an enumopens the change
  • web#288Regenerate the billing clientpart of acme/api#412
  • shared#91Add Currency to the money typespart of acme/api#412
3 pull requestsone branch name · feat/billing-currencymerge order is yours

Capabilities

The session carries the context a shell script cannot

Onboarding

One person writes the hall once, reviewed in a pull request. Everyone else gets the same repos, branches and setup scripts with two commands.

git clone git@github.com:acme/hall.git && cd hall && ivar sync

Namespaced skills

Repo skills keep their origin in their name, so two repos can both mount an openapi-contract skill. Hall skills mount flat and apply to the whole session.

api--openapi-contract · web--openapi-contract

Session relay

Nothing in the view directory belongs to one vendor. The same worktrees, branch and context hand off to another harness; nothing is uploaded.

ivar session relay billing-currency --to opencode

The fair objection

Why not git worktree and a shell script?

Write the script. It is forty lines and it works. These are the limits you will meet, in order.

Cutting the worktrees

Loop over four repos and run git worktree add with the same branch name. The script handles this.

the script wins

Keeping the agent out

Every repo in the directory is writable. You can ask the agent not to touch shared. It eventually will.

chmod, not a prompt

Two repos, one skill name

Both openapi-contract skills land in the same folder. One silently wins. You find out later.

prefix by origin

The next person

Your script is on your machine. Their four repos are on theirs, on whatever branch they last used.

a committed manifest

Prior art

Other tools and what they actually do

Each comparison states what the tool does and does not do, checked against its source and commit history. That includes tools better than ivar at their own job.

Monorepo vs multi-repo conceptgit submodule pins a SHAgit subtree vendors historyAndroid repo branches in placeGit Repo Manager worktree, one repoNx build graphTurborepo shared cacheGradle composite JVM buildsclaude-squad single repoConductor macOS, closedVibe Kanban task boardSuperset source-available

Get it running

Install ivar in thirty seconds

It makes more sense after you use it. macOS and Linux work. On Windows, use WSL because the view directory is built entirely from symlinks.

$curl -fsSL ivar.run/install | sh