DEV Community

Cover image for Rename an API Field Across 3 Repos in One Claude Code Task (No Monorepo)
Gabriel Anhaia
Gabriel Anhaia

Posted on AI-assisted

Rename an API Field Across 3 Repos in One Claude Code Task (No Monorepo)


The API pull request was green. The types package had no open changes. Then the order page in the web app started rendering an empty address block, because order.shipTo was now undefined.

The agent did what you asked. You told Claude Code to rename shipTo to shippingAddress on the order response, from inside orders-api, and it renamed every use it could find. The two repos that read that field were never in the folder it could see.

Three sibling checkouts, nothing shared but a parent folder:

~/code/
├── orders-api/     # GET /orders/:id returns the order
├── orders-types/   # @acme/orders-types, the shared types
└── web-app/        # React client, imports the types package
Enter fullscreen mode Exit fullscreen mode

The field lives in three places: the type in orders-types, the serializer in orders-api, and every component in web-app that reads it. By default, Claude Code "has access to files in the directory where you launched it." Launch it in orders-api and the other two repos sit outside that line, so the agent finishes the rename where it is allowed to look.

You don't need a monorepo to fix this. You need to hand the agent all three folders, tell it how they relate, and check for stale callers before anything merges.

Three separate wooden crates on a workbench, joined by one burnt-orange thread that a single hand reaches across to pull taut
One contract, three repositories. The agent has to be able to reach all of them.

Step 1: give Claude Code the other two folders

The permissions docs list three ways to widen that line:

  • --add-dir <path> when you start a session
  • /add-dir in the middle of one
  • permissions.additionalDirectories in a settings file, to keep the folders across sessions

Start from the API repo:

cd ~/code/orders-api
claude --add-dir ../orders-types ../web-app
Enter fullscreen mode Exit fullscreen mode

The CLI reference passes two paths to one flag the same way: claude --add-dir ../apps ../lib. Files in the added folders "follow the same permission rules as the original working directory." Claude reads them without asking, and edits follow whatever permission mode you are in.

Two things don't come along, and both matter for this task.

The first is the other repos' instructions. Per the memory docs, CLAUDE.md files from added directories are not loaded by default. If web-app has its own CLAUDE.md with its test command and its conventions, turn that on:

CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 \
  claude --add-dir ../orders-types ../web-app
Enter fullscreen mode Exit fullscreen mode

That loads CLAUDE.md, .claude/CLAUDE.md, .claude/rules/*.md and CLAUDE.local.md from each added folder.

The second is most of their configuration. In the docs' words, additional directories "grant file access, not configuration." Skills, commands and subagents in an added folder's .claude/ do load. Hooks and most settings.json keys come only from the folder you launched in, so a format-on-edit hook that lives in web-app won't fire in this session. Folders listed in permissions.additionalDirectories get file access and none of those exceptions, which makes the flag the better fit for a one-off cross-repo change.

If you also use Codex, its CLI has a flag with the same name. OpenAI's reference describes --add-dir as "Grant additional directories write access alongside the main workspace."

Step 2: write the map into CLAUDE.md

Access isn't understanding. The agent can now read web-app, but nothing tells it that web-app calls this API, or which repo goes first. Put that in the API repo's CLAUDE.md, which loads in every session you start there:

## Related repositories

This API has two consumers, usually added with --add-dir:

- orders-types: the shared response types,
  published to npm as @acme/orders-types.
- web-app: the React client. It calls
  GET /orders/:id and imports @acme/orders-types.

A change to a response field touches all three repos.
Work in this order: orders-types, orders-api, web-app.
Run `npm test` in every repo you edit.
After a rename, run scripts/stale-callers.sh with the
old name and the path of every repo you edited.
Enter fullscreen mode Exit fullscreen mode

Name the repos instead of hard-coding ../web-app. Relative paths break as soon as the session runs from somewhere else, and Claude Code's own --worktree flag starts you in <repo>/.claude/worktrees/<name> (CLI reference), where ../web-app points at the wrong folder.

The script the map mentions is a few lines of git grep, run in each repo through git -C:

#!/bin/sh
# usage: stale-callers.sh <old-name> <repo>...
# Lists remaining whole-word uses of a name in each repo.
old="$1"; shift
for repo in "$@"; do
  echo "== $repo"
  git -C "$repo" grep -n -w --untracked -e "$old"
done
Enter fullscreen mode Exit fullscreen mode

-w matches at word boundaries, so shipToCountry doesn't count. --untracked also searches new files the agent created that nobody has added yet, as long as they aren't gitignored. -n prints line numbers. The script won't catch a key built from strings at runtime, or a ship_to column in a SQL file. If your API maps snake_case columns, run it a second time with that name.

Step 3: one prompt, three diffs

Run the checklist at the end of the post before you send this.

One more catch first. web-app reads the types from its node_modules, not from your orders-types checkout, so its tests would check order.shippingAddress against the old published types. Point it at the local copy for the length of the task:

(cd ../web-app && npm install ../orders-types)
Enter fullscreen mode Exit fullscreen mode

Per the npm docs, installing a folder outside the project creates a symlink to it, and npm install saves the package into dependencies by default, so web-app/package.json now points at that local path. If the types package builds to dist/, it needs a rebuild after the agent edits it. Put package.json and the lockfile back before you commit, and publish orders-types before the web app ships.

Rename the `shipTo` field on the order response to
`shippingAddress`. Follow the repo order in CLAUDE.md.
Don't change any other field. Run the tests in each
repo you touch. Then run scripts/stale-callers.sh
shipTo with all three repo paths and show me the output.
Enter fullscreen mode Exit fullscreen mode

What you want back is a diff in each repo and nothing under each == header. Nothing there means no tracked or untracked file in those three repos still uses shipTo as a whole word. It doesn't mean the change is right, so read every diff yourself:

git -C ../orders-types diff
git diff
git -C ../web-app diff
Enter fullscreen mode Exit fullscreen mode

Then commit in each repo and open one pull request per repo. Claude worked across three folders, but git still sees three repositories with three histories. The one thing a monorepo would have given you for free, a single atomic commit, you don't get.

The same task in Hermes IDE

I build Hermes IDE, so weigh this section with that in mind. Since 2.3.0, one task can span several folders, and the launcher shows you the state of each one before the agent starts.

Press ⌘N (Ctrl+Shift+N on Windows and Linux) and describe the task. Click the project chip (⌘P on macOS) to open the project menu, search for orders-api and press Enter to make it the main project. Then tick orders-types and web-app, or press Shift+Enter on each. A task takes up to five extra folders.

Hermes IDE project menu with orders-api as the main project and orders-types and web-app ticked as extra folders, each showing its branch
The project menu: orders-api is the main project, the other two ride along.

Close the menu and the launcher sums it up. The chip reads orders-api +2, and the line under the chips lists each extra folder with its branch and its uncommitted changes. That line is the point. Here orders-types is already on rename-ship-to, and web-app is still on main with two modified files: the dirty tree from checklist item 1, caught before the agent writes a line instead of after.

Hermes IDE new-task sheet: the rename task typed in, the project chip reading orders-api +2, and the Also line showing orders-types on rename-ship-to and web-app on main with 2 changes
Before you press Launch: one task, three folders, and the branch and changes of each.

Press Launch and Hermes starts Claude Code with --add-dir for every extra folder, the same flag from step 1, without typing a path. It also refuses folders that would confuse the agent, such as the same repository picked twice or a folder that sits inside your main project.

What you get, and what you don't:

  • The main project gets its own worktree on a new hermes/ branch, so the API change stays isolated. Each agent reply becomes a chip you can open to see its diff or restore your files to that point, and you land the branch when it's done.
  • Extra folders are edited in place, on whatever branch they're on. The launcher says so right on the Also line ("edited in place, not on the task branch"), which is why the branch from checklist item 2 matters.
  • The reply chips track the main project only, and Hermes says so in a multi-folder task. Review orders-types and web-app with git diff, as in step 3.

The flags, the paths and the "is that repo clean?" check are the parts that get skipped at 6 pm. Hermes puts them on one screen before anything runs.

Checklist: before you press Enter, and after you merge

Half-merged contracts usually start before the agent writes a line: a dirty extra folder, a missing branch, or no agreed landing order. Check the first three items up front, whichever tool you use.

1. Clean trees. In each extra folder:

git -C ../web-app status --short --branch
Enter fullscreen mode Exit fullscreen mode

With --branch, the first line starts with ## and names the branch. Any line after it is a change that is already there. Commit or stash it first, or the agent's edits end up in the same diff as yours.

2. The right branch in every extra folder. Neither --add-dir nor Hermes's extra folders create a branch there. Make one yourself with git switch -c:

git -C ../orders-types switch -c rename-ship-to
git -C ../web-app switch -c rename-ship-to
Enter fullscreen mode Exit fullscreen mode

The same branch name in each repo makes the pull requests easy to pair up. In Hermes, the line under the chips is where you confirm it took.

Three wooden crates lined up as steps, with a burnt-orange thread running from the first to the last, each crate placed one after the other
Repos don't merge together. Decide the order before the code exists.

3. A landing order. If all three ship in one deploy, the plain rename from step 3 is fine. If the API and the web app deploy separately, renaming in one step breaks whichever side ships second. Use a parallel change instead. As Danilo Sato puts it: "In the expand phase you augment the interface to support both the old and the new versions." For this field:

  1. orders-types: add shippingAddress, mark shipTo deprecated, publish.
  2. orders-api: send both fields.
  3. web-app: bump the types package, read shippingAddress.
  4. A second, smaller task removes shipTo from the types and the API once the web app is live.

Ask the agent for steps 1 to 3 only, and link each pull request to the other two.

4. Grep again after the last merge. Pull each repo, then rerun stale-callers.sh shipTo across all three. After step 4 it should print three headers and nothing under them.


If this was useful

An agent can only respect a contract it can see, so most of the work here is choosing what it sees and checking what it missed. If you want the extra-folder picker, download Hermes IDE: it's free, source-available (BSL 1.1), runs on macOS, Windows and Linux, and isn't affiliated with Anthropic, OpenAI or Nous Research. If it saves you a broken client, star it on GitHub. For the patterns behind scoping what an agent can reach and touch, the AI Agents Pocket Guide is the short read.

AI Agents Pocket Guide: Patterns for Building Autonomous Systems with LLMs

Top comments (1)

Collapse
 
fable7 profile image
fable7 •

Solid walkthrough. The --add-dir vs permissions.additionalDirectories distinction (file access, not configuration, hooks don't travel) is the part most posts skip.

One gap in the stale-callers step: git grep -w won't see the old name inside JSON test fixtures, mocked responses, or OpenAPI-generated clients. That's exactly the failure mode I hit porting a plugin to OpenCode 2 - the response shape changed, my mock still had the v1 body, so every test stayed green. Out of 5 calls against 2 real servers: 3 threw, 1 returned HTTP 200 with a body that didn't match the v1 schema, 1 was actually fine. Only calling the real endpoint and asserting on the required keys caught it.

Would you add a "hit the real endpoint once, assert required keys" line next to the grep, or do you regenerate the generated clients before the rename and grep after?