Skip to content

Migration guide · claude-mpm → trusty-mpm

Coming from claude-mpm

trusty-mpm is a Rust meta-harness for Claude Code sessions, driven by one binary called tm. If you already run a PM-style multi-agent setup, the way you start a session barely changes. What changes is everything underneath it: a supervising daemon instead of a per-session process, tmux-hosted sessions you can walk away from, and agents deployed where they cannot leak into your other tools.

Binary
tm, trusty-mpm
Install
tctl install trusty-mpm
Daemons
mpm, memory, search
Platforms
macOS 12+, Linux

Read this first

trusty-mpm is not a version of claude-mpm

They are unrelated codebases. trusty-mpm is not a fork, a port, or a rewrite of claude-mpm — there is no shared code, the languages differ (Rust and Python), the maintainers differ, and they ship through different channels (crates.io and Homebrew, versus PyPI). What they share is an idea: a project-manager session that delegates work to specialised agents.

The similar names cause real confusion, including for Claude Code sessions asked what they are running under. trusty-mpm keeps a canonical answer in its own repository — WHAT-IS-TRUSTY-MPM.md — and that document, not a shell probe, is the thing to quote.

What carries over

The shape of the work is the same. A PM session orchestrates and delegates; specialised agents do the implementing; skills are reusable instruction packs; an output style decides how the PM writes back to you. If those four words already mean something to you, you know how to drive tm.

That parity is deliberate rather than incidental. tm launch runs the full deployment sequence — instructions, agents, skills, MCP config — into the project and then starts or attaches the session in your current terminal, which is the same single-command experience you already have. The source comment on that subcommand says it plainly: it behaves like running claude-mpm.

Same concept, different plumbing
ConceptWhere it lives under tm
PM delegation modelUnchanged. The PM orchestrates and delegates; it does not write code itself.
Agent rosterComposed and deployed by tm install and by each session launch.
SkillsDeployed alongside the agents, plus a project tier under the project's own .claude/skills.
Output stylesThree bundled ids — trusty-mpm, trusty-mpm-teacher, trusty-mpm-research. Selected with /config or the outputStyle settings key.

Install

Two lines. The bootstrap puts tctl — the install control plane — on your PATH; tctl install trusty-mpm then resolves what tm needs at runtime and brings those daemons up too. No Rust toolchain is required on a supported platform.

shell

curl -sSf https://raw.githubusercontent.com/bobmatnyc/trusty-tools/main/install.sh | sh
tctl install trusty-mpm
tm --version

Homebrew and cargo install from a checkout both work; the other install paths are on the home page. macOS 12 or later and Linux are supported. Windows is not.

The bootstrap script verifies every downloaded tarball against its published SHA-256 checksum. The script itself is unsigned — read it before piping it to a shell if you need higher assurance.

A supervising daemon, not a process per session

claude-mpm runs inside the session it manages: start it, and its lifetime is that session's lifetime. trusty-mpm inverts that. A long-lived daemon holds the project registry and the session roster, and sessions are things it configures, launches, and supervises. Close a terminal and the daemon still knows what is running; restart the daemon and the roster survives, because it is on disk rather than in a process.

Two more daemons come with it. Together they are what a session reads from and writes to, and each one is also an MCP server, so a session reaches them through tool calls rather than by shelling out.

DaemonTransportWhat it holds
trusty-mpm127.0.0.1:7880Registered projects, the session roster, and the relayed hook feed. A separate supervisor process watches sessions and publishes its fleet snapshot to a file the daemon reads.
trusty-memoryUnix socketMemory palaces — long-term recall organised per project, over an HNSW vector index, a redb store, and a knowledge graph.
trusty-search127.0.0.1:7878Named code indexes, one per project, kept fresh by a file watcher. One install per machine.

Every one of those is local-only. trusty-mpm and trusty-search bind loopback HTTP and additionally reject requests whose origin is not the machine itself; trusty-memory's daemon has no network listener at all — its transport is a Unix domain socket, reachable only to processes on the same host. Either way, a page you happen to have open cannot reach them.

launchd

memory and search

Registered as LaunchAgents and controlled through tctl start / stop / restart, which drive launchctl underneath. They come back after a reboot without you doing anything.

Own verb

the tm daemon

Not launchd-managed. It ships its own tm start / stop / restart, and tctl shells out to those. Worth knowing before you go looking for a plist that does not exist.

Addresses are resolved from each daemon's discovery file rather than assumed, so a daemon that rebinds to a different port is still found. When one is unreachable the caller gets an error, not a silent timeout.

Sessions live in tmux

A tm session is a tmux session. That is not a convenience wrapper you can opt out of — it is how the daemon can keep a session alive while no terminal is attached to it, and how several sessions run at once without one window per session on your screen.

Names are derived rather than random. A session gets tm-<project>-NN — an explicit hint if you gave one, otherwise the repository name, otherwise the directory's own name — with a per-project serial so two sessions on the same project stay distinguishable. When a session does get its own git worktree, the worktree is named after the session, so a path like .worktrees/tm-trusty-tools-01/ tells you which session owns it without consulting anything.

CommandWhat it does
tm launchDeploys instructions, agents, skills and MCP config, then starts or attaches the session here.
tm connectStarts or attaches the tmux-hosted session and nothing else — no deployment step.
tm lsOn a terminal, an interactive picker over the live fleet. Piped or with --json, a plain list.
tm attach <target>Finds a session by id, name prefix, or project path and opens the dashboard focused on it.
tm tuiA terminal dashboard across every live session.

Sessions also pause and resume — tm sessions pause and tm sessions resume — and finish with tm sessions decommission, which removes the session's worktree and branch together rather than leaving either behind.

Agents deploy per project, not into your global config

This is the difference most likely to surprise you, and it comes from a real incident. A shared global ~/.claude/agents is a single namespace every Claude Code tool writes into, and in July 2026 one tool's agents leaked into another tool's sessions. The fix was a rule: trusty-mpm never depends on the user's real ~/.claude, and never writes your live checkout.

The mechanism is one environment variable. tm launches Claude Code with CLAUDE_CONFIG_DIR pointed at a directory tm owns, so every ~/.claude path Claude Code would otherwise read resolves inside that directory instead. Your real ~/.claude — and whatever hooks, MCP servers, and agents another tool put there — is excluded outright rather than merged on top. Project-scoped content lands in the checkout's own .claude/ inside a workspace tm controls.

Surfaceclaude-mpmtrusty-mpm
Agents and skillsThe real ~/.claude, sharedA tm-owned config dir, plus the project's own .claude/
Hooks and MCP serversThe real ~/.claude, sharedThe same tm-owned dir; the real global is not merged
Per-session stateThe project's .trusty-mpm/, holding the instructions the session actually received

A side effect worth knowing

Because the real global is excluded, a customisation you added to ~/.claude for claude-mpm does not follow you into a tm session. That is the point of the isolation, not a bug in it — but it does mean a hand-written agent or hook you rely on has to be reinstated on tm's side rather than inherited.

Memory and search per project

Memory is a palace: a named store scoped to a project rather than to a conversation, holding prose an assistant wrote and can recall later, alongside a knowledge graph of structured triples. A session reaches it through MCP tool calls at an address resolved from the daemon's own discovery file — never a hardcoded port, which is what used to make this silently fail when the daemon rebound.

Search is scoped the same way, and more tightly than you might expect. When a session is provisioned, its project gets a search index derived from the project's path, and that index id is pinned into the session's own .mcp.json. Every search the session runs is therefore already scoped — there is no ambiguity for the daemon to guess at, and a query cannot return results from the last project you worked on. When the session is decommissioned the index is marked for collection, so indexes do not accumulate.

Migrating your kuzu-memory data

If the memory server you were running is kuzu-memory, trusty-memory migrates it for you rather than leaving you to re-enter anything. trusty-memory migrate takes two targets, and they are independent: one rewrites configuration, the other moves data. Most people want both, in that order.

kuzu-memory — the configuration

Scans every .claude/settings.json and settings.local.json under your home directory, drops any kuzu-memory or kuzu_memory entry from the mcpServers block, and inserts a canonical trusty-memory one in its place. Unrelated keys survive, each write is atomic with a .bak alongside it, and a file already carrying a trusty-memory entry is left byte-for-byte alone — so running it twice is safe.

shell — look first, then write

trusty-memory migrate kuzu-memory --dry-run
trusty-memory migrate kuzu-memory

kuzu-data — the memories themselves

Reads a kuzu-memory store.redb and imports it into a palace: every entity becomes a drawer, every relation becomes a knowledge-graph triple. There is no default source path — --from and --palace are both required, and the command errors naming the missing one rather than guessing. The palace is created if it does not exist yet. Re-running it changes nothing: drawer ids come from a stable hash of the entity id and the palace name, and an existing triple is skipped rather than duplicated.

shell

trusty-memory migrate kuzu-data \
  --from ~/.open-mpm/memory/store.redb \
  --palace your-project --dry-run

--dry-run prints the schema it found and the plan without writing. --limit <N> caps how many entities are imported, which is the way to try it on a slice before committing to the whole store. Drop --dry-run to run it for real.

One class of triple does not come across

Four predicates — is_alias_for, has_convention, is_fact, and is_shorthand_for — put a fact on the surface injected into every turn of every session, and the importer refuses them outright. A bulk import of a legacy file is not somebody deciding a standing rule should be in front of every session, so a relation whose type collides with one of the four is logged and skipped while the rest of the import continues. Ordinary relation types — relates_to, mentions, derived_from, part_of, alias_of — are unaffected. If one of the four really was a standing rule, re-assert it with kg_assert, which is the deliberate path and carries the real gate.

Where the work happens

By default a session runs on your project's main checkout — several sessions sharing one checkout is the normal arrangement rather than a hazard. What keeps that safe is a write boundary the harness enforces rather than asks for: a session standing in a main checkout may write documents and configuration there, and an agent it dispatches that needs to change source is granted its own git worktree instead.

tm launch --worktree opts the whole session into its own worktree when you want the live checkout left completely alone.

The migration, step by step

Nothing here uninstalls claude-mpm. The two can coexist — they read different configuration directories, so neither sees the other's agents — and you can migrate one project at a time.

  1. Install trusty-mpm.

    The two lines above. tctl brings up the memory and search daemons as part of the same run.

  2. Deploy the framework.

    tm install composes and writes the agent roster, the skills, the hook registrations, and the output styles into the tm-owned config directory. This is the step that makes a session find a full roster rather than a partial one.

  3. Bring your kuzu-memory data across — only if you ran it.

    trusty-memory migrate kuzu-memory repoints the MCP config, then trusty-memory migrate kuzu-data --from <store.redb> --palace <name> imports the memories. Both take --dry-run, and both are safe to re-run. The full section covers what each one touches and the one class of triple it declines to import.

  4. Register the project.

    From the project directory, tm project init registers it with the daemon. Registration rather than inference is what makes the same directory resolve to the same project across sessions and across restarts.

  5. Clear a stale output style.

    If a project or global settings.json still carries an outputStyle left over from claude-mpm, the session will not be running under trusty-mpm's instructions at all. tm doctor's output-style check reports exactly this, and a fresh tm launch rewrites the setting correctly.

  6. Verify before you start work.

    tm doctor runs the full diagnostic and needs a reachable daemon. tm validate runs the same deployment diff against the filesystem with no daemon at all and exits non-zero when something is missing, which makes it the one to put in a script. tm health is the one-line answer.

  7. Start a session.

    tm launch in the project directory. From there it should feel familiar — and tm ls will find it again after you close the terminal.

shell — the whole thing

tm install
trusty-memory migrate kuzu-memory      # only if you ran kuzu-memory
cd ~/your-project
tm project init
tm doctor
tm launch

Everything tm can do

The daemon, the dashboard, the hook relay, and remote control from Telegram or Slack.

trusty-mpm

Browse the documentation →