<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Stefan Lendl - Embedded Rust &amp; Systems Engineering - Blog</title>
    <subtitle>Freelance software engineering services specializing in embedded Rust, embedded Linux, and systems architecture.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://lendl.dev/blog/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://lendl.dev/blog/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-10-09T00:00:00+00:00</updated>
    <id>https://lendl.dev/blog/atom.xml</id>
    <entry xml:lang="en">
        <title>Multi-repo Claude Code: let each repository brief its own agent</title>
        <published>2026-10-09T00:00:00+00:00</published>
        <updated>2026-10-09T00:00:00+00:00</updated>
        
        <author>
          <name>Stefan Lendl</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lendl.dev/blog/claude-code-multi-repo/"/>
        <id>https://lendl.dev/blog/claude-code-multi-repo/</id>
        
        <content type="html" xml:base="https://lendl.dev/blog/claude-code-multi-repo/">&lt;p&gt;Most of my sessions touch more than one repository. An agent project needs a
fix in the CLI it drives; a session in some project notices something to fix
in my NixOS dotfiles. Each of those repositories carries its own &lt;code&gt;CLAUDE.md&lt;/code&gt;,
&lt;code&gt;.claude/rules/&lt;/code&gt;, skills and hooks: the brief that makes an agent behave
there. Edit the neighbour from the wrong session, and the agent works without
any of it.&lt;/p&gt;
&lt;h2 id=&quot;what-claude-code-loads-and-from-where&quot;&gt;What Claude Code loads, and from where&lt;/h2&gt;
&lt;p&gt;A session&#39;s context is anchored to the directory it starts in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;At launch:&lt;/strong&gt; the &lt;code&gt;CLAUDE.md&lt;/code&gt; of the start directory and of every parent
above it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On demand:&lt;/strong&gt; &lt;code&gt;CLAUDE.md&lt;/code&gt; files, rules and skills in subdirectories of the
start directory, the first time the agent reads a file there.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Everywhere else:&lt;/strong&gt; nothing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So when a session in &lt;code&gt;agent/&lt;/code&gt; edits &lt;code&gt;../cli/src/main.rs&lt;/code&gt;, it does so with none
of &lt;code&gt;cli/&lt;/code&gt;&#39;s instructions loaded. No error, no warning. The edit ignores the
conventions, the test commands and the &quot;never do X&quot; lines that repository
wrote down for exactly this agent.&lt;/p&gt;
&lt;h2 id=&quot;the-native-workarounds-and-where-they-stop&quot;&gt;The native workarounds, and where they stop&lt;/h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Approach&lt;/th&gt;&lt;th&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/th&gt;&lt;th&gt;Nested &lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/th&gt;&lt;th&gt;Skills&lt;/th&gt;&lt;th&gt;Hooks&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Edit &lt;code&gt;../cli&lt;/code&gt; directly&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;permissions.additionalDirectories&lt;/code&gt;&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--add-dir ../cli&lt;/code&gt; with the env var&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Start in a common parent directory&lt;/td&gt;&lt;td&gt;on demand&lt;/td&gt;&lt;td&gt;on demand&lt;/td&gt;&lt;td&gt;on demand&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A subagent&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;A session started in &lt;code&gt;../cli&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;td&gt;on demand&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;--add-dir&lt;/code&gt;&lt;/strong&gt; comes closest. With &lt;code&gt;CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1&lt;/code&gt;
set, it loads the added repository&#39;s root &lt;code&gt;CLAUDE.md&lt;/code&gt; and its skills. But the
directories are chosen at launch, so a session that &lt;em&gt;might&lt;/em&gt; touch the CLI
carries the CLI&#39;s whole brief from the first prompt, even on a day I only
wanted to work in the agent project. Nested &lt;code&gt;CLAUDE.md&lt;/code&gt; files below the added
root never load, and that repository&#39;s hooks never run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;permissions.additionalDirectories&lt;/code&gt;&lt;/strong&gt; in &lt;code&gt;settings.json&lt;/code&gt; grants file access
and loads no instructions at all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Starting in a common parent&lt;/strong&gt; picks up nested files on demand, but each
repository&#39;s own settings and hooks stay unloaded, and the session no longer
belongs to any one project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A subagent&lt;/strong&gt; can&#39;t help either: the Agent tool takes no working directory.
A subagent starts in its parent&#39;s project with the same blind spot, and its
worktree isolation is a copy of the &lt;em&gt;parent&#39;s&lt;/em&gt; repository.&lt;/p&gt;
&lt;p&gt;What&#39;s missing is a way to say &quot;this change belongs to that repository, so
load it as you would there&quot;, decided at the moment the change comes up.&lt;/p&gt;
&lt;h2 id=&quot;hand-the-change-to-a-session-that-lives-there&quot;&gt;Hand the change to a session that lives there&lt;/h2&gt;
&lt;p&gt;Claude Code can already start a session anywhere, in the background:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; ../cli&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; claude&lt;/span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; --bg --worktree&lt;/span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; cli-exit-codes&lt;/span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; --name&lt;/span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; cli-exit-codes&lt;/span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  --permission-mode&lt;/span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; auto &amp;quot;&amp;lt;task&amp;gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is a full session rooted in &lt;code&gt;../cli&lt;/code&gt;. It loads that repository&#39;s
&lt;code&gt;CLAUDE.md&lt;/code&gt;, its nested files as it reads them, its rules, skills and hooks:
everything you&#39;d get by opening a terminal there. &lt;code&gt;--worktree&lt;/code&gt; gives it its
own checkout on the branch &lt;code&gt;worktree-cli-exit-codes&lt;/code&gt;, so it never collides
with whatever is open in the main checkout. When it&#39;s done, it reports back
with &lt;code&gt;SendMessage&lt;/code&gt;, and the report lands in the session that started it.
&lt;code&gt;claude agents&lt;/code&gt;, &lt;code&gt;attach&lt;/code&gt;, &lt;code&gt;logs&lt;/code&gt;, &lt;code&gt;stop&lt;/code&gt; and &lt;code&gt;rm&lt;/code&gt; manage it from a terminal.&lt;/p&gt;
&lt;p&gt;Nothing loads until a change needs that repository, and the session doing the
work is the one that pays for its context.&lt;/p&gt;
&lt;h2 id=&quot;the-setup-one-rule-file&quot;&gt;The setup: one rule file&lt;/h2&gt;
&lt;p&gt;No wrapper, no tooling. One global rule in
&lt;code&gt;~/.claude/rules/other-repositories.md&lt;/code&gt; teaches every session the move:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot; &gt;&lt;code data-lang=&quot;markdown&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;font-weight: bold;&quot;&gt;# Changes to other repositories&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Never change a git repository other than the session&amp;#39;s project from this&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;session, and never through a subagent: only a session started in that&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;repository loads its CLAUDE.md files, hooks and skills. Start a fresh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;background session there, in a worktree of its own:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cd &amp;lt;repo&amp;gt; &amp;amp;&amp;amp; claude --bg --worktree &amp;lt;name&amp;gt; --name &amp;lt;name&amp;gt; --permission-mode &amp;lt;this session&amp;#39;s mode&amp;gt; &amp;quot;&amp;lt;task&amp;gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E36209;&quot;&gt;-&lt;/span&gt;&lt;span&gt; The task stands alone: what to change and why, how to verify it, commit on&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  its branch, and report back with SendMessage to this session&amp;#39;s name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (ListAgents shows it).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E36209;&quot;&gt;-&lt;/span&gt;&lt;span&gt; When it reports, check its branch, then merge it the same way: a fresh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  background session in that repository, told to merge &lt;/span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;`worktree-&amp;lt;name&amp;gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  into the branch it belongs on and finish as that repository&amp;#39;s rules say.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Nobody switches the main checkout&amp;#39;s branch. Then &lt;/span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;`claude stop`&lt;/span&gt;&lt;span&gt; and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  `claude rm`&lt;/span&gt;&lt;span&gt; the first session.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E36209;&quot;&gt;-&lt;/span&gt;&lt;span&gt; Never message a session already running in that repository.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E36209;&quot;&gt;-&lt;/span&gt;&lt;span&gt; A subagent never starts one: it stops and tells the main agent.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E36209;&quot;&gt;-&lt;/span&gt;&lt;span&gt; Shell changes count too (&lt;/span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;`sed -i`&lt;/span&gt;&lt;span&gt;, a formatter, &lt;/span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;`git commit`&lt;/span&gt;&lt;span&gt;). Reading&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  another repository is fine.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few of those lines carry most of the weight:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fresh, never an existing session.&lt;/strong&gt; Another session in that repository
may be deep in something unrelated, with a context full of it. A new one
starts clean.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The task stands alone.&lt;/strong&gt; The new session knows nothing of yours, so it has
to be told what to change, why, and how to check it. That makes a better
hand-over than &quot;fix it like we discussed&quot;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The permission mode travels along.&lt;/strong&gt; A subagent inherits its parent&#39;s
mode; a background session gets it from &lt;code&gt;--permission-mode&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Merging is part of the loop.&lt;/strong&gt; Check the branch the session reports, then
let another fresh session in that repository merge it and finish the way
that repository says: formatting, tests, a rebuild.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One prerequisite: Claude Code won&#39;t start a background session in a folder it
doesn&#39;t trust yet, so open &lt;code&gt;claude&lt;/code&gt; in each repository once.&lt;/p&gt;
&lt;h2 id=&quot;why-this-beats-stretching-one-session&quot;&gt;Why this beats stretching one session&lt;/h2&gt;
&lt;p&gt;A repository&#39;s &lt;code&gt;CLAUDE.md&lt;/code&gt; is its brief for an agent. Every native workaround
tries to carry that brief into a session that started somewhere else, and each
one drops part of it. Starting the session where the code lives drops
nothing, costs nothing until it&#39;s needed, and leaves each repository in charge
of how it&#39;s changed.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Measured on Claude Code 2.1.292.&lt;/em&gt;&lt;/p&gt;
</content>
        
    </entry>
</feed>
