herdr and cmux: two shapes of the same agent multiplexer

André Lindenberg’s post about herdr came across my feed, and the pitch landed on something I have been living in for months:

You already run tmux to keep agents alive when you close the laptop. herdr goes further: through its socket API an agent splits a sibling pane, starts another agent, and blocks on its settled state before continuing.

My terminal is cmux — I wrote up my setup a while back, including the fight to make agent panes come back on their real conversations after a reboot. Same problem, two tools, so: an honest comparison, and an actual decision at the end rather than a shrug about a thousand flowers.

The structural difference

herdr is a daemon plus a TUI client that runs inside the terminal you already have. The daemon owns the PTYs; clients attach and detach. ctrl+b q detaches, herdr reattaches, including over SSH. macOS, Linux, Windows (beta). Rust, Apache-2.0, v0.7.5. The repo was created in late March 2026 and is at 20.8k stars — four months, from zero. That pace is not an accident and it is not a toy.

cmux is a native macOS app that embeds Ghostty as its renderer. It is the terminal, not a program running inside one. Swift, GPU rendering, vertical workspace tabs, browser panes, a notification center. GPL-3.0-or-later with a commercial option, v0.64.20, 25.1k stars.

Almost everything below follows from that one choice.

Which seat is each one optimizing?

This is the whole comparison, so I will put it before the evidence rather than after.

herdr optimizes the seat the agent sits in. agent wait --until done is a primitive for a program coordinating other programs. Occupant pinning, fused prompt-and-wait, the HERDR_ENV gate — those are the concerns of a caller that is not a person.

cmux optimizes the seat I sit in. The approval feed, notifications, browser panes, hook-recorded native session restore — those matter when a human is the scheduler and the agents are the ones asking permission.

So “which is better” resolves to “who does the scheduling in your workflow.” In mine, today, it is still me: I fan agents out, they come back with questions, I unblock them. That is a human-in-the-loop shape and cmux is built for it. The day the dominant pattern becomes agent spawns agent and blocks on it, herdr’s design is the right one.

What herdr does better

1. The wait verb. This is the real content of André’s post and the thing I would take today:

herdr agent wait w1:p1 --until done
herdr agent wait w1:p1 --until blocked

Server-owned, event-driven rather than polled, and it pins the resolved pane occupant so a replacement agent cannot satisfy the wait. agent.prompt also accepts an optional wait object, so submit-and-wait is one request with no race between the calls.

That is a genuine orchestration primitive. “Start the sibling, hand it work, block until it settles” becomes three lines of shell instead of a bespoke state machine.

cmux has the state — its hook integrations record running / idle / needsInput / unknown — and it has a durable event stream. It just does not expose a verb that joins them.

2. Detach is a real concept. cmux’s session lives in the app; herdr’s lives in a daemon you attach to. That difference is why my reboot post needed an appendix. To be fair: a power cycle kills the herdr daemon too, and nothing resurrects a dead PTY. But “close the laptop, reattach from another terminal, reattach over SSH” is a first-class flow there and a workaround-shaped thing in a GUI app.

3. It runs where the work runs. Linux boxes, remote hosts, Windows beta. cmux is macOS-only by construction. If your agents live on a build server, that is not a preference, it is a constraint.

4. Plugins are shipped surface. A herdr-plugin.toml declares startup hooks, actions, event hooks, and pane entrypoints; plugins launch as processes with HERDR_* context injected. There is a marketplace and a visible third-party ecosystem — review sidebars, file viewers, phone clients, remote mirrors. cmux’s ExtensionKit sidebars are younger and have been through at least one revert.

What cmux does better

1. It is a terminal, so it does not have to borrow one. No nested-multiplexer key contention, no arguing over ctrl+b, no “which layer ate my mouse event.” Real tabs, real drag-and-drop, GPU rendering.

2. Panes are not only PTYs. Surfaces can be terminals, browsers, markdown viewers, or file previews — and the browser is scriptable from the same CLI (cmux browser navigate|click|wait|download). An agent can be handed a rendered page and a doc alongside its shell. In herdr everything is a character grid; the nearest analog is experimental Kitty-protocol pane graphics.

3. Agent state is told, not inferred. cmux hooks setup installs session hooks for 14 agents — Claude Code, Codex, Grok, OpenCode, Pi, Amp, Cursor, Gemini, Kiro, Rovo Dev, Copilot, CodeBuddy, Factory, Qoder — and stores each one’s native resume command (claude --resume <id>, codex resume <id>, amp threads continue <id>, …), so a relaunch continues the real conversation. herdr detects state by evaluating manifests against a terminal snapshot. Detection is clever; being told is sturdier.

4. The human is in the protocol. The Feed collects permission requests and questions from every agent into one approval queue. Notifications, sidebar status pills, progress bars, and log lines are all CLI-writable by the agents themselves. herdr’s notification.show is a toast; six running agents need one blocked-list, not six toasts.

5. Remote and cloud are features, not an absence. cmux ssh creates remote workspaces with a bundled daemon and persisted PTY sessions you can list, attach, and clean up; cmux vm manages cloud VMs. Different shape from detach/reattach, but the “my agents are on another machine” case is covered.

The decision

Not “let a thousand flowers bloom.” That is what you say when you do not want to choose, and two multiplexers on one machine means two keymaps, two session stores, and two places to look for the agent that is blocked.

cmux stays the cockpit on macOS. Not because it wins on paper — on the agent-facing API it does not — but because switching cockpits costs everything built around the human loop: the approval feed, the notification wiring, hooks for 14 agents, workspace layouts, muscle memory. herdr would have to be better by a lot to clear that, and on the axis I actually sit on it is not better, it is differently good.

herdr gets adopted where cmux structurally cannot go: Linux boxes, remote hosts, SSH-first work. That is not hedging, because cmux is not competing there. It is a division of territory, not a bake-off.

No dual-running on the same Mac. If I catch myself doing it, that is evidence this split is wrong and I should re-run the comparison rather than live in both.

The tripwire, stated in advance so it is falsifiable. I switch outright if either becomes true:

  1. Primary development moves off macOS. Then cmux’s best feature — being an excellent native Mac terminal — is simply unavailable, and the rest is a wash.
  2. Agent-to-agent orchestration becomes the dominant mode — agents spawning and blocking on agents rather than me fanning out and unblocking — and cmux still has no wait verb. At that point I would be hand-rolling in event-stream shell what herdr ships as one command, which is the definition of using the wrong tool politely.

