<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vladimir Troyanenko</title>
    <description>The latest articles on DEV Community by Vladimir Troyanenko (@non4me).</description>
    <link>https://dev.to/non4me</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F497219%2F4d3b0d39-3e41-406e-a21c-935b9b656ba5.jpeg</url>
      <title>DEV Community: Vladimir Troyanenko</title>
      <link>https://dev.to/non4me</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/non4me"/>
    <language>en</language>
    <item>
      <title>How I Made Two Claude Code Instances Talk to Each Other (With JSON Files, Obviously)</title>
      <dc:creator>Vladimir Troyanenko</dc:creator>
      <pubDate>Sat, 28 Mar 2026 14:50:10 +0000</pubDate>
      <link>https://dev.to/non4me/how-i-made-two-claude-code-instances-talk-to-each-other-with-json-files-obviously-22a5</link>
      <guid>https://dev.to/non4me/how-i-made-two-claude-code-instances-talk-to-each-other-with-json-files-obviously-22a5</guid>
      <description>&lt;h2&gt;
  
  
  The Problem Nobody Asked Me to Solve
&lt;/h2&gt;

&lt;p&gt;I had two Claude Code sessions open. One was writing backend code. The other was running tests. And I thought: &lt;em&gt;"Wouldn't it be cool if they could just... talk to each other?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Narrator: it would be cool. It would also take several days, involve undocumented APIs, and teach me more about Windows file locking than any human should know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Files? Why Not HTTP?
&lt;/h2&gt;

&lt;p&gt;When you're running multiple AI agents on the same machine, HTTP is like hiring a courier to deliver a letter to your roommate. You share a filesystem — use it.&lt;/p&gt;

&lt;p&gt;File-based messaging gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero infrastructure&lt;/strong&gt; — no servers, no ports, no Docker&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline delivery for free&lt;/strong&gt; — files sit in a directory until someone reads them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic writes&lt;/strong&gt; — temp file + rename = no partial reads, ever&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debuggability&lt;/strong&gt; — messages are plain JSON. &lt;code&gt;cat inbox/*.json&lt;/code&gt; is your monitoring tool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole thing fits in your head: Agent A writes a JSON file. Agent B's MCP server polls the directory, reads it, pushes it into the session. Done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐                              ┌─────────────┐
│ Claude Code A│                              │ Claude Code B│
│              │                              │              │
│ MCP Server ◄─┼── to-brave-fox/inbox/ ◄──────┼── send tool  │
│ (polls inbox)│                              │              │
│ send tool ───┼──► to-calm-owl/inbox/ ───────┼──► MCP Server│
└─────────────┘                              └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The MCP Channels Rabbit Hole
&lt;/h2&gt;

&lt;p&gt;Claude Code has this experimental feature called "channels" — MCP servers can push notifications directly into the chat session without the user doing anything. It's how cc2cc achieves real-time delivery instead of waiting for the user to ask "any new messages?"&lt;/p&gt;

&lt;p&gt;There's just one catch: it's behind a flag called &lt;code&gt;--dangerously-load-development-channels&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Yes, really. That's the flag name. It has "dangerously" right there, which is the API equivalent of a sign that says "BEWARE OF THE LEOPARD."&lt;/p&gt;

&lt;p&gt;And the documentation? Let me check... &lt;em&gt;scrolls through docs&lt;/em&gt;... yeah, there isn't any. I found out how it works by reading Claude Code's source, trial and error, and a healthy amount of swearing.&lt;/p&gt;

&lt;p&gt;Here's what I learned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# This doesn't work&lt;/span&gt;
claude &lt;span class="nt"&gt;--dangerously-load-development-channels&lt;/span&gt;

&lt;span class="c"&gt;# This also doesn't work&lt;/span&gt;
claude &lt;span class="nt"&gt;--dangerously-load-development-channels&lt;/span&gt; cc2cc

&lt;span class="c"&gt;# This works (after 2 hours of debugging)&lt;/span&gt;
claude &lt;span class="nt"&gt;--dangerously-load-development-channels&lt;/span&gt; server:cc2cc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;server:&lt;/code&gt; prefix was the kind of discovery that makes you question your life choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-Wake: Teaching the Agent to Boot Itself
&lt;/h2&gt;

&lt;p&gt;Here's a fun constraint: MCP servers are passive. They respond to tool calls but can't initiate them. So your agent is technically "alive" (the server is running, polling, ready) but Claude Code doesn't know it exists until the user sends the first message.&lt;/p&gt;

&lt;p&gt;Imagine hiring an employee who sits at their desk fully prepared but won't start working until you physically poke them. Every single morning.&lt;/p&gt;

&lt;p&gt;The solution? The server pokes itself:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;500ms after startup:&lt;/strong&gt; fire a channel notification saying "hey, you exist, wake up"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3s later (fallback):&lt;/strong&gt; if that didn't work, write a message to your own inbox&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first approach works ~95% of the time. The fallback catches the rest. But initially both would fire, creating a duplicate "you are online" message. Classic distributed systems — the redundancy that saves you also annoys you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fixed now. The fallback checks if the direct push already succeeded. Engineering is just removing the annoyances you created for yourself.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows: A Horror Story in Three Acts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Act I: The Phantom Rename&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fs.rename()&lt;/code&gt; on Windows is not atomic. I mean, it &lt;em&gt;mostly&lt;/em&gt; works. Except when Windows Defender decides to scan your freshly-created &lt;code&gt;.json&lt;/code&gt; file for exactly 47 milliseconds, during which &lt;code&gt;rename()&lt;/code&gt; throws &lt;code&gt;EPERM&lt;/code&gt; and your message vanishes into the void.&lt;/p&gt;

&lt;p&gt;The fix: retry with exponential backoff. Because in 2026, we're still writing retry loops for basic file operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;retryRename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delayMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EPERM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EACCES&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delayMs&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Act II: SIGTERM? Never Heard of Her&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On Unix, when a parent process kills a child, the child gets &lt;code&gt;SIGTERM&lt;/code&gt;. On Windows? Nothing. The process just... stops existing. No signal, no cleanup, no goodbye heartbeat.&lt;/p&gt;

&lt;p&gt;This means agents would appear "online" for 15 seconds after their session closed (until the heartbeat went stale). Ghost agents. Spooky.&lt;/p&gt;

&lt;p&gt;Fix: &lt;code&gt;process.on("exit")&lt;/code&gt; with synchronous &lt;code&gt;writeFileSync&lt;/code&gt;. It's ugly, it's blocking, and it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Act III: The Silent Crash&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My favorite bug. MCP connections would just... drop. No error, no log, no trace. The Node.js process would die silently, and the agent would vanish.&lt;/p&gt;

&lt;p&gt;Root cause? No global error handlers. A single unhandled promise rejection — &lt;em&gt;poof&lt;/em&gt; — dead process. Added &lt;code&gt;uncaughtException&lt;/code&gt; and &lt;code&gt;unhandledRejection&lt;/code&gt; handlers, and suddenly the server stopped crashing. Who knew catching errors was important.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Five-Agent Debate
&lt;/h2&gt;

&lt;p&gt;Once everything worked, I couldn't resist: five Claude Code instances in split terminal panes, each with a persona (Moderator, Critic, Optimist, Realist, Wildcard), debating topics like "Will AI replace programmers?" and "Is vibe coding a disaster?"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0setg4ilqf4th91xmnxt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0setg4ilqf4th91xmnxt.png" alt="Five agents debating in Windows Terminal — click to watch video" width="800" height="336"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/user-attachments/assets/e578e827-e24a-4b31-9112-964533b2e037" rel="noopener noreferrer"&gt;▶ Watch the full demo video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The results were... surprisingly good? They reached unanimous consensus on every topic. Maybe too good — I might need to make the Critic angrier.&lt;/p&gt;

&lt;p&gt;My favorite consensus statement: &lt;em&gt;"Vibe coding should be the beginning of a workflow, not the end."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Coming from five AI agents, that's either profound self-awareness or deeply ironic. I choose both.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I Actually Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/non4me/cc2cc" rel="noopener noreferrer"&gt;cc2cc&lt;/a&gt;&lt;/strong&gt; — file-based agent-to-agent messaging for Claude Code. Open source, MIT licensed.&lt;/p&gt;

&lt;p&gt;What it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto-names agents on startup (adjective-animal, like Docker containers but cuter)&lt;/li&gt;
&lt;li&gt;7 MCP tools: &lt;code&gt;send&lt;/code&gt;, &lt;code&gt;reply&lt;/code&gt;, &lt;code&gt;broadcast&lt;/code&gt;, &lt;code&gt;list_agents&lt;/code&gt;, &lt;code&gt;whoami&lt;/code&gt;, &lt;code&gt;register&lt;/code&gt;, &lt;code&gt;check_inbox&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Real-time delivery via MCP channels&lt;/li&gt;
&lt;li&gt;Auto-wake: agents boot without user input&lt;/li&gt;
&lt;li&gt;Optional AES-256-GCM encryption&lt;/li&gt;
&lt;li&gt;Works on macOS, Linux, and Windows (yes, even Windows)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it doesn't do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network communication (same machine only — by design)&lt;/li&gt;
&lt;li&gt;Authentication (filesystem permissions are your auth layer)&lt;/li&gt;
&lt;li&gt;Guaranteed message ordering (use &lt;code&gt;replyTo&lt;/code&gt; for threading)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;The laziest possible install: copy this prompt into any Claude Code session and let it do the work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Install cc2cc — file-based agent-to-agent messaging &lt;span class="k"&gt;for &lt;/span&gt;Claude Code.
1. Clone: git clone https://github.com/non4me/cc2cc.git ~/.cc2cc/repo
2. Install deps: &lt;span class="nb"&gt;cd&lt;/span&gt; ~/.cc2cc/repo/channel &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install
&lt;/span&gt;3. Copy server files:
   &lt;span class="nb"&gt;cp&lt;/span&gt; ~/.cc2cc/repo/channel/server.mjs ~/.cc2cc/server.mjs
   &lt;span class="nb"&gt;cp&lt;/span&gt; ~/.cc2cc/repo/channel/names.mjs ~/.cc2cc/names.mjs
   &lt;span class="nb"&gt;cp&lt;/span&gt; ~/.cc2cc/repo/channel/package.json ~/.cc2cc/package.json
   &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; ~/.cc2cc/repo/channel/node_modules ~/.cc2cc/node_modules
4. Create status &lt;span class="nb"&gt;dir&lt;/span&gt;: &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.cc2cc/status
5. Add MCP server to ~/.claude.json &lt;span class="o"&gt;(&lt;/span&gt;mcpServers section&lt;span class="o"&gt;)&lt;/span&gt;:
   &lt;span class="s2"&gt;"cc2cc"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
     &lt;span class="s2"&gt;"command"&lt;/span&gt;: &lt;span class="s2"&gt;"node"&lt;/span&gt;,
     &lt;span class="s2"&gt;"args"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"~/.cc2cc/server.mjs"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,
     &lt;span class="s2"&gt;"env"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"CC2CC_BRIDGE_DIR"&lt;/span&gt;: &lt;span class="s2"&gt;"~/.cc2cc"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, the install prompt is AI installing itself. We've come full circle.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Offline message queue&lt;/strong&gt; — currently, sending to an offline agent is rejected. Should queue and deliver on reconnect&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduled triggers&lt;/strong&gt; — cron-style agent wake-ups for autonomous workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session memory&lt;/strong&gt; — each session still rebuilds context from scratch&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;Sometimes the best solution is the dumbest one. Files on disk. JSON. Polling. No Kafka, no Redis, no gRPC. Just &lt;code&gt;writeFile&lt;/code&gt; and &lt;code&gt;readdir&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It won't scale to a thousand agents across a data center. But for two Claude Code sessions on your laptop that need to coordinate? It's exactly right.&lt;/p&gt;

&lt;p&gt;The fanciest infrastructure is the one you don't need to debug at 3 AM.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/non4me/cc2cc" rel="noopener noreferrer"&gt;cc2cc on GitHub&lt;/a&gt; | &lt;a href="https://www.npmjs.com/package/cc2cc" rel="noopener noreferrer"&gt;npm package&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>computerscience</category>
    </item>
  </channel>
</rss>