Neither is true today. Both are plausible within a year, and #2 is the one I would bet on. Review date: January 2027. A decision with no review date is just a preference.

Where a thousand flowers genuinely help is at the ecosystem level, not on my desk: herdr existing is the best argument cmux will ever get for shipping a wait verb, and cmux’s hook-based session capture is the best argument herdr will get for taking state from hooks instead of the screen. Each is holding up a mirror the other needs. I would rather have both projects than a merged one — and still pick one per machine.

What I asked of cmux

Filed, because I use it daily and can answer the follow-ups:

  • #8950a wait verb: cmux wait --surface <id> --until idle|needs-input --timeout <ms>, occupant-pinned, plus send --wait-until to close the submit-then-wait race. Today the closest thing is cmux events --name agent.hook.Stop --limit 1, which matches one agent’s hook vocabulary rather than semantic state and pins nothing.
  • #8951publish agent lifecycle as an event (agent.state.changed). The running / idle / needsInput state exists but lives in ~/.cmuxterm/<agent>-hook-sessions.json and the hibernation subsystem; it is absent from the public event catalog that already carries window.*, workspace.*, surface.*, and feed.*. It is the substrate the wait verb should be built on.

Two more that need no issue: resume bindings should stay PATH-relative rather than storing a resolved absolute path at pane creation (#6572, already fixed by #6582), and reboot restore should be a stated contract — my panes started coming back on 0.64.15 while the flag I expected to gate it was false for every pane (#5802, still open). Getting the right answer for a reason you cannot name is not a fixed bug, it is a deferred one.

What I would ask of herdr, and why it stays here

Two things would move herdr from “right tool for the remote boxes” to “candidate for the cockpit”:

Take state from hooks, not from the screen. The scaffolding exists — pane.report_agent accepts exactly that shape, pane.report_agent_session stores native session references, integration.install is there. The gap is breadth: cover agents first-party the way cmux hooks setup covers 14 of them, and let screen detection be the fallback rather than the primary path.

Give the human a queue. When six agents are running, what I need is not six toasts, it is one list of what is blocked. herdr already has the ingredients — semantic blocked state, agent.view.set projections, an agent sidebar — so this may be more assembly than invention. Both of these are things a plugin could prototype without touching the core.

I am deliberately not filing either as an issue. I have read herdr’s docs closely and have not run it in anger, and a feature request from a non-user is a maintainer tax: they have to reconstruct my context before they can even judge whether I found a real gap or just did not finish the manual. The cmux asks went to its tracker precisely because I use it daily. These stay at blog volume, where someone who actually runs herdr can correct me cheaply — and I would rather be corrected here than spend a maintainer’s triage.

One note on addressing, since the post that started this was not from the maintainer: herdr is Can Celik’s. Thanks to André for putting it in front of me — the framing in that post is what made me go read the socket API instead of skimming another launch.

Footnote: the fork nobody was maintaining

While writing this I checked our own voitta-ai/cmux fork. It was 0 commits ahead and 3,171 behind upstream — a June snapshot with no patches on it. That is not a fork, it is a stale bookmark that quietly implies we carry local changes.

We do not, and that is correct: the cmux problems I actually hit went upstream as issues, and one is already fixed there by a maintainer. Filing beats forking whenever the maintainer is responsive — a fork you do not rebase is a liability with a nice URL. Resynced while writing this; it is identical to upstream again.

It also mattered for the two issues above. Our checkout was seven weeks stale, so I checked both proposals against upstream main before filing — “open an issue for a feature that shipped last month” is a real way to waste someone’s afternoon. Still missing on current main: wait-for remains the tmux-compat named synchronization point, and the event catalog still has nothing for agent state.

The metric that graded my orchestration a C, and what it was actually measuring

I let an AI proficiency scanner read thirty days of my Claude Code transcripts. It handed back a 349 out of 1000 — a C — and, more interestingly, two flat zeros: Planning: 0. Customization: 0. Agent dispatches: 5.

I spend most of my time running multi-agent squads and writing skills. So either I’m worse at my own workflow than I thought, or the instrument is measuring the wrong thing. It turns out the source is right there — Elastic-licensed, readable — so I did the thing the score didn’t credit me for, and read it.

What it actually measures

The scanner is a transcript-shape heuristic. Volume it gets right: 168 sessions, 11.3k messages, 524M tokens — all there. The trouble is the three dimensions meant to capture sophistication, each pinned to a single tool-name signal:

  • Orchestration is the literal tool named Agent — and “parallel agents” only counts when two or more Agent blocks fire in one assistant turn.
  • Planning is the ExitPlanMode tool. That’s the whole definition.
  • Customization is a Write or Edit whose path ends in SKILL.md / CLAUDE.md / .mcp.json.

Three proxies, each standing in for a competence, each satisfiable by clicking the corresponding button.

Why my real work scored zero

Regular readers know the shape of my orchestration skill: an architect derives the parallel set, then each issue gets a developer in its own git worktree, an adversarial reviewer, an SDET, a productivity engineer. That is the opposite of “two Agent calls in one turn.” The squads run as background agents and separate worktree sessions, each producing its own top-level transcript. From the scanner’s vantage, that isn’t orchestration — it’s a pile of independent user sessions. The one line that decides this, is_main = "subagents" not in path, doesn’t just miss cross-process orchestration; it actively penalizes it, because every coordinated sub-session inflates the denominator as a plain session.

Planning is worse, and funnier. My whole doctrine is start with a conversation, not a spawn — the hard, valuable artifact is the parallel set an architect reasons out after reading the issues. None of that touches ExitPlanMode. So a planning discipline more deliberate than the feature scores lower than one press of the Plan button. The map graded me on whether I visited a specific city, not on whether I arrived.

And Customization: I author SKILL.md files as routine work, but from worktree sub-sessions and through git and PRs — not via the Write tool under ~/.claude/skills/ in a session it happens to be watching. The scanner even records prs_opened; it just doesn’t spend it on the dimension that would have caught me.

The actual defect

This is a construct-validity failure, not a bug. Every proxy assumes competence lives inside one process boundary and expresses itself through a named UI feature. Mine lives across boundaries — worktrees, sibling sessions, gh — and in git history the instrument never opens. It measures feature adoption and calls it proficiency. Goodhart is right there: I could raise the number tomorrow by making my workflow worse — two Agent calls per turn, a ceremonial ExitPlanMode, a Write I don’t need — and the score would thank me for the regression.

What I’d change

Same thesis I keep landing on: the score is the spaceship; the missing instrument is telemetry that reads across boundaries. Concretely — correlate sibling worktree sessions plus gh pr create into a single orchestration event; score customization from committed SKILL.md diffs, not in-transcript writes; credit the gh issue view fan-out and the long analysis turn that precede a spawn as the planning they are. I’ve written it up as an issue against the plugin, and I’ll send the scanner half as a PR. The server-side scoring isn’t in the open repo, so I can hand them a better signal but not a better weight — which is its own small lesson about what “open source” buys you.

None of this makes the tool useless. A cheap proxy over free telemetry is a reasonable place to start; I’d have started there too. But a proxy has to know it’s a proxy, and this one reports a C with the confidence of a measurement. The gap between what it counted and what I did is, as usual, the only part with information in it.

Shmobster: a Slack agent built bottom-up

We open-sourced Shmobster today (MIT). It’s a Slack agent: you @mention it in a channel and it reads threads, runs commands, opens PRs, and answers. There are a dozen of those. Here’s why we built another one, and the two ideas that shaped it enough to be worth stealing.

Two features forced it to exist

Most of what an agent does — hold a conversation, call tools, read a repo — is a solved problem you can rent. We only started writing code because two requirements had no off-the-shelf answer that fit:

  1. A multi-vendor API waterfall. Any single LLM vendor rate-limits you at the worst possible moment. We wanted an ordered list of vendors — a primary, then fallbacks — with a cooldown so a throttled vendor is skipped for a window instead of hammered every call. When the primary 429s, slide to the next one without the user ever noticing.

  2. Multi-user Slack authz. The moment more than one person can talk to an agent that runs shell commands, “what is this allowed to do?” stops being a constant and becomes a function of who is asking and where.

Everything else, we refused to write.

Own, rent, delegate

The organizing question for every capability was: own it, rent it, or delegate it?

  • Own the things that are the actual reason the project exists: the orchestration loop, the Slack door and its socket reliability, and authorization.
  • Rent the things that are somebody else’s core competency: LiteLLM for the multi-vendor waterfall, voitta-yolt as a library for classifying whether a shell command mutates state.
  • Delegate the things another agent already does better: browser work goes to claude -p, which has a browser tool. We don’t reimplement it — and we don’t waterfall it.
  • Transplant the parts that are runtime-agnostic: the agent’s “spine” is a set of markdown files (SOUL, RUNBOOKS, and friends) adapted from OpenClaw (MIT). The loop boots by reading them.

The waterfall — the first forcing function — turned out to be almost entirely rented. It’s a LiteLLM Router built from a config list:

"waterfall": [
{ "name": "anthropic", "model": "anthropic/claude-sonnet-5", "api_key": "..." },
{ "name": "openrouter", "model": "openrouter/openai/gpt-4o", "api_key": "..." },
{ "name": "nvidia", "model": "nvidia_nim/meta/llama-3.1-405b-instruct",
"api_key": "...", "api_base": "https://integrate.api.nvidia.com/v1" }
]

First entry is primary; the rest are fallbacks; a 429 slides down the list; a broken vendor cools down for sixty seconds. That’s the whole feature. We own maybe forty lines around it.

0, 1, 2, 3, many

Here’s the idea I’d steal for your next project. Every axis of a system sits at its own cardinality, and you should architect each one to its actual number — not to infinity, and not advance until the count really increments.

Axis Count Consequence
vendors many rented (LiteLLM, a config list)
owners 2 a hardcoded pair
collaborators 0 -> 1 binary owner / non-owner, no RBAC
workspace / tenant 1 hardcoded, no multi-tenant
channels many per-channel policy in config

“Many” earns a real abstraction. “Two” gets hardcoded. “One” gets a constant. “Zero heading to one” gets a boolean, not a role system. The mistake most systems make is treating every axis as “many” on day one — building RBAC for two users, multi-tenancy for one tenant, a plugin system for one plugin. You generalize an axis only when someone else actually wants in. Not before.

Authz = f(user, channel)

The second forcing function. Authorization here isn’t a global setting; it’s a function of two arguments.

  • What a command may do is answered by YOLT: is this command read-only or mutating? Read-only just runs. Mutating pauses for approval. You don’t want to approve git status; you do want to approve git push.
  • Where the agent is answers scope: each channel carries a policy — a working directory, an allow-list of GitHub repos, an AWS profile, a set of extra tools. A channel scoped to one project points at that project’s directory and can’t wander out of it.

Compose them and the agent that helps you in a locked-down production channel is the same binary as the one running free in your scratch channel — only the policy differs.

There’s a third argument creeping in — who — but at cardinality 0->1 it’s still just a trusted_users list: a handful of Slack IDs allowed to widen a channel’s restrictions by chat. The list itself is file-only. The agent can’t add to it, so there’s no privilege escalation to design against yet. When collaborators become “many,” that boolean grows into something real. Not before.

Built bottom-up, in public

Shmobster was built one issue at a time — skeleton, then the exec gate, then per-channel policy, then waterfall hardening — each a small PR against a design argued out in the issue tracker. That history is now public too.

If you run agents in Slack and have felt any of these edges — the rate-limit cliff, or the “wait, what can this thing actually do” question — the code is at github.com/voitta-ai/shmobster, MIT-licensed. Steal the cardinality table at minimum.

Recursive self-improvement, you said?

Spawning a fleet of coding agents is a solved problem. You write a for loop, you call the Agent tool N times, you go get coffee. The unsolved problem is everything wrapped around the spawn: deciding what can actually run in parallel, stopping the agent that wrote the code from also grading (or eating) its own homework, and — the part nobody ships — recording how the run went so the next one isn’t the same run with the same mistakes.

I wish I didn’t remember this anymore but this used to be called a “retrospective” in that sect I was once a member of.

I’ve been dogfooding a small orchestration skill (agent-team-orchestration, open in voitta-ai/skillz) that treats those as the actual work. Three runs in. This is the first write-up, warts very much included — the warts are the only part with information in them.

The shape, and the one non-negotiable rule

Start with a conversation, not a spawn. Before any developer agent exists, an architect reads the open issues (gh issue list, then actually gh issue view each one) and the repo, and produces the one deliverable that’s genuinely hard: the parallel set. Independent work (different modules, no shared schema, PRs that won’t collide on merge) fans out; everything else serializes (shared files, a migration that has to land first, B’s acceptance depends on A). Get that wrong and you don’t get parallelism, you get merge conflicts with extra steps.

Then each issue in the wave gets a squad, roles deliberately split so no agent both writes and blesses the same diff:

  • developer — its own git worktree, opens the PR;
  • adversarial reviewer — a different agent, briefed to break the diff, not rubber-stamp it;
  • SDET — drives the change like a user;
  • productivity engineer — a meta-role that watches the process: every stall, every human approval, every bit of rework, written down.

The dev/reviewer split is load-bearing. The instant the context that wrote the code also reviews it, the review is theater.

And the telemetry is free, which is the best price. Every Claude Code session is a complete JSONL transcript at ~/.claude/projects/<slug>/<uuid>.jsonl — every tool call, every AskUserQuestion, every answer you gave. (We’ll gate the privacy policy to not log every breath you take).

TFW that retrospective is not a wishful thinking, it’s actionable.

Three runs, in ascending order of interesting

Run 1 — shipped clean, screwed up in a way I didn’t catch until I read the log. Two bug fixes on a production Next.js + Prisma app (two-branch staging/prod). Both merged, deployed, SDET-verified green. Then I read the transcript: the two bugs already had open PRs from a prior run. The architect never looked. We’d built and squash-merged duplicates, closed the issues, and orphaned two perfectly good PRs.

That’s not an agent being dumb. It’s a hole in the recipe. “Choose the parallel set” reasoned about file overlap and ordering and never asked the first question a human lead asks — is anyone already on this? — which is one gh pr list away. Second tell, same run: asked “where’s the evidence the reviewer approved these?”, the answer was nowhere. The verdicts lived in the agents’ context and never touched the PR. An approval that leaves no durable artifact didn’t happen. (Worse, squash-merge later buried even the merge-commit note, but I’m getting ahead of myself.)

Run 2 — the loop closed, and I have receipts. New work — a homepage redesign across seven sub-issues — same skill. At startup the agent did something I didn’t tell it to: it ran gh issue view 122 on the prior run’s recorded retro and read the engagement log. Then it did exactly the things Run 1 botched. It pre-flighted existing PRs. Every merge carried an adversarial verdict with specifics; the reviewer caught a dead query param (?q= where the target route reads ?search=) and sent it back with REQUEST_CHANGES.

Then it got interesting. A staging route started returning 500. The team traced it to schema drift, and went to fix the deploy pipeline by adding prisma db push. The safe version (no --accept-data-loss) did the right thing and aborted:

⚠️ There might be data loss when applying the changes:
• drop column `negotiableTerms` on `Property` (1 non-null value)
Error: Use the --accept-data-loss flag to ignore the data loss warnings

It refused to drop a column with live data, surfaced it for a human call, took a one-time --accept-data-loss against staging only, reconciled, and reverted — production never saw the flag. The redesign isn’t the headline. The headline is that the run improved because it had read how the last run went. Best current read: that’s the flywheel, showing up unprompted.

Run 3 — we pointed it at itself, which is geekily elegant, and scientifically noble I scraped every point across Runs 1–2 where an agent stopped to ask a human to approve something — fifteen gates — dumped them into one issue, and ran the skill on that issue. The architect grouped the fifteen by type, correctly separated the gates worth keeping (destructive DB ops — yes, always ask) from the avoidable friction (re-asking a runtime question it already answered two turns ago), and — the good part — ran two of the fixes on its own execution before they were written into the skill. It pre-flighted with gh pr list and caught two pre-existing issues that overlapped the work, exactly the Run-1 bug, fixed live by the thing being fixed.

What’s actually carrying the weight

  • The parallel-set call is real architecture. Run 3 ran two repos in parallel but serialized five edits that all touched one SKILL.md into a single PR — instead of four agents racing to conflict on the same file.
  • Build/attack/verify pays rent. The reviewer caught a bug the developer was happy with. Once is enough to justify the second agent.
  • Worktree-per-issue keeps the squads from knifing each other.
  • The flight recorder is the product. Every stall is a candidate fix — a default, a permission, a pre-flight, a sharper brief.

Where it falls down (best current read)

  • The headline feature has never once fired. The skill leads with “every agent is a watchable terminal tab you can steer mid-run.” That needs the root session launched through the cmux claude-teams wrapper, which prepends a tmux shim to PATH (CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 alone is a red herring — diagnose with which tmux + echo $TMUX). Three runs, three fallbacks to background agents, because the session wasn’t started that one specific way. A feature nobody reaches isn’t a feature, it’s a positioning bug.
  • The same two process bugs recur every run until baked in: a setup question asked at spawn time instead of as a step-0 precondition, and re-asking a decision already made. Prose doesn’t self-correct — the executor re-litigates your opinions until you encode them as defaults.
  • N=3 and confounded. Run 2’s wins rode on memory carried from Run 1, so I can’t yet split skill-value from memory-value. The compounding loop is a strong signal, not a proof. The honest next experiment is one run on a clean, never-seen repo, launched under cmux, with no carried memory, measured by a typed telemetry schema — which doesn’t exist yet, so I’m building that before I build anything else.

The actual thesis

Spawning is commodity; the moat is the operating doctrine plus the telemetry loop — the thing that makes human-interventions-per-issue trend down run over run. Build the instrument first, defer the spaceship. YAGNI applies to strategy, too.

Skill’s open in voitta-ai/skillz. Run it on your backlog and tell me where it stalls. The stalls are the entire point.

voitta-bookmarklet: A Local AI Sidecar for Arbitrary Web Pages

voitta-bookmarklet is a lightweight browser-side entry point for a larger local AI tool runtime. The user enters the bookmark URL, clicks it on any HTTPS page, and gets a right-side chat pane injected into the current document. That pane is backed by a local FastAPI service running on https://127.0.0.1:12358, which serves both the frontend widget and the tool-using chat backend.

The interesting part is not the bookmarklet itself; it’s the architecture behind it. The frontend is built as a single bundled widget and mounted via Shadow DOM, which keeps the UI isolated from page styles. The backend exposes a multi-provider chat runtime supporting Anthropic, OpenAI, and Gemini, plus an in-memory tool bridge for orchestrating tool calls, session state, and provider-specific actions.

The repository is structured around extensibility. There is a clear separation between provider-agnostic tools, provider-specific integrations, browser/page-context tooling, and retrieval components. External data providers live under their own packages, with Google Drive implemented first via OAuth and read-only access. The project also includes RAG indexing for its own documentation, so the agent can use repo-specific reference material as part of its runtime behavior.

From an engineering perspective, this is a practical approach to embedding an assistant into real browser workflows without requiring a full browser extension as the primary product surface. It treats the browser page as the host environment, the local backend as the secure execution boundary, and the model as one component inside a broader tool system.

The implementation also surfaces the real constraints of this design: local TLS setup, CSP restrictions on script injection, OAuth plumbing, and the need to separate user-facing widget code from backend orchestration logic. Those details are exactly what turn a generic “AI chat overlay” into an actual usable system.

In short, voitta-bookmarklet is interesting because it is not just a UI experiment. It is a compact architecture for attaching model-driven, tool-using assistance to arbitrary web pages while keeping execution local and leaving room for more integrations over time.

Repo so you can give a star: https://github.com/voitta-ai/voitta-bookmarklet

Beyond voitta-rag: A Quick Tour of Voitta AI’s Other Public Projects

Most of our coverage of Voitta AI’s GitHub organization has focused on llm-tldr and voitta-rag. Fair enough: those are central projects, and easy to explain. But the org has turned into a broader workshop for agent tooling, developer workflows, and MCP-adjacent experiments.

So here is a quick tour of the other public repos worth a look — including two that we already mentioned elsewhere but are too useful not to repeat here.

Claude Code workflow tools

voitta-yolt

voitta-yolt is a Claude Code safety hook that statically analyzes commands before execution, auto-allows clearly read-only invocations, and flags mutating ones for review. The interesting bit is that it closes practical gaps in Claude Code’s built-in allowlist behavior, especially around compound shell commands and interpreter wrappers.

GitHub: voitta-ai/voitta-yolt. It would be nice to give a star.

omemepo

omemepoomnia mea mecum porto, “all that is mine, I carry with me” — is a portability and sharing layer for Claude Code. It can pack up your ~/.claude/ setup, move it to another machine, and act as a marketplace layer for plugins and shared Claude Code artifacts.

Right now the implemented surface includes pack, unpack, publish, and an mcp command with subcommands like list, export, import, enable, disable, profile, and prompts. That makes it feel less like a vague portability pitch and more like a concrete attempt to make Claude Code environments reproducible and shareable.

GitHub: voitta-ai/omemepo. It would be nice to give a star.

Tools for working with other software through MCP

voitta-freecad-mcp

voitta-freecad-mcp gives an LLM control over FreeCAD: create geometry, manipulate documents, inspect assemblies, and capture screenshots. The architecture is pragmatic: an MCP server talks to a bridge running inside FreeCAD so operations execute on the app’s main thread.

GitHub: voitta-ai/voitta-freecad-mcp. It would be nice to give a star.

fusion-360-mcp

fusion-360-mcp appears to be the same general idea for Autodesk Fusion 360: an MCP server paired with an in-app HTTP add-in, with documentation for geometry inspection, screenshots, measurements, and design-tree operations. If voitta-freecad-mcp is the open-source-CAD path, this looks like the commercial-CAD sibling.

GitHub: voitta-ai/fusion-360-mcp. It would be nice to give a star.

voitta-pptx

voitta-pptx is smaller but very practical: upload a PowerPoint file, render slides as PNGs through OnlyOffice, and hand the results back to the model. In other words, make decks visible to systems that reason better over images than over zipped XML internals.

GitHub: voitta-ai/voitta-pptx. It would be nice to give a star.

Glue for agent workflows

voitta-auth

voitta-auth is a macOS menu bar app that authenticates against Microsoft, Google, and Okta, then exposes a unified FastMCP proxy with credentials injected for downstream tools. That is not a flashy demo; it is infrastructure for making agent tooling actually usable in enterprise environments.

GitHub: voitta-ai/voitta-auth. It would be nice to give a star.

voitta-bookmarklet

voitta-bookmarklet injects a chat pane into arbitrary web pages via bookmarklet, backed by a local FastAPI service and pluggable model providers. It is a nice reminder that “agent interface” does not have to mean “yet another standalone app.” Sometimes the right UI is: put the assistant next to the page you are already looking at.

GitHub: voitta-ai/voitta-bookmarklet. It would be nice to give a star.

voitta-gannt

voitta-gannt is an interactive Gantt editor backed by Mermaid markdown, with both browser UI and MCP access. That is an oddly specific but smart pattern: keep the source of truth plain text, keep the interface visual, and let agents edit the same artifact humans do.

GitHub: voitta-ai/voitta-gannt. It would be nice to give a star.

Earlier platform pieces

voitta

voitta predates a lot of the current MCP craze and reads like the underlying orchestration layer: a Python framework for routing and automating LLM tool calls across APIs and handlers.

GitHub: voitta-ai/voitta. It would be nice to give a star.

voitta-example

voitta-example is, as the name suggests, a working example app using the library.

GitHub: voitta-ai/voitta-example. It would be nice to give a star.

mcp-voitta-gateway

mcp-voitta-gateway exposes the older Voitta framework through MCP. Together with voitta-example it shows a through-line: Voitta was thinking about tool routing before MCP became the default wrapper for the conversation.

GitHub: voitta-ai/mcp-voitta-gateway. It would be nice to give a star.

IDE and developer-environment experiments

mcp-server-plugin

mcp-server-plugin provides JetBrains-side MCP server plumbing.

GitHub: voitta-ai/mcp-server-plugin. It would be nice to give a star.

jetbrains-voitta

jetbrains-voitta extends that world with AST analysis and debugging tools. That is an important theme across the org: not just calling tools, but embedding them where developers already work.

GitHub: voitta-ai/jetbrains-voitta. It would be nice to give a star.

truffaldino

truffaldino is a configuration manager for AI-development setups — effectively dotfiles for MCP servers and prompts across Claude Code, Cursor, Cline, IntelliJ, and friends. Less glamorous than a model demo, but probably more useful over time.

GitHub: voitta-ai/truffaldino. It would be nice to give a star.

Odds and ends, but not random ones

claude-svg

claude-svg turns Claude Code into a diagram generator for architecture visuals, banners, and other polished SVG outputs. It is easy to dismiss as a side project until you remember how often engineering work needs presentable graphics fast.

GitHub: voitta-ai/claude-svg. It would be nice to give a star.

a2amcp

a2amcp is an example dispatcher agent built around Google’s A2A ideas. Small repo, but it points toward multi-agent routing rather than single-assistant tooling.

GitHub: voitta-ai/a2amcp. It would be nice to give a star.

shoelace

shoelace is the oddball in the org right now because it is really OpenClaw under an older or alternate banner. Still, it reflects the same interest in practical assistant infrastructure across devices and channels.

GitHub: voitta-ai/shoelace. It would be nice to give a star.

The pattern

The org looks less like one product with a few helpers and more like a workshop around agent ergonomics.

Some repos are about retrieval. Some are about auth. Some are about getting LLMs into CAD, IDEs, or decks. Some are about making workflows inspectable, configurable, portable, or just less annoying. Not every repo is equally mature, but taken together they show a consistent instinct: build the missing connective tissue between models and real work.

That, more than any one repository, is what seems interesting about Voitta AI.

New voitta-rag features

A follow-up to our earlier looks at voitta-rag vs llm-tldr, the February updates, and the search-scope release.

voitta-rag has kept moving since then. The recent work is less about flashy new connectors and more about something arguably more important: usability. Because — dogfooding is real.

Login got more practical

voitta-rag now supports Microsoft OAuth and Google token validation. That matters because a self-hosted knowledge layer gets much more useful once people can sign in with the accounts they already use for work, instead of maintaining a parallel identity system just for search.

In the Microsoft-heavy shops (yeah, ok, shut up) this also tightens the loop with SharePoint permissions: the same work identity can be used both for login and for permission-aware retrieval.

GDrive specific: URLs can now resolve back to indexed content

One of the more quietly useful additions is source URL resolution. If content came from Google Docs, Sheets, or Slides, voitta-rag can now store the source URL in chunk metadata and resolve that URL back to the indexed material through MCP.

That sounds small until you think about actual workflow. Someone drops a Google Docs link into chat, ticket comments, or an LLM prompt. Instead of treating the link as an opaque pointer and making the assistant start from scratch, voitta-rag can connect it to content it already knows.

This also works well with GDrive-based pointers that appear on your disk as *.gdoc, e.g.

Docker mode looks much more usable

Docker mode now auto-discovers mapped folders, distinguishes managed mounts from ordinary folders, etc. Local filesystem sources also got a real first-class flow instead of feeling bolted on.

This works real well if you can, for example, use GDrive app because your admin does not allow voitta-rag to read GDrive. It can read local GDrive (but see for resolving *.gdoc) and, well, it’s supported nicely.

Claude Code integration got real

There is now a Claude Code plugin setup flow, plus tooling to import Claude Code session history into voitta-rag memory. That is a meaningful step beyond “here is an MCP server” toward “here is a workflow.”

The interesting part is not just convenience. It hints at voitta-rag becoming a memory layer around actual agent work: not only your repos and docs, but also the history of what the assistant did, why, and in what context.

Bulk repo handling improved

Bulk repository import/export got better documentation and a round-trip workflow, and Git sync learned a practical trick: when token auth is in play, SSH repository URLs can be converted automatically to HTTPS.

That is exactly the kind of fix mature tools accumulate. It does not make for a dramatic screenshot, but it removes friction from the real environments where people actually deploy this stuff.

The direction is getting clearer

At first glance voitta-rag looks like “RAG for code and documents.” That is still true, but increasingly incomplete.

What is emerging is a self-hosted knowledge substrate for AI work: identity-aware, connector-rich, MCP-accessible, and increasingly conscious of workflow instead of just indexing. The recent changes are part polish, part plumbing, but together they make the system feel much closer to something a team could rely on every day.

Well… Almost… There’ll be more.

voitta-rag: Scoping Your AI’s Knowledge, and a few new features

A follow-up to our February 13 comparison of llm-tldr and voitta-rag.


Part I: The Search Toggle — Context Management for the Multi-Project Developer

One of the quieter problems with RAG-assisted development is context pollution. You index everything — your client project, your internal tools, that side experiment from last month — and then your AI assistant cheerfully retrieves code snippets from all of them, muddying every answer.

voitta-rag now has a clean answer to this: a per-folder search toggle in the file browser.

voitta-rag search toggle

Each indexed folder has a Search checkbox. Green means its content shows up in search results (and thus in MCP responses to Claude Code or any other connected assistant). Grey means the folder stays indexed — nothing is deleted or re-processed — but it’s invisible to search. Toggle it back on, and it’s instantly available again.

Why this matters

If you consult for multiple clients, or are just working on multiple not very related projects, your voitta-rag instance might hold:

  • Project A’s monorepo, Jira board, and Confluence space
  • Project B’s microservices and SharePoint docs
  • An internal project — say, a lead generation pipeline
  • A few open-source repos you reference occasionally

Without scoping, a search for “authentication flow” returns results from all of them. Your AI assistant synthesizes an answer that blends Project A’s OAuth implementation with Project B’s API key scheme and a random auth.py from your internal tool. Not wrong, exactly, but not useful either.

With the search toggle, you flip Project B and the internal project off when you’re heads-down on Project A. Searches — including MCP tool calls from Claude Code — only return Project A’s content. When you context-switch, you flip the toggles. It takes one click per folder.

Projects: grouping toggle states

If toggling folders one by one sounds tedious for a large index, voitta-rag also supports projects — named groups of toggle states. Create a “Project A” project and a “Project B” project, each with its own set of active folders. Switching projects flips all the toggles at once.

The active project persists across sessions and is respected by the MCP server, so your AI assistant automatically searches the right scope when you resume work.

Per-user scoping

The toggle is per-user. On a shared instance, each developer can have their own search scope without stepping on each other. Your teammate can be searching across everything while you’ve scoped down to one client — same voitta-rag deployment, different views.

The takeaway

This is a small feature with disproportionate impact. The whole point of a RAG knowledge base is to give your AI assistant relevant context. If you can’t control what “relevant” means, you’re outsourcing that judgment to vector similarity scores — which don’t know that Project A and Project B are different engagements. The search toggle puts that judgment back in your hands.


Part II: What Else Shipped — Glue Data Catalog, UI Polish, and More

Since our last deep-dive, voitta-rag has been on a steady clip of new features. Here’s what landed in the latest batch.

AWS Glue Data Catalog as a Data Source

This is the headline addition. voitta-rag can now sync schema metadata from AWS Glue Data Catalog — databases, tables, columns, partition keys — and index it for RAG search.

The connector (PR #11) renders Glue metadata as markdown: each database becomes a document with a summary table and a per-table breakdown of columns, types, and partition keys. This gets chunked and embedded like any other content.

Why would you want your data catalog in a RAG knowledge base? Because schema questions are exactly the kind of thing developers ask AI assistants all the time:

  • “Which table has the customer email field?”
  • “What are the partition keys on the events table?”
  • “Show me all tables in the analytics database”

Without Glue indexing, the assistant either hallucinates a schema or asks you to go look it up. With it, the answer comes back from your actual catalog metadata — correct, current, and grounded.

The UI offers a region dropdown, an auth method toggle (AWS profile or access keys), and optional catalog ID and database filters. You can index everything or cherry-pick specific databases.

SharePoint Global Sync and Timestamp Visibility

The SharePoint connector got a global sync implementation — configure once, index everything in the site. Additionally, source timestamps are now exposed in MCP search results, so an AI assistant can see when a document was created or last modified, not just its content. This matters for questions like “what changed recently?” or “is this documentation current?”

Multi-Select Dropdowns for Jira and Confluence

Previously, you typed Jira project keys and Confluence space names into a text field — error-prone and tedious if you have dozens. Now there are multi-select dropdown widgets (PR #10) that fetch available projects and spaces from your instance and let you pick. Select “ALL” to dynamically sync everything, including projects or spaces created in the future.

A small but satisfying fix: JQL project keys are now quoted to handle reserved words like IS that would otherwise break queries. The kind of bug you only hit when a real user has a project named something unfortunate.

File Manager UI Overhaul

The file browser got a visual refresh: independent scroll within the file list (headers and sidebar stay fixed), full-width layout, a file count status bar, styled scrollbars, and file extensions preserved when names are truncated. Mostly quality-of-life, but it makes a noticeable difference when you’re browsing a large index.

MCP Improvements

The get_file tool now includes guidance to prefer get_chunk_range for large files — a pragmatic touch. When an AI assistant tries to fetch a 10,000-line file, it’s better to get a targeted range of chunks than to blow up the context window.

SharePoint ACL Sync — Permission-Aware Search

This is the most architecturally significant addition in this batch. voitta-rag now syncs SharePoint Online permissions (ACLs) alongside document content, so search results respect who’s allowed to see what.

SharePoint’s permission model is deceptively complex: permissions flow down from site → library → folder → file through an inheritance chain, but any object in the chain can break inheritance (e.g., when someone shares a file with a colleague who doesn’t have parent-level access). Effective permissions for a given file might come from the file itself, a parent folder three levels up, or the site root.

The new ACL sync walks this hierarchy via the Microsoft Graph API, resolves effective permissions per file, and stores them in the vector index alongside the document chunks. At search time, results are filtered by the requesting user’s identity — you only see content you’d be allowed to see in SharePoint itself.

The implementation includes an acl-probe diagnostic endpoint that lets you inspect permissions on a sample of files without triggering a full sync — useful for debugging “why can’t user X see document Y?” scenarios.

An 800-line research document covers the SharePoint permission model, Graph API capabilities and limitations, and design decisions. Worth reading if you’re building anything that needs to reason about SharePoint access control.

Microsoft OAuth Login

voitta-rag now supports Microsoft OAuth as a login provider, alongside the existing authentication methods. For organizations already on Microsoft 365, this means users can sign in with their work accounts — and those identities can be matched against SharePoint ACLs for permission-aware search. A .env.sample file documents all the configuration options.

Landing Page Rebrand

A small but notable change: the landing page now reads “Voitta RAG” instead of the previous branding. The project has a clear identity now.


Wrapping Up

The search toggle and project system solve a real workflow problem — context management when you’re juggling multiple codebases. The Glue Data Catalog connector extends voitta-rag’s reach beyond code and documents into infrastructure metadata. The SharePoint ACL sync adds enterprise-grade access control to RAG search — which matters a lot once you’re indexing sensitive documents across an organization. And the UI, connector, and auth improvements continue to sand down the rough edges.

All of it still runs on your infrastructure. Nothing phones home. If you’re building with MCP-connected AI assistants and want a self-hosted knowledge layer, voitta-rag is worth a look.

voitta-rag Grows Up, voitta-yolt Is Born: February Updates from Voitta AI

A follow-up to our February 13 comparison of llm-tldr and voitta-rag.

Part I: voitta-rag — From Code Search to Knowledge Platform

When we last looked at voitta-rag, it was a solid hybrid search engine for codebases — index your repos, search via MCP, get actual code chunks back. Twelve days and 11 commits later, it’s become something broader: a self-hosted knowledge platform that indexes not just code but your entire work graph.

Here’s what landed since February 13.

Enterprise Connectors: Jira, Confluence, SharePoint

The biggest expansion is connector coverage. voitta-rag now syncs from Jira, Confluence, and SharePoint alongside the existing Git, Google Drive, Azure DevOps, and Box integrations.

Jira and Confluence support both Cloud (API token with Basic auth) and Server/Data Center (PAT with Bearer auth), selectable via dropdown in the UI — a detail that matters because plenty of enterprises still run on-prem Atlassian. Cloud uses the v3 search endpoint (v2 is deprecated), and Confluence Cloud correctly routes through /wiki/rest/api.

SharePoint got a full global sync implementation. And on the UI side, both Jira projects and Confluence spaces now use multi-select dropdown widgets — you can cherry-pick specific projects or select “ALL” to dynamically sync everything, including future additions. Practical touch: JQL project keys are now quoted to handle reserved words like IS that would otherwise break queries.

Time-Aware Search

Search results are no longer timeless. voitta-rag now tracks source timestampscreated_at and modified_at — propagated from every remote connector through a .voitta_timestamps.json sidecar file into the indexing pipeline and vector store.

This enables time range filtering on the MCP search tool via date_start/date_end parameters. “What changed in the last week?” is now a first-class query. For an AI assistant trying to understand recent activity across repos, Jira boards, and Confluence spaces simultaneously, this is a significant upgrade.

Anamnesis: Persistent Memory for AI Assistants

The most architecturally interesting addition. Anamnesis (Greek for “recollection”) gives AI assistants a persistent memory layer backed by voitta-rag’s vector store.

Six new MCP tools let an assistant create, retrieve, update, delete, like, and dislike memories. The like/dislike mechanism adjusts relevance scoring — memories the assistant finds useful surface more readily over time, while unhelpful ones fade. It’s essentially a learning loop: the AI assistant builds up a knowledge base of its own observations and decisions, searchable alongside the actual indexed content.

This turns voitta-rag from a read-only knowledge base into a read-write one — the assistant doesn’t just consume context, it contributes to it.

Per-User Search Visibility

A multi-tenancy feature: users can now enable or disable folders for their own search scope without affecting other users. If you’ve indexed 50 repos but only care about 5 for your current task, you toggle the rest off. The MCP server respects these per-user visibility settings, so AI assistants scoped to different users see different slices of the same knowledge base.

More File Types

The indexing pipeline now handles AZW3 (Amazon Kindle) files, joining the existing support for DOCX, PPTX, XLSX, ODT, ODP, and ODS. Not the most common format in a work context, but it signals that voitta-rag is thinking beyond code and office docs toward general document ingestion.

The Bigger Picture

Two weeks ago, voitta-rag was a code search tool. Now it indexes your Git repos, Google Drive, SharePoint, Jira, Confluence, Box, and Azure DevOps — with time-aware search, per-user scoping, and persistent AI memory. The trajectory is clear: it wants to be the single search layer across everything your team produces, exposed to AI assistants via MCP.

The self-hosted angle remains the key differentiator. Nothing leaves your network. For teams where that matters (and increasingly, it does), this is starting to look like a serious alternative to cloud-hosted RAG services.


Part II: voitta-yolt — You Only Live Twice

Brand new from Voitta AI today: voitta-yolt (You Only Live Twice) — a safety analyzer for Claude Code that statically analyzes Python scripts before execution.

The Problem

Claude Code can write and run Python scripts. That’s powerful and dangerous in equal measure. By default, you either pre-approve all Python execution (fast but risky) or manually approve each script (safe but maddening). Neither is great.

How YOLT Works

YOLT registers as a Claude Code PreToolUse hook on the Bash tool. When Claude Code runs python3 script.py, YOLT intercepts the command, parses the Python AST, and walks every function call against a configurable rule set:

  • Safe scripts (pure computation, data parsing, read-only operations) get auto-approved — no permission prompt.
  • Destructive scripts (file writes, AWS mutations, subprocess calls, network POSTs, database connections) get flagged for human review with specifics about what was detected, including the source line content.

Zero external dependencies — it’s pure stdlib (ast, json, fnmatch, shlex). AST parsing is near-instant, so there’s no perceptible delay.

The Rule System

The default rules are sensible and well-structured:

  • AWS boto3: describe/list/get/head → safe. delete/put/create/terminate → destructive. Rules scope via trigger_imports, so cache.delete_item() in a non-AWS script won’t false-positive.
  • File I/O: open() in write modes, os.remove, shutil.rmtree → destructive. Read-only access is fine.
  • Subprocess: Always flagged. subprocess.run, os.system, the lot.
  • Network: requests.get → safe. requests.post/put/delete → destructive.
  • Database: Connection creation → flagged for review.

A curated list of safe imports (json, csv, re, datetime, pathlib, hashlib, and ~50 others) means scripts that only use standard library data-processing modules sail through without interruption.

Custom rules go in ~/.claude/yolt/rules.json and merge with defaults — you can add safe methods, define new categories with their own trigger_imports, and use glob patterns (fetch_, drop_).

One Important Gotcha

If you have Bash(python3:*) in your Claude Code settings.local.json allow list, YOLT’s hook never fires — static allow rules take precedence over PreToolUse hooks. YOLT replaces the need for that allow rule entirely: safe scripts get auto-approved by the hook itself.

Why This Matters

The design philosophy — “false positives OK, false negatives not” — is the right one for a safety tool. It’s the security principle of fail-closed applied to AI code execution.

YOLT is small (527 lines across 6 files in the initial commit), focused, and immediately useful. If you’re letting Claude Code run Python, this is the kind of guardrail that should exist by default.


Wrapping Up

voitta-rag is evolving from a code search tool into a self-hosted knowledge platform with enterprise connectors and AI memory. voitta-yolt tackles a different but equally practical problem: making AI code execution safer without making it slower.

Both projects are open source (AGPL v3) and available on Voitta AI’s GitHub.


Gregory Golberg is co-founder of Method & Apparatus, a fractional CTO consultancy. Previously: llm-tldr vs voitta-rag: Two Ways to Feed a Codebase to an LLM.

llm-tldr vs voitta-rag: Two Ways to Feed a Codebase to an LLM

Every LLM-assisted coding tool faces the same fundamental tension: codebases are too large to fit in a context window. Two recent tools attack this from opposite directions, and understanding the difference clarifies something important about how we’ll work with code-aware AI going forward.

The Shared Problem

llm-tldr is a compression tool. It parses source code through five layers of static analysis — AST, call graph, control flow, data flow, and program dependence — and produces structural summaries that are 90–99% smaller than raw source. The LLM receives a map of the codebase rather than the code itself.

voitta-rag is a retrieval tool. It indexes codebases into searchable chunks and serves actual source code on demand via hybrid semantic + keyword search. The LLM receives real code, but only the relevant fragments.

Compression vs. retrieval. A map vs. the territory.

At a Glance

llm-tldr voitta-rag
Approach Static analysis → structural summaries Hybrid search → actual code chunks
Foundation Tree-sitter parsers (17 languages) Server-side indexing (language-agnostic)
Interface CLI + MCP server MCP server
Compute Local (embeddings, tree-sitter) Server-side

What Each Does Better

llm-tldr wins when you need to understand how code fits together:

  • Call graphs and dependency tracing across files
  • “What affects line 42?” via program slicing and data flow
  • Dead code detection and architectural layer inference
  • Semantic search by behavior — “validate JWT tokens” finds verify_access_token()

voitta-rag wins when you need the actual code:

  • Retrieving exact implementations for review or modification
  • Searching across many repositories indexed server-side
  • Tunable search precision (pure keyword ↔ pure semantic via sparse_weight)
  • Progressive context loading via chunk ranges — start narrow, expand as needed

The Interesting Part

These tools don’t compete — they occupy different layers of the same workflow. Use llm-tldr to figure out where to look and why, then voitta-rag to pull the code you need. Static analysis for navigation, RAG for retrieval.

This mirrors how experienced developers actually work: first you build a mental model of the architecture (“what calls what, where does data flow”), then you dive into specific files. One tool builds the mental model; the other hands you the files.

The fact that both expose themselves as MCP servers makes combining them straightforward — plug both into your editor or agent and let the LLM decide which to call based on the question.

References