Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

mind installs agent tooling (skills, agents, rules, commands, workflows, tools, and shared resources) from git repos into your agent homes, and keeps it up to date.

Point it at any repo that ships agent tooling and it finds the items by their layout, with no manifest required. It copies the items you choose into a store it manages and links them into ~/.claude (or Gemini CLI, Codex CLI, Windsurf, or any directory you name). Later it tells you what changed upstream and upgrades on your say-so.

mind meld anthropics/skills     # register a repo and pick items to install
mind probe                      # browse everything your repos offer
mind upgrade                    # pull upstream changes into installed items

Who it is for

  • People using agent tooling. Install skills from several repos without copying files around, see what is installed and where it came from, update or roll back, and keep more than one agent harness in step.
  • Teams. Share one curated set of sources, reproduce a setup on a new machine with one command, and (for an organization) restrict which sources may be installed.
  • People publishing agent tooling. Any repo is installable as it is. An optional mind.toml adds a namespace, install profiles, hooks, or an explicit inventory.

Where to go next

The normative behavior is the spec; these pages describe the same behavior for people using it.

Install

Requirements

mind runs git to clone and sync sources, so git must be installed and on your PATH. The methods below fetch the mind binary itself; they do not install git. Without git, meld and sync fail with a clear “git executable not found” error.

Install script (Linux and Apple Silicon macOS)

curl --proto '=https' --tlsv1.2 -fsSL https://raw.githubusercontent.com/jaemk/mind/main/resources/install.sh | sh

Downloads the release binary for your platform (x86_64 or aarch64 Linux, or aarch64 macOS) and installs it to ~/.local/bin. On Linux it fetches the musl build, which is statically linked and runs on any glibc version (Ubuntu 22.04, Debian 12, RHEL 9, Amazon Linux 2, and newer). The script verifies the download against the published SHA256SUMS asset before extracting. Override the target dir with MIND_INSTALL_DIR or pin a version with MIND_VERSION:

curl --proto '=https' --tlsv1.2 -fsSL https://raw.githubusercontent.com/jaemk/mind/main/resources/install.sh \
  | MIND_INSTALL_DIR=/usr/local/bin MIND_VERSION=0.12.0 sh

If ~/.local/bin is not on your PATH, the script prints the line to add.

Homebrew (Apple Silicon macOS and Linux)

brew tap jaemk/mind https://github.com/jaemk/mind
brew trust jaemk/mind
brew install mind

The repo is not named homebrew-mind, so the tap needs its clone URL. Homebrew bottles are provided for Apple Silicon macOS (arm64) and Linux (x86_64 and aarch64, glibc-linked). Intel macOS is not covered by the tap; use cargo install --locked mind-cli instead (see below).

cargo install (Linux and macOS)

cargo install --locked mind-cli

Builds from source using the Rust toolchain. This is the recommended path for Intel macOS and any other Linux or macOS host not covered by the install script or Homebrew tap. Requires Rust 1.98 or later (rustup is the standard way to install it).

Use --locked. Without it cargo install re-resolves dependencies instead of using the published Cargo.lock, and that lockfile is the one tested against Rust 1.98 in CI. A fresh resolve can pick a newer dependency needing a newer compiler, which fails the build with a rustc-version error naming a crate you did not ask for.

The supported platforms are Linux and macOS; the binary does not currently build on Windows. On Windows, run mind under WSL (Windows Subsystem for Linux).

Updating

mind evolve updates the binary itself to the latest release. It reports the target version and the resolved target triple (the exact artifact it would fetch) and prompts before downloading, unless --yes is given (--check reports without changing anything, --to <v> pins a target). It downloads the same release artifacts the install script uses and verifies the SHA256SUMS asset before swapping in the new binary. Unlike the install script, it makes the request itself rather than shelling out to curl or wget, so neither needs to be on PATH, and it verifies TLS against the machine’s certificate store.

If a gh binary is on PATH, evolve also verifies the downloaded archive’s build-provenance attestation (gh attestation verify) before swapping. Without gh, this step is skipped and evolve proceeds as it always has. Unlike install.sh (which never blocks on this check), evolve aborts the swap, leaving the existing binary in place, when gh runs the check and reports the artifact does not verify; it still proceeds, with a note, when gh itself could not complete the check (no network, or a gh build without the attestation subcommand).

Uninstall

Remove installed items and sources first, then the binary.

  • mind forget <item> removes one installed item: its store copy (~/.mind/store/<kind>/<name>) and its symlink in every lobe. mind forget --unmanaged also removes lobe items mind did not install (with no <item>, every unmanaged item across all lobes).
  • mind unmeld <name> drops a melded source and uninstalls the items it installed; --keep-items drops the source without touching installed items.
  • rm -rf ~/.mind (or the MIND_HOME override) removes the store, source clones, manifest, registry, and config – everything mind tracks on disk. forget/unmeld the items you want cleaned up first: deleting ~/.mind leaves lobe symlinks dangling, and mind introspect on a lobe with no ~/.mind reports every link as broken.
  • Remove the mind binary itself:
    • Install script: delete it from ~/.local/bin/mind, or from $MIND_INSTALL_DIR/mind if you overrode the target directory.
    • Homebrew: brew uninstall mind.
    • cargo install: cargo uninstall mind-cli.

Quickstart

This page installs a first skill, shows where it went, and points at what to do next. It assumes mind and git are installed (Install).

1. Meld a source

mind meld jaemk/mind

meld clones the repo, lists the items it offers, and asks whether to install them. Confirm to install all of them, or pick individually. For this repo that is one example skill, hello-mind. It also registers two sources the repo recommends, anthropics/skills and ComposioHQ/awesome-claude-skills, without installing anything from them.

Any repo that ships agent tooling works the same way:

mind meld owner/repo
mind meld https://gitlab.example.com/team/agent-tools
mind meld ~/dev/my-skills

2. See what is installed

mind recall

recall lists each source with its items, marked installed or available. The skill is now linked at ~/.claude/skills/hello-mind, and Claude Code offers it as /hello-mind.

3. Browse and install more

mind probe

probe opens an interactive browser over every melded source: search, read descriptions, and install or remove items. See Browse in the terminal UI. To install by name instead:

mind learn skill-creator          # one item
mind learn 'anthropics/skills#*'  # every item of one source

4. Keep it current

mind upgrade

upgrade fetches the sources your installed items came from, shows what changed, and asks before applying anything.

Where next

Core concepts

mind connects sources (git repos full of agent tooling) to lobes (your agent homes, like ~/.claude). This page defines the words the rest of the docs use and shows what each step puts on disk.

Sources, items, lobes, and the store

Source. A git repo you have melded. mind meld <repo> clones it into ~/.mind/sources/<host>/<owner>/<repo> and records it in ~/.mind/sources.json. Melding makes its items available; it does not have to install any. mind sync refreshes the clone later.

Item. One thing a source offers, of one of seven kinds:

kindin a source repowhat the harness does with it
skillskills/<name>/SKILL.md plus any bundled filesloads the skill; offers it as /<name>
agentagents/<name>.mda subagent the harness can delegate to
rulerules/<name>.mdcontext the harness reads
commandcommands/<name>.mda slash command, /<name>
workflowworkflows/<name>.jsa script offered to the harness’s Workflow tool (see Workflows)
tooltools/<name>/nothing directly: a helper other items call (see Share helper scripts)
resourceresources/<file-or-dir>nothing directly: data other items read, offered only when something references it (see Share data files)

Items are found by these conventional paths, by a Claude plugin manifest, or by an explicit list in the source’s mind.toml. See Source layout and item kinds.

Claude Code treats custom commands and skills alike: both appear as /<name>. A source that ships commands/foo.md and skills/foo/SKILL.md offers two distinct items (command:foo and skill:foo) that link into different directories, so mind installs both if you ask for both. Install only one unless you mean to have both.

Lobe. A directory mind links items into: an agent home holding skills/, agents/, rules/, commands/, and workflows/. The default lobe is ~/.claude. You can add Gemini CLI, Codex CLI, Windsurf, or any directory, each optionally limited to the kinds that harness understands. See Use more than one agent.

Store. ~/.mind/store/<kind>/<name>, where mind keeps the installed copy of each item. Lobes link to the store, not to the source clone, so a sync never changes an installed item under you. Only upgrade does.

Learn and forget. mind learn <item> copies an item from the clone into the store and links it into every lobe. mind forget <item> removes both. Tools and resources are the exception: they live in the store only and are not linked into a lobe.

What each step puts on disk

mind meld jaemk/mind clones the source. Nothing is linked yet:

~/.mind/
  sources.json                              # registry: `mind` is now melded
  sources/
    github.com/jaemk/mind/                  # the clone
      examples/hello/skills/hello-mind/     # the offered skill
        SKILL.md
      ...
  store/                                    # empty: nothing learned yet

mind probe lists what the melded sources offer (kind:name, source, content hash, description):

$ mind probe --no-tui
  skill:hello-mind  github.com/jaemk/mind  c62e88cc  A hello-world skill; confirms mind melded this repo

mind learn skill:hello-mind copies that item into the store and links it into the lobe:

~/.mind/
  manifest.json                                  # registry: `hello-mind` installed
  store/
    skill/hello-mind/SKILL.md                    # the copy, taken from the clone
  sources/
    github.com/jaemk/mind/ ...                   # clone untouched

~/.claude/                                       # a lobe
  skills/
    hello-mind -> ~/.mind/store/skill/hello-mind # the link the harness finds

The harness now resolves /hello-mind through that link. A tool or resource the skill used would land in ~/.mind/store/tool/<name> or ~/.mind/store/resource/<name>, with no link under ~/.claude.

Staying current

  • mind sync fetches every source. Installed items do not change.
  • mind upgrade shows what changed upstream for each installed item and, once you confirm, swaps in the new versions. It syncs the sources it needs first.
  • mind evolve updates the mind binary itself.

See Keep everything up to date.

Naming things

Commands take refs to name items and sources.

formnames
reviewan item named review, of any kind
skill:reviewthe skill named review
owner/repo#reviewthe item review from that source
'skill:*'every skill (a glob; quote it)
owner/repo or repoa source, by its trailing path
github.com/owner/repoa source, by its full identity
github.com/owner/repo@jkthe instance of that repo melded under namespace jk
github.com/owner/repo#skills/fooa single-item source created from an item link

A source’s identity is host/owner/repo, so the same repo name on GitHub and on an internal host never collide. See Commands for which verbs need an exact match and which accept several.

Namespaces

If two sources each ship a review, they would install to the same path. A namespace (also called a prefix) makes one of them install as jk:review. A source can declare its own; you can impose one with meld --namespace. See Fix name collisions.

Glossary

termmeaning
meldregister a source (clone it) and optionally install its items
unmelddrop a source and, by default, uninstall its items
learninstall an item
forgetuninstall an item
recalllist what is installed
probebrowse and search what sources offer
introspectcheck for drift and broken links
evolveupdate the mind binary
lobean agent home mind links into
store~/.mind/store, the installed copies
curatora source that lists other sources (a super-source or a Claude marketplace)
profilea named set of a source’s items, chosen at meld time
unmanaged itemsomething in a lobe that mind did not install
driftan installed item whose content differs from its source

Verb aliases

Each verb also answers to a conventional package-manager name. These docs use the primary verbs.

aliasprimary verb
addmeld
remove, rmunmeld
installlearn
uninstall, unlearnforget
updatesync
list, statusrecall
searchprobe
doctorintrospect
reconcilecurate
self-updateevolve

Note that update is sync (it refreshes sources) and does not upgrade installed items; upgrade does that.

Recipes

Short answers to common tasks. Each links to the page with the details.

mind learn https://github.com/owner/repo/tree/main/skills/foo

Installs just that skill. mind forget skill:foo removes it. See Install one item from a link.

Install only some items from a large repo

mind meld owner/big-repo --learn 'review*' --learn agent:dev --yes

Or register it and pick later:

mind meld owner/big-repo --register-only
mind probe --source big-repo

See Choose what a source installs.

Switch a source to its “full” profile

mind learn owner/repo --profile full

And back: mind forget owner/repo --profile full. See Install profiles.

Track a branch while testing a change

mind meld owner/repo --pin branch=my-feature
mind upgrade 'owner/repo#*'

Return to the default branch with --pin branch=main, then upgrade. See Pin a source to a version.

Undo a bad upgrade

mind meld owner/repo --pin <previous-sha>
mind upgrade skill:review --yes

See Roll back an item.

Use Claude Code and Codex side by side

mind config lobes add --preset codex

Every installed skill now links into ~/.agents too, and new installs go to both. See Use more than one agent.

Install skills into one project only

cd ~/projects/myapp
mind link-project --preset windsurf      # or: mind config lobes add ./.claude
mind learn skill:review --local

See Project lobes.

Two repos ship a review skill

mind meld other/repo --namespace other

Its skill installs as other:review. See Fix name collisions.

Keep a skill you wrote by hand under version control

mind absorb skill:my-skill --to ~/dev/my-agent-library

Moves it into that repo, commits it, and links it back as a managed item. See Adopt items you placed by hand.

Set up a new machine like this one

mind dump --output mind.toml          # on the old machine, in a git repo you push
mind meld <that-repo> --yes           # on the new machine

See Back up and reproduce your setup.

Onboard a teammate with one command

mind meld git@github.com:acme/agent-setup --yes

See Share a setup with your team.

See what changed upstream without applying it

mind sync
mind recall            # out-of-date items are marked

mind upgrade then shows the details and asks before applying. See Keep everything up to date.

Find out where an item came from

mind recall skill:review

Shows its source, commit, profiles, and dependencies.

Remove everything mind installed

mind unmeld '*' --yes

See Uninstall mind entirely.

Publish your own skills

mkdir -p my-skills/skills/greet
$EDITOR my-skills/skills/greet/SKILL.md
cd my-skills && git init -q && git add -A && git commit -qm init
mind review            # check it

Push it, and mind meld owner/my-skills installs it anywhere. See Publish your first source.

Find and install items

Once a source is melded, its items are available to browse and install. This page covers finding an item, installing it, and what learn does with the items it depends on.

mind probe review            # search names and descriptions for "review"
mind learn skill:review      # install it
mind recall                  # confirm it is installed

Browse what is available

mind probe opens an interactive browser on a terminal (see Browse in the terminal UI). Give it a query, or pipe it, for a plain listing:

mind probe --no-tui               # everything every source offers
mind probe pdf                    # names or descriptions containing "pdf"
mind probe --kind agent           # only agents
mind probe --source '*anthropic*' # only items from matching sources
mind probe --json                 # one JSON document, for scripts

--kind takes skill, agent, rule, command, workflow, tool, or resource. --source takes an exact source name, a trailing part of it (repo or owner/repo), or a glob matched against the full host/owner/repo identity. Matching several sources is normal. mind recall accepts the same two filters for the installed listing.

Install by name

mind learn review                  # an item named review
mind learn skill:review            # the skill named review, when an agent shares the name
mind learn acme/skills#review      # review from one specific source

A bare name that matches items of two kinds (skill:review and agent:review) is an error rather than a guess; add the kind.

Install many at once

learn takes a glob. Quote it so the shell does not expand it first:

mind learn 'skill:*'               # every skill from every source
mind learn 'acme/skills#*'         # every item of one source
mind learn acme/skills --all       # the same, without the glob
mind learn 'review*'               # every item whose name starts with review

A glob that matches nothing is an error. To install a named subset of a source in one step at meld time, see Choose what a source installs.

Items that depend on other items

An item can reference siblings in its own source, for example a skill that hands off to an agent. When you install part of a source, learn follows those references and installs what they need, then shows the tree before changing anything:

$ mind learn skill:review
skill:review  [selected]
  agent:dev   [dependency]
    agent:test  [dependency]

Install these 3 items? [y/N]

The same goes for the tools and resources an item points at with {{tools:name}} or {{resources:name}}: learning the item installs them too, after the same prompt.

Items already installed are marked [installed] and left alone. Preview the set without installing anything with --dry-run:

mind learn --dry-run skill:review

forget does not cascade: removing review leaves dev and test installed, since another item may still use them. See Dependencies between items for how authors declare these references.

When a path is already taken

mind never overwrites a file or link it did not create. If ~/.claude/skills/review already exists and is not one of its links, learn asks on a terminal and refuses otherwise. Move the existing entry aside, or pass --force to replace it. To bring a hand-placed item under management instead, see Adopt items you placed by hand.

Installing into one project only

By default an item links into every configured lobe. Inside a project that has its own lobe (see Use more than one agent), --local links it into that project’s lobe only:

cd ~/projects/myapp
mind learn skill:review --local

Other ways in

Install one item from a link

Paste the URL of a skill straight from GitHub or GitLab and learn installs just that skill, without melding the rest of the repo:

mind learn https://github.com/owner/repo/tree/main/skills/foo
mind learn https://github.com/owner/repo/blob/main/skills/foo/SKILL.md

A link to any other .md file installs that one file as an agent, rule, or command:

mind learn https://github.com/owner/repo/blob/main/agents/reviewer.md
mind learn https://github.com/owner/repo/blob/main/rules/style.md

How the kind is decided

The first of these that applies wins:

  1. --kind agent, --kind rule, or --kind command on the command line.
  2. The file’s directory: agents/, rules/, or commands/.
  3. A kind: key in the file’s own frontmatter.

A link into a conventional layout needs no annotation. A file elsewhere needs --kind:

mind learn https://github.com/owner/repo/blob/main/vendor/reviewer.md --kind agent

Skills and tools are directories, so --kind never names them; a directory link is always a skill. A workflow (workflows/<name>.js) cannot be installed from a link, since a file link takes .md files only. Meld the repo and learn workflow:<name> instead.

--kind is fixed when the link is first registered. Running it again against an already-registered link changes nothing, and mind says so.

What gets registered

The link registers as its own single-item source with the identity host/owner/repo#<path>, for example github.com/owner/repo#skills/foo. It clones, syncs, and upgrades like any source, but offers exactly the linked item. Several links into the same repo, and a plain meld of that repo, coexist as separate sources.

The URL’s ref sets the version:

  • a branch name (tree/main/...) follows that branch; upgrade picks up new commits.
  • a 40-character commit pins it.
  • --pin freezes a branch link at the branch’s current commit:
mind learn https://github.com/owner/repo/tree/main/skills/foo --pin

mind meld <url> accepts the same URLs and runs the normal meld flow (--register-only, --namespace, --kind, --pin <value>). A local repo is reachable as file:///path/to/repo/tree/<branch>/<path>.

Because you name the exact path, a link reaches items the repo’s own inventory does not list, such as a skill left out of an authoritative mind.toml or a .claude-plugin/marketplace.json.

Removing it

mind forget <item> uninstalls the item and leaves the link’s source registered, with a hint to drop it:

mind unmeld 'github.com/owner/repo#skills/foo'

When the item needs its siblings

A link offers one item, so an item that references others in its repo cannot get them this way. That covers a requires: frontmatter entry and a {{ns:}}, {{tools:}}, or {{path:}} token (see Dependencies between items).

  • A requires: entry is metadata. The item installs, mind warns which entries it dropped, and mind recall <item> and mind introspect keep showing the gap.
  • A token would be written into the item’s text, so it is an error and nothing installs.

Both print a command that swaps the link for a meld of the whole repo and installs that one item with what it needs:

mind unmeld 'github.com/owner/repo#skills/foo' --yes && \
  mind meld https://github.com/owner/repo --learn 'skill:foo' --yes

When the repo’s own inventory (an authoritative mind.toml or plugin manifest) does not list the item, the command adds --add-root, derived from the link’s path:

mind unmeld 'github.com/owner/repo#skills/foo' --yes && \
  mind meld https://github.com/owner/repo --add-root '.' --learn 'skill:foo' --yes

A file link outside its kind’s directory (vendor/reviewer.md) has no such remedy, since a whole-repo meld would never find it; the error says to drop the reference or move the file under agents/ upstream. The printed command does not carry over a --namespace, --pin, or extra --add-root the link was melded with; add those yourself.

A curator can list an item link in [discover].sources, with kind when the file needs it:

[[discover.sources]]
source = "https://github.com/owner/repo/blob/main/vendor/reviewer.md"
kind = "agent"
install = true

mind dump writes link sources back out as URLs, so a setup that includes them reproduces. Spec: spec/item-link.md.

Choose what a source installs

By default meld offers every item a source ships. For a large repo you usually want less. There are three ways to choose:

you wantuse
to decide later, item by itemmeld --register-only, then learn
specific items by name or patternmeld --learn <pattern>
a set the source’s author definedmeld --profile <name>

Register now, install later

mind meld owner/repo --register-only
mind probe --source owner/repo        # browse what it offers
mind learn owner/repo#review

Install items by name or pattern

--learn narrows the meld to the items you name, in one command:

mind meld owner/repo --learn review --yes
mind meld owner/repo --learn 'skill:*' --learn agent:dev --yes
mind meld owner/repo --learn 'pdf-*' --yes
  • The pattern selects within the source being melded: an item name or glob, never a source-qualified ref. Quote globs.
  • A name matches whether or not the source namespaces its items, so --learn review finds an item that installs as team:review.
  • Each match brings the siblings it depends on (see Find and install items). The rest of the source stays registered for learn later.
  • A pattern matching nothing is an error naming the source; the source stays melded.
  • *, ?, and [ are always glob syntax here, so an item whose name contains one cannot be selected literally.
  • --learn conflicts with --register-only.

Pass --yes in scripts. Like the install-all offer it replaces, --learn prompts on a terminal and installs nothing without one, so a script without --yes registers the source and exits 0 having installed nothing.

An item the source’s inventory does not declare is not reachable this way. Add --add-root <dir> at the meld that first registers the source to scan more of the repo; an already-melded source must be unmelded first.

For a super-source, --learn applies to its own items. The sources it curates are still registered, but nothing from them is installed.

Install profiles

A source can define named sets of its items, called profiles, in its mind.toml (for example default, full, minimal). If it defines a default profile, a plain meld installs that set instead of everything.

mind meld owner/repo                         # the default profile
mind meld owner/repo --profile full          # default plus full
mind meld owner/repo --no-default-profile --profile full   # only full
mind meld owner/repo --all-profiles          # every profile

--profile takes a comma-separated list or repeats. An unknown name is an error that lists the profiles the source declares, and leaves the source melded with nothing installed.

On a terminal, the first meld of a source that declares default and at least one other profile asks which profiles to enable, with default preselected. Press Enter to keep it, or type the names you want instead (full alone enables only full). mind recall --sources shows which profiles are enabled for each source; the terminal UI shows them in the source’s details dialog, with Enable and Disable actions.

Change profiles later

mind learn owner/repo --profile full     # enable full and install its items
mind forget owner/repo --profile full    # disable full

Disabling a profile forgets the items that were installed only for it. It keeps an item you installed by name, an item another enabled profile includes, and an item another installed item depends on.

When the author adds an item to a profile you have enabled, mind upgrade offers to install it. When an item leaves a profile, upgrade reports it and leaves it installed; forget it if you no longer want it. An item you forget yourself is not offered again while it stays in the profile.

What profiles do not cover

  • A source with no default profile keeps the install-everything offer on a plain meld; its profiles are opt-in.
  • --all-profiles enables every profile but does not install items that are in no profile. learn owner/repo --all installs the whole source.
  • --learn and --profile compose: the meld offers the union.

To define profiles for your own source, see Offer install profiles.

Pin a source to a version

By default a source follows its remote’s default branch: sync fetches the latest commit and upgrade offers whatever changed. Pin a source to stay on a known version, follow a different branch, or roll back.

mind meld owner/repo --pin v2.0              # freeze at tag v2.0
mind meld owner/repo --pin branch=stable     # follow the stable branch

Freeze or follow

--pin takes one value, and the form decides whether the source moves:

valueeffect
HEADfreeze at the commit the source currently resolves to
<tag>, <sha>, or <branch>resolve that ref now and freeze at its commit
branch=<name>follow that branch; sync moves to its latest commit
tag=<name>follow that tag; sync moves if the tag is re-pointed
# Freeze
mind meld owner/repo --pin HEAD
mind meld owner/repo --pin v2.0
mind meld owner/repo --pin a1b2c3d4e5f6
mind meld owner/repo --pin release-branch    # a snapshot of the branch's tip

# Follow
mind meld owner/repo --pin branch=stable
mind meld owner/repo --pin tag=latest

Note that --pin stable and --pin branch=stable differ. The first takes a one-time snapshot of stable that sync never moves; the second keeps tracking the branch. Use branch= to keep following.

A source’s own mind.toml can declare a pin (follow-branch, pin-tag, or pin-ref under [source]). A --pin you pass overrides it.

Change the pin of a melded source

Run meld --pin again on a source you already have:

mind meld owner/repo --pin branch=dev        # switch to tracking dev
mind upgrade 'owner/repo#*'                  # bring installed items to it

mind resolves the new pin, checks out the new commit, and records both. Nothing changes until every git step succeeds, so a mistyped ref leaves the source on its old pin. Installed items stay as they are until you upgrade.

The terminal UI takes the same flags in its meld input, for example owner/repo --pin branch=dev (see Browse in the terminal UI).

Roll back an item

Re-pin the source to the older commit, then upgrade the item. upgrade applies whatever the pin resolves to, older or newer:

mind recall --sources                    # find the source's name
mind meld owner/repo --pin <old-sha>
mind upgrade skill:review --yes

Pick <old-sha> from the forge’s history, or run git log in the clone under ~/.mind/sources/. A tag or branch works too (--pin tag=v1.4). To go forward again, re-pin (--pin HEAD, --pin branch=main) and upgrade.

An item link takes its version from the URL. learn --pin (a flag with no value) freezes a branch link at the branch’s current commit:

mind learn https://github.com/owner/repo/tree/main/skills/foo --pin

Organization-wide pinning

A managed policy can require every source to be pinned to a tag or commit, refusing floating branches.

Older flag spellings

--follow-branch <b>, --pin-tag <t>, and --pin-ref <c> still work as hidden aliases for --pin branch=<b>, --pin tag=<t>, and --pin <c>.

Keep everything up to date

Installed items do not change on their own. mind copies each one into its store, so upstream changes reach you only when you ask for them.

mind upgrade              # fetch, show what changed, confirm, apply

Upgrade installed items

mind upgrade fetches every source an installed item came from, then reports each item whose content changed, with its content hash and commit before and after. Nothing changes until you confirm.

mind upgrade                       # everything
mind upgrade skill:review          # one item
mind upgrade 'owner/repo#*'        # every item from one source
mind upgrade --no-sync             # skip the fetch; compare against the clones on disk
mind upgrade --yes                 # apply without asking

A filter that matches no installed item reports that everything is up to date rather than erroring. Each upgrade is transactional: the new copy is built first and the old one is kept until the swap succeeds, so a failed upgrade leaves the previous version in place.

upgrade also:

  • offers items a source added to an install profile you enabled, and reports items that left one without removing them. This runs on a plain upgrade or a source-wide one, not on upgrade <item>.
  • re-runs a source’s install hooks when the source moved to a new commit, or its update hooks if it declares any. See Run setup with hooks.
  • reports a rename when an item’s installed name changed (for example after a source added a namespace) and installs the new name before removing the old.

When the scope spans several sources, upgrade names each one before running any hooks, even with --yes.

Refresh sources without upgrading

mind sync                 # fetch every source
mind sync acme            # only sources whose name matches acme

sync updates the clones and catalogs, so probe and recall show what is new, but installed items do not change. recall and probe mark installed items that are out of date. sync exits non-zero if any fetch failed, after saving what did succeed.

mind update is an alias for sync, not for upgrade. sync --upgrade still works as shorthand for a sync followed by an upgrade, but plain upgrade already syncs first.

mind introspect           # report drift, broken links, unsynced sources
mind introspect --fix     # recreate missing links

Drift means an installed item’s content no longer matches its source. Editing a store copy by hand causes it, and so does a source that changed without an upgrade. introspect --fix repairs links but never changes versions; use upgrade for that.

Follow curators

If you melded a curated list of sources, mind curate applies the curator’s own changes (new entries, re-pins, removals). sync and upgrade do not. See Follow a curated collection.

Update the mind binary

mind evolve --check       # is there a newer release?
mind evolve               # download, verify, and replace the binary
mind evolve --to 0.29.0   # a specific version

See Install for how evolve verifies the download, and Restricted networks for proxies and IT-managed binaries.

Automate it

mind upgrade --yes

A non-interactive run skips install and build hooks unless you pass --dangerously-skip-install-hook-check and --dangerously-skip-build-hook-check. Both run code from the source, so use them only for sources you trust. See Scripts and CI.

Remove items and sources

forget uninstalls items; unmeld drops a whole source.

mind forget skill:review          # uninstall one item
mind unmeld acme/skills           # drop a source and uninstall its items

Uninstall items

mind forget review                # by name
mind forget skill:review          # when a name exists in more than one kind
mind forget 'acme/skills#*'       # every item from one source
mind forget 'skill:*' --yes       # every skill, without the confirmation

forget removes the item’s store copy and its link in every lobe. A glob that matches more than one item lists them and asks once; --yes skips that.

Removing an item that another installed item depends on asks first, since the dependent may stop working. --force skips only that question. forget never removes dependencies along with an item; uninstall them separately if you no longer want them.

If the item declares an uninstall hook, forget runs it after a prompt. A non-interactive run skips it unless you pass --dangerously-skip-hook-check.

Turn off an install profile

mind forget acme/skills --profile full

This forgets the items installed only for that profile and keeps the rest. See Choose what a source installs.

Drop a source

mind unmeld acme/skills                  # uninstall its items, then drop it
mind unmeld acme/skills --keep-items     # drop it, leave its items installed
mind unmeld '*agents' --yes              # every source matching a glob

unmeld needs its name to match exactly one source. repo or owner/repo is enough when it is unambiguous; a name matching several sources is an error rather than a guess. Use the full identity to pick one instance, such as github.com/acme/skills@jk for a namespaced instance or github.com/acme/skills#skills/foo for an item link.

A source can declare uninstall hooks, which run in its clone before the clone is removed. They prompt like install hooks; --uninstall-hook <cmd> replaces them and --dangerously-skip-hook-check runs them without a prompt. A required uninstall hook that fails or is aborted leaves the source melded.

Remove things mind did not install

forget touches only what mind installed unless you ask for more. To remove a hand-placed skill or agent from a lobe, see Adopt items you placed by hand.

Stop linking into an agent home

mind config lobes remove ~/.gemini/config              # unregister; links stay
mind config lobes remove ~/.gemini/config --snapshot   # replace links with copies, then unregister

See Use more than one agent.

Uninstall mind entirely

  1. mind unmeld '*' --yes uninstalls every item and drops every source. Do this first: deleting ~/.mind alone leaves dangling links in your lobes.
  2. Optionally mind forget --unmanaged to remove items you placed by hand.
  3. rm -rf ~/.mind (or your MIND_HOME) removes the store, clones, and config.
  4. Remove the binary: delete ~/.local/bin/mind (or $MIND_INSTALL_DIR/mind) for the install script, brew uninstall mind, or cargo uninstall mind-cli.

Use more than one agent

mind links every installed item into each configured agent home, called a lobe. The default is ~/.claude. Add Gemini CLI, Codex CLI, Windsurf, or any directory, and items you install appear in all of them.

mind config lobes detect          # find the harnesses installed here and offer to add them
mind config lobes add --preset codex
mind config lobes list

Add a harness with a preset

A preset knows a harness’s directory and which item kinds it understands:

presetlobe pathkinds linked
gemini~/.gemini/config (Gemini CLI and Antigravity)skill
codex~/.agentsskill
universal~/.agentsskill
windsurf<dir>/.windsurf, default the current directoryskill
mind config lobes add --preset gemini
# + added gemini lobe ~/.gemini/config [skill]

mind config lobes add . --preset windsurf
# + added windsurf lobe ./.windsurf [skill]

Adding a lobe creates its directory and links every already-installed item of the kinds it admits, in the same command. A preset for a harness you have not installed yet still works.

~/.agents is a vendor-neutral path: Codex reads it for user skills, and so does any harness following the same convention, so one lobe serves them all.

mind config lobes detect reports which known harness homes exist on this machine and offers to add them (--yes adds without asking, --json reports only). It never changes config without one of those.

harnessskills directoryagents directorylobe
Claude Code~/.claude/skills/<n>/SKILL.md~/.claude/agents/<n>.md~/.claude
Gemini CLI / Antigravity~/.gemini/config/skills/-~/.gemini/config
Codex CLI~/.agents/skills/(subagents)~/.agents
Windsurf<project>/.windsurf/skills/-<project>/.windsurf

Skills and agents use layouts several harnesses now share, so mind links them unchanged. Other kinds are Claude-specific:

  • Rules have no directory equivalent elsewhere (other harnesses use one concatenated file such as AGENTS.md or GEMINI.md), so presets never link them.
  • Commands and workflows are not linked by any preset either.

A lobe with no kinds filter receives every kind. ~/.claude has none, and neither does a lobe added by bare path:

mind config lobes add ~/.config/some-agent      # all kinds

To limit a lobe to particular kinds, write it by hand in ~/.mind/config.toml (config lobes add has no flag for it):

lobes = ["~/.claude", { path = "~/.gemini/config", kinds = ["skill"] }]

Linking an item into a lobe whose kinds excludes it does nothing for that lobe. That is not an error, and forget, upgrade, and introspect know not to expect the link.

Project lobes

A lobe can live inside a project instead of your home directory. Windsurf only reads skills from a project’s .windsurf/skills/, so a Windsurf lobe is always a project lobe:

cd ~/projects/myapp
mind link-project
# + added windsurf lobe ./.windsurf [skill]

link-project is shorthand for config lobes add . --preset windsurf. Use --preset for another harness or --subdir <dir> for an arbitrary subdirectory (skills only), for example --subdir .cursor.

A managed project lobe holds symlinks into ~/.mind/store, so add its directory to the project’s .gitignore. To commit the skills with the project instead, take a snapshot: real-file copies that mind does not manage or update.

mind link-project --snapshot

link-project never creates a lobe that links workflows, since every preset and --subdir admit skills only. To link workflows into a project’s .claude/, add it by path:

mind config lobes add ./.claude              # all kinds

Inside a project lobe, learn --local and meld --local install into that project’s lobe only, instead of every lobe.

Remove a lobe

mind config lobes remove ~/.gemini/config              # unregister; links are left in place
mind config lobes remove ./.windsurf --snapshot        # replace links with copies, then unregister

Frontmatter is not translated

mind links files as they are. A skill or agent whose frontmatter uses Claude-specific keys (such as the tools: allow-list) links into Gemini or Codex correctly, but that harness may ignore those keys or warn about them. Adapting frontmatter for another harness is up to the item’s author.

Per-invocation and organization overrides

  • MIND_AGENT_HOMES (a :-separated list) replaces the configured lobes for one command. It wins over lobes in config.toml, which wins over the default ~/.claude.
  • A managed policy can lock the lobes so users cannot change them.

Fix name collisions

Two sources can each ship an item with the same name, such as two skills called review. Both would install to ~/.claude/skills/review, so melding the second fails with a name-collision error that lists the conflicting items and suggests a namespace. Give one source a namespace and its items install as <namespace>:<name> instead:

mind meld acme/skills --namespace acme      # its review installs as acme:review

Claude Code then offers /acme:review alongside /review.

Namespacing a source

  • --namespace <prefix> (short -N) at meld time sets it. It overrides any namespace the source declares for itself in its mind.toml.
  • --namespace '' removes a namespace the source declares.

The namespace is part of the source’s identity. Melding a repo you already have under a different namespace registers a second, separate instance (github.com/acme/skills@acme) with its own clone and items; it does not rename the first. Address that instance by its full identity:

mind upgrade 'github.com/acme/skills@acme#*'
mind unmeld github.com/acme/skills@acme

To change the namespace of a source with nothing installed yet, use Set namespace in the terminal UI’s source details dialog (see Browse in the terminal UI). Once items are installed, unmeld and meld again with the new namespace.

Agents collide differently

The harness identifies an agent by the name in its frontmatter, not by its file name, so a namespace cannot rename an agent. mind links agents under their bare name even in a namespaced source. Two sources shipping an agent with the same frontmatter name therefore still collide: learn fails with an AgentCollision error naming the agent already installed, and meld warns about it in advance.

To resolve it, forget the agent you do not want before installing the other.

When a namespaced item breaks

Items that mention each other by name (a skill that says “hand off to the dev agent”) can break under a namespace, since dev is now acme:dev. Authors guard against this with {{ns:name}} references, which mind rewrites at install. When melding a namespaced source, meld --verbose warns about items that name a sibling in plain text. See Namespaces and references.

Commands and skills with the same name

commands/foo.md and skills/foo/SKILL.md are different items (command:foo and skill:foo) that link into different directories, so mind does not treat them as a collision. Both offer /foo to Claude Code. Install only one unless you want both.

Follow a curated collection

A curator is a source that lists other sources: a mind.toml with [discover].sources (a super-source), or a Claude plugin marketplace catalog. Melding one registers everything it lists in one step, and mind curate keeps you in step as the list changes.

mind meld acme/agent-registry        # registers every source it lists
mind curate                          # later: apply what the curator changed

Meld a curator

A meld registers each listed source as its own source, with its own clone and pin. By default it installs only the curator’s own items plus the entries the curator marked for install (install = true, install-items, or profiles). Everything else stays registered and available to learn or probe.

mind meld acme/agent-registry --recursive   # also offer every listed source's items
mind probe --source acme                    # browse what the chain offers

mind probe’s Available group also suggests sources listed by your curators that you have not melded yet; see Browse in the terminal UI.

Apply a curator’s changes

sync and upgrade do not act on a curator’s decisions. sync registers a newly listed source but installs nothing from it, and neither re-reads a curator’s pins or notices an entry it dropped. curate does:

mind curate              # report the plan, ask once, apply
mind curate --check      # report only
mind curate --yes        # apply without asking
mind curate --no-sync    # plan against the clones on disk; fetch nothing

It fetches every curator and the sources it manages, then reports one plan:

changemeaningapplying it
registerthe curator lists a source you do not haveregisters it, then installs what the entry declares
installthe entry declares items or profiles that are not installedinstalls them
repinthe entry’s pin no longer matches the source’s recorded pinre-pins and checks out the new commit
upgradea curated source’s installed items are out of dateruns the upgrade for those sources
unlista source you registered from the list is no longer on it--prune only: uninstalls its items and drops it
namespacethe entry declares a different namespace than your instance hasreport only; prints the unmeld and meld that adopt it
adoptthe curator lists a source you already have but does not managereport only; run the mind curate --adopt <identity> it prints

Changes apply in the order of the table. unlist needs --prune, so an unattended curate --yes never uninstalls anything. namespace and adopt are never applied by curate, --yes included.

What curate will not touch

curate changes only sources a curator registered. A source you melded yourself, even one a curator also lists, shows up as adopt instead of being changed. Run the printed --adopt command to hand it to the curator; after that it is managed like any curated source.

A failure on one source (an unreadable curator, a bad pin, a scan error) is listed under skipped and does not stop the run. A non-interactive run without --yes reports the plan and applies nothing. Registering and installing run the same hook prompts as meld and learn, so the --dangerously-skip-install-hook-check and --dangerously-skip-build-hook-check flags exist on curate too. Pending changes exit 0; under --json, outcome is clean, pending, or applied.

Make your own

To build a curated list for yourself or a team, see Curate other repos and Share a setup with your team. mind dump writes one from your current setup; see Back up and reproduce your setup.

Spec: spec/curate.md.

Adopt items you placed by hand

Agent homes often hold skills, agents, and rules that mind did not install: files you wrote yourself or another tool put there. mind lists these as unmanaged items, can remove them, and can move one into a git repo you own so it is versioned and managed from then on.

mind recall                         # unmanaged items are listed at the end
mind absorb skill:my-skill          # move it into a source you own, then manage it
mind forget skill:old-experiment    # or delete it

What counts as unmanaged

Any entry in a lobe’s skills/, agents/, rules/, commands/, or workflows/ directory that is not one of mind’s own links. A path is always one or the other. An item present in several lobes is listed once with every path it occupies. Tools and resources live only in the store, so they are never unmanaged.

List them

  • mind recall lists them after your sources, under “unmanaged: not installed by mind”, with each item’s kind:name and lobe paths. --kind filters them; --source hides them, since they have no source.
  • mind probe includes them in its listing and search. In --json each carries "unmanaged": true and no source or hash. recall --json covers managed items only.
  • The terminal UI shows an Unmanaged group.

Move one into a source you own

mind absorb <ref> takes one unmanaged item, moves it into a git repo you own, commits it there, and replaces the lobe entry with a managed link. From then on it upgrades, syncs, and forgets like anything else.

mind absorb skill:my-skill
mind absorb agent:reviewer --to ~/dev/my-agent-library

The ref must name exactly one item; globs are refused. Add a kind when a name exists in more than one.

Where it goes

The first of these that is set wins:

  1. --to <path>
  2. the MIND_ABSORB_TO environment variable
  3. absorb-to in ~/.mind/config.toml

With none set, an interactive run asks, offering ~/.mind/personal (created and git init-ed on demand), and then offers to save your answer as absorb-to. With --yes and no destination, it uses and saves ~/.mind/personal. A non-interactive run with no destination is an error.

The destination must already be a git repo, except ~/.mind/personal. The item lands at its conventional path (skills/<name>/, agents/<name>.md, rules/<name>.md, commands/<name>.md, workflows/<name>.js) and is committed as absorb <kind>:<name>. mind melds the destination first if it is not already a source. If the path is taken in the destination, absorb stops; --force overwrites it.

What it asks first

If the item sits in several lobes, absorb takes the content from one and deletes the other copies so the managed link can replace them. It lists what it will move and delete and asks once, even for a single lobe, since it deletes files you own. --yes skips the question; a non-interactive run without --yes changes nothing.

A failure before the install finishes leaves the original file in place and nothing recorded. absorb never loses the file.

After absorbing, the item’s name follows the destination source’s namespace, if it has one.

Remove unmanaged items

mind forget skill:old-experiment          # one item, by exact kind:name
mind forget --unmanaged 'skill:*'         # every unmanaged skill
mind forget --unmanaged                   # every unmanaged item in every lobe

forget deletes the file or directory itself, not a link. It always asks first and says that the item is not managed by mind; --yes skips the question, and a non-interactive run without --yes removes nothing.

An ordinary glob such as forget '*' matches managed items only, so it never deletes your own files. Unmanaged items are reached by exact kind:name, or by --unmanaged, which matches unmanaged items only and can never remove a managed one.

Back up and reproduce your setup

mind dump writes your current sources and installed items as one mind.toml. Melding that file on another machine, or on this one after a reset, recreates the same sources at the same commits with the same items installed.

mind dump --output my-setup.toml     # write it
mind meld ./my-setup-repo --yes      # reproduce it elsewhere (see below)

What it writes

The output is a super-source: a mind.toml whose [discover].sources lists each melded source. Each entry:

  • pins the source to the commit it is at now (pin-ref), overriding any pin the source declares, so the reproduction gets exactly what you have.
  • carries the meld-time settings: namespace, scan roots, and added roots.
  • records which items are installed:
    • every item: install = true
    • none: install = false
    • some: install-items = ["skill:review", "agent:dev"]
    • for a source installed through profiles: profiles = [...] (and default-profile = false when default is off), plus install-items for anything you installed by name on top.

An item link source is written back as its URL, so a skill you installed from a pasted link reproduces too.

mind dump                          # to stdout
mind dump --output snapshot.toml   # to a file
mind dump --whole-sources          # install = true for every source

--whole-sources drops the per-item filtering and offers each source’s full catalog instead of the subset you installed. With no sources melded, dump writes a valid empty super-source.

Reproduce it

A meld takes a repo, so put the file at the root of a git repo as mind.toml:

mkdir my-setup && cd my-setup
mind dump --output mind.toml
git init -q && git add mind.toml && git commit -qm "agent setup"
mind meld . --yes

Push that repo somewhere private and any machine can reproduce the setup with mind meld <url> --yes. To keep teammates in step as the list changes, see Share a setup with your team; mind curate applies the changes on each machine.

Keep a backup current

Run dump again after changes and commit the result. The diff shows what was added, removed, or moved to a new commit since the last snapshot.

Browse in the terminal UI

mind probe opens an interactive terminal UI: a browsable, searchable view of every source and item, from which you can meld, install, remove, sync, upgrade, and manage agent homes. For the probe verb’s flags, see Commands.

mind probe                 # open it
mind probe review          # open it with the search set to "review"
mind probe --kind skill    # open it filtered to skills

Quick keys: arrows move, Enter opens a details dialog with the actions for the selected source or item, m melds a new source, q quits.

Opening it

  • mind probe with no opt-out launches the TUI. It requires a TTY on stdout.
  • It falls back to the non-interactive catalog listing when --no-tui is given, when --json is given, or when stdout is not a terminal (piped or redirected).
  • The query, --kind, and --source arguments apply in both modes. In the listing they filter it; in the TUI they seed the initial search and filter state.

The browse tree

Two top-level groups, each an independently collapsible tree:

  • Installed: the manifest. Installed items grouped source -> kind -> item, each showing effective name, source, and short commit, matching what recall reports.
  • Available: catalog items of melded sources that are not installed, not-yet-melded sources suggested by the registry, and ad-hoc sources you enter, de-duplicated.

A third Unmanaged group appears below them when the agent home holds items mind did not install. See Adopt items you placed by hand.

Under each group the hierarchy is source -> kind (skills, agents, rules, commands, workflows, tools, resources) -> item -> detail. Expanding an item shows its description and frontmatter, and for a skill its file tree.

  • Up / Down: move the highlight.
  • Left / Right: collapse / expand a structural node (source and kind buckets).
  • Space: also collapses / expands a structural node.
  • Enter: open the details dialog on a source or item; on a group header or kind bucket it toggles instead.

As the selection moves, the view scrolls to keep the highlighted row away from the top and bottom edges while there are more rows to scroll. Near the start or end of the list the highlight may sit at the edge.

A search box filters the visible tree by case-insensitive substring over item name and description, across both groups, and composes with the active kind and source filters. Clearing the search restores the full tree.

Live refresh

The TUI polls the on-disk registry (sources.json) and manifest (manifest.json) about once a second, so changes made by another mind process or a direct edit appear without a manual reload. A refresh preserves the current selection, expansion, and search state, and is skipped while a mutating action is running.

Actions

Each action invokes the same verb the CLI exposes, against the same registry, manifest, and store, with one exception: upgrade does not fetch first (see below). Every mutating action confirms before applying; destructive actions (forget, unmeld --forget) require an explicit confirmation. Results and errors are shown inline.

  • Install / learn: install the selected Available item. On a higher node it installs in bulk: a source installs every available item from it, a kind bucket every item of that kind, and the Available group everything. Already-installed items are skipped.
  • Forget: uninstall the selected Installed item.
  • Meld / unmeld: meld the selected or entered source; unmeld a melded source. The TUI’s unmeld uninstalls the source’s installed items by default and confirms first. The m input takes a repo spec followed by any mind meld flags, parsed exactly as the CLI parses them, for example owner/repo --pin branch=dev --profile full. A flag the CLI would reject is shown as an error and the input stays open with your text. The meld then runs the CLI’s full flow on the normal terminal: registering, the profile picker, and the install offer.
  • Sync: sync all or the selected source.
  • Upgrade: upgrade pending items (or the selected item), showing the same deltas the CLI would and confirming before applying. Unlike the CLI’s plain upgrade, this does NOT sync first: it acts on whatever the last sync or poll already found to be out of date. Run Sync first if you want to check upstream before upgrading. When nothing is currently known to be out of date, the confirm says so relative to the last sync, not as a claim that you are fully current.
  • Config lobes: view and manage agent homes (list / add / remove).

An install preview arms the same dependency closure as the direct install action. See Find and install items.

Details dialog

Pressing Enter on a source or item opens a centered details dialog describing the node and listing the actions valid for it as a selectable list, each run through the normal confirm-and-execute path.

  • For an item: its kind, source, the commit when installed, the install profiles it was installed for, and the description. It offers Install when the item is not installed, else Forget. Installing a single item this way counts as asking for it by name, so disabling its profiles later keeps it.
  • For a source: its name and installed/available item counts, and Install all available items, Uninstall all installed items, and Unmeld. While the source has no installed items, a Set namespace action is also offered: it opens an input for a namespace prefix, so items later install as <prefix>:<name>. It disappears once any item from the source is installed (a source’s prefix locks once items are installed). An action is omitted when it would do nothing.
  • For a source that declares profiles: the declared profiles, with the enabled ones marked, and an Enable profile or Disable profile action for each. The confirm lists the items enabling installs, or the items disabling forgets and any it keeps because another installed item depends on them. These run mind learn <source> --profile <name> and mind forget <source> --profile <name>.

In the dialog, j / k move the highlight, Enter or y runs the highlighted action, and Esc, q, or n dismisses without acting. On a group header, kind bucket, or suggested source there is no dialog: Enter keeps its toggle/preview.

Registry preview

The Available group lists suggested, not-yet-melded sources: the union of the [discover].sources entries declared by all melded sources, de-duplicated by URL and excluding sources already melded. Expanding a registry entry shallow-clones it to a temporary preview area and shows its catalog tree under Available without registering it. Confirming promotes the preview to a real meld; declining discards the temp clone.

Terminal handling

  • Actions whose verbs may prompt interactively run with the TUI suspended: mind leaves raw mode and the alternate screen, runs the verb on the normal terminal so its prompts read stdin and write stdout exactly as the CLI does, then restores the alternate screen and redraws. After the verb you press Enter to return. The verbs that suspend are meld and unmeld.
  • While the TUI holds the terminal, every git child runs non-interactively (GIT_TERMINAL_PROMPT=0 and an ssh BatchMode=yes wrapper), so an auth-required remote fails fast with an error surfaced inline instead of hanging the UI on a hidden prompt. The suspended interactive meld restores interactive git for its duration, so a passphrase or host-key prompt works on the normal terminal.

Exit

  • q quits from the main view.
  • Ctrl-C is a force-exit from every mode. One Ctrl-C arms and shows a hint; a second consecutive Ctrl-C exits, so a single accidental Ctrl-C while typing does not quit. Any other key disarms.

Scripts and CI

Every verb runs without a terminal. This page covers prompts, hooks, JSON output, and exit codes for scripts and CI jobs.

mind meld git@github.example.com:platform/agent-config --yes
mind recall --json

Prompts

Pass --yes (-y) to answer every confirmation. Without it, a command that would ask on a terminal fails with ConfirmationRequired when stdin is not a terminal, rather than hanging.

meld is the exception: without --yes and without a terminal, it registers the source, prints that nothing was installed, and exits 0. Pass --yes to install.

--yes does not suppress the notice that names every source a multi-source upgrade or sync --upgrade will run hooks for.

Hooks

Install, update, and uninstall hooks are skipped without a terminal, with a note. To run them unattended:

hookflagverbs
a source’s or item’s install/update hook--dangerously-skip-install-hook-checkmeld, learn, upgrade, sync --upgrade, curate, hooks run
an item’s build hook--dangerously-skip-build-hook-checkmeld, learn, upgrade, sync --upgrade, curate, hooks run
an uninstall hook--dangerously-skip-hook-checkunmeld, forget

Each runs code from the source without asking. Use them only for sources you trust. See Run setup with hooks.

JSON output

--json is a global flag, accepted before or after the verb. Every verb answers it with exactly one JSON document on stdout, except:

  • dump, which always writes TOML.
  • completions and man, which print a script or a roff page.
  • init-source, which has no result document.
  • evolve, which writes its own JSON document by a separate path.

Shapes:

  • recall and probe: {"schema": 1, "items": [...]}.
  • introspect: {"schema": 1, "issues": [...], "sources": N, "items": N}.
  • Every changing verb (meld, learn, forget, sync, upgrade, unmeld, config lobes add and remove) returns an object with "schema": 1 and at least action, target, and outcome.
  • review: {"schema": 1, "action": "review", "outcome": "clean|advisory|failed", "hard": [...], "advisory": [...], "fixed": [...]}, each finding being {"kind": "<slug>", "message": "<text>"}.
  • hooks list: each matched source’s or item’s hooks. hooks run: a tally, {"schema": 1, "action": "hooks-run", "target": "...", "event": "...", "existed": N, "ran": N, "skipped": N}.
  • curate: one document whose outcome is clean, pending, or applied.

learn --json refuses with confirmation-required when the dependency closure adds items you did not name, unless --yes is given, since there is no prompt to answer.

Errors

Under --json, an error is a JSON envelope on stdout instead of text on stderr:

{"schema": 1, "error": {"kind": "item-not-found", "message": "..."}}

kind is a stable kebab-case slug (item-not-found, source-not-found, git, digest-mismatch, …) a script can branch on. An optional details member carries more; a failed review --json puts its findings document there. Command-line usage errors (exit 2) stay plain text.

Exit status

exitmeaning
0success, including “nothing to do” and curate with pending changes
1any runtime error
2a command-line usage error

Specific cases:

  • sync exits 1 if any source failed to fetch, after saving the ones that succeeded.
  • review exits 1 on a hard finding and 0 on advisory findings only.
  • upgrade <filter> with no match reports up to date and exits 0; sync <filter> with no match is an error.

Output format

Color and Unicode glyphs are used only when stdout is a terminal, the locale is UTF-8, NO_COLOR is unset, and neither --json nor --ascii is given. Any one of those failing gives plain ASCII with no escapes. The ASCII forms carry the same information (+ installed, - available, ! warning, x error).

Other global flags: --verbose (-v) adds advisory output such as unguarded-reference warnings, and --max-metadata-size <SIZE> sets the metadata size cap for one run (see Configuration).

A CI job

ssh-keyscan -H github.example.com >> ~/.ssh/known_hosts   # first SSH connection only
mind meld git@github.example.com:platform/agent-config --yes
mind recall --json | jq '.items[].items[] | select(.installed | not)'

The last line lists items that are offered but not installed; fail the job if it prints anything you expected. For a whole-team setup, see Share a setup with your team; for a managed fleet, Managed policy.

Share a setup with your team

Put the list of sources your team uses in one git repo. Everyone melds that repo, gets the same sources at the same versions, and stays in step with mind curate when the list changes.

mind meld git@github.com:acme/agent-setup --yes     # each teammate, once
mind curate                                         # after the list changes

1. Create the setup repo

Start from a machine that already has the sources you want:

mkdir agent-setup && cd agent-setup
mind dump --output mind.toml
git init -q && git add mind.toml && git commit -qm "team agent setup"
git remote add origin git@github.com:acme/agent-setup
git push -u origin main

dump pins each source to the commit you have now and records which items are installed. Edit the file to suit the team. A hand-written one looks like this:

[source]
description = "Acme team agent setup"

[discover]
sources = [
  { source = "acme/agents", install = true },
  { source = "acme/skills", namespace = "acme", follow-branch = "main", install = true },
  { source = "anthropics/skills", install-items = ["skill:pdf", "skill:docx"] },
  { source = "acme/big-library", profiles = ["basic"] },
]

Pin with pin-ref for an exact commit everyone shares, or follow-branch to let teammates pick up new commits with upgrade. See Curate other repos for every entry key.

The setup repo can also ship its own team skills and agents in skills/ and agents/ beside the mind.toml.

2. Onboard a teammate

mind meld git@github.com:acme/agent-setup --yes

This registers every listed source and installs what each entry declares. Without --yes they see the list and confirm. To also link into Codex or Gemini, they add a lobe first (mind config lobes detect); see Use more than one agent.

3. Change the list

Edit mind.toml, commit, and push. Teammates run:

mind curate            # shows the plan, asks once

curate registers new entries and installs what they declare, re-pins entries whose pin changed, upgrades curated sources, and reports entries you removed. It uninstalls removed entries only with --prune. See Follow a curated collection.

In CI

The same repo provisions a CI runner or a dev container:

ssh-keyscan -H github.com >> ~/.ssh/known_hosts   # first SSH connection only
mind meld git@github.com:acme/agent-setup --yes
mind recall --json | jq '.items[].items[] | select(.installed | not)'

The last line lists items offered but not installed; fail the job if anything you expected appears. Hooks are skipped without a terminal. If the sources are yours and their install or build hooks must run, add --dangerously-skip-install-hook-check and --dangerously-skip-build-hook-check. See Scripts and CI.

Enforcing it

A setup repo is a convenience; teammates can still meld other sources. To restrict sources, require pins, lock agent homes, or provision a baseline on every mind sync without anyone melding it, an administrator deploys a managed policy:

[sources]
allow = ["github.com/acme/*", "github.com/anthropics/skills"]
lock = true

[[sources.auto_meld]]
repo = "acme/agent-setup"
tag = "v1.4.0"
install = true

Managed policy

A managed policy file lets an administrator constrain mind machine-wide. It restricts the client to a trusted set of sources and locks related settings, modeled on Claude Code’s managed settings: a file at a fixed system path takes precedence over user configuration and the user cannot override it.

This is a compliance guardrail, not a security sandbox. mind runs with the user’s own privileges, so a user can still place agent files under an agent home by hand without invoking mind; the policy cannot prevent that. What it provides is a policy the user cannot edit (enforced by file permissions), refusal of disallowed operations through mind, and auditability.

The policy file

mind reads a managed policy file on every invocation from a fixed per-OS system path:

OSPath
Linux/etc/mind/policy.toml
macOS/Library/Application Support/mind/policy.toml
Windows%PROGRAMDATA%\mind\policy.toml

Windows-native use is not a supported configuration. For WSL environments, deploy the policy to /etc/mind/policy.toml within the WSL distribution (the Linux path above applies inside WSL).

The path is owned by an administrator and world-readable but not user-writable. It is not relocatable by MIND_HOME or other user environment.

MIND_POLICY_FILE overrides the path only when no file exists at the system path (for tests and non-managed use). When the system file exists it is authoritative and MIND_POLICY_FILE is ignored. The env path is honored only when the file it names exists; a set-but-missing MIND_POLICY_FILE is treated as no policy, not a hard error.

When a policy is in effect it is authoritative over user configuration (~/.mind/config.toml) and the user’s registry: a user cannot widen what the policy restricts.

With no policy file present, mind is unmanaged and behaves exactly as it does without this feature. Every control below is opt-in and absent unless the policy sets it, so the feature is inert by default.

A policy file that does not parse, has unknown keys, or violates an internal rule is a hard error on every command that loads it (fail closed), naming the problem; mind does not silently fall back to unmanaged. The one exception is plain mind recall (read-only, no agent-home access): it does not load the policy and remains usable as an escape hatch during a botched rollout. mind review --policy <file> bypasses the system policy path and can diagnose a broken deployed policy without needing to fix it first. Validate a new policy with mind review --policy before deploying it.

Schema

[sources]
# Allowlist matched against a source's host/owner/repo identity. `*` matches
# within one path segment.
allow = ["github.com/acme/*", "github.example.com/platform/*"]

# Refuse to meld any source whose identity is not in `allow`. Without lock, the
# allowlist is advisory (a non-matching meld warns but proceeds).
lock = true

# Forbid local-path and file:// melds under lock regardless of allow patterns.
# Default is true (local-path melds are evaluated against allow like any source).
allow-local = false

# Every meld must resolve to a tag or ref; floating branches are refused. When
# pinned, every auto_meld entry below must declare a tag or ref.
pinned = true

# Sources mind provisions automatically (melds if not already present), during
# `sync`. `repo` is a repo spec as `meld` accepts (owner/repo, a URL, git@, or a
# path); its derived host/owner/repo identity must satisfy `allow` under lock.
#
# `install = true` (default false): after provisioning, install every item the
# source offers headlessly (equivalent to `learn '<source>#*' --yes`).
# `run-build-hooks = true` (default false): also run item build hooks during the
# headless install; equivalent to --dangerously-skip-build-hook-check. Only
# meaningful when `install = true`.
[[sources.auto_meld]]
repo = "acme/agent-baseline"
tag = "v1.4.0"
install = true

[[sources.auto_meld]]
repo = "https://github.example.com/platform/security-rules"
ref = "9f3a1c2e7b1d0a4c5e6f8a9b0c1d2e3f40516273"
install = true

[lobes]
# Lock the agent homes: `config lobes` edits and $MIND_AGENT_HOMES are refused,
# and the effective homes are exactly `targets`. With lock off, `targets` is a
# base set the user's configured lobes are unioned onto.
lock = true
targets = ["~/.claude"]

[binary]
# Control `mind evolve` (binary self-update). Absent or true: unrestricted.
# false: evolve and evolve --check fail before any network call.
# A version string: evolve resolves to that version offline; --to with a
# different value is refused; an invalid string fails policy parse.
self-update = false     # or true, or "0.14.0"

The scalar [sources] keys precede the [[sources.auto_meld]] tables, per TOML ordering. Source identity is host/owner/repo.

Trusted-source allowlist

[sources].allow is a list of patterns matched against a source’s host/owner/repo identity, where * matches within a path segment, so github.com/acme/* matches every repo under acme. When GHES runs on a non-standard port, the identity includes the port (e.g. github.example.com:8443/owner/repo); patterns must include the port to match, for example github.example.com:8443/platform/*.

Local-path identity. A meld of an absolute path, a .//../ relative path, or a file:// URL gets identity local/<parent-dir>/<repo> where <parent-dir> is the last path component above the repo directory. For example, /srv/mirrors/agent-baseline gets identity local/mirrors/agent-baseline. To allow a mirror directory under lock, add a pattern like local/mirrors/* to allow.

Item-link and alias instances. An item-link instance’s identity carries a #<path> suffix (host/owner/repo#skills/foo), and an @<prefix> alias instance’s identity carries an @<prefix> suffix (host/owner/repo@jk). Allow/lock matching strips both suffixes before comparing, so it always runs against the bare host/owner/repo identity. A link or alias instance therefore inherits its repo’s allow decision, and an allow pattern never needs to name a #path or @prefix suffix.

[sources].lock is the enforcement switch:

  • With lock = true, meld refuses any repo whose identity does not match allow (SourceNotAllowed) before any clone or egress. learn, sync, and upgrade operate only on allow-matching sources. A registered source that is no longer allowed is reported and skipped, not updated or installed from.
  • With lock absent or false, allow is advisory: a non-matching meld is warned about but not refused, and the verbs above are not restricted.

Local-path control (allow-local)

[sources]
allow-local = false   # default: true

allow-local = false forbids all local-path and file:// melds when lock = true, regardless of what allow patterns match. The error names allow-local = false as the reason, distinct from the generic pattern-miss message. With lock absent or false, allow-local has no effect.

allow-local = true (the default) preserves existing behavior: local-path and file:// melds are evaluated against allow like any other source under lock.

When to use allow-local = false. Admitting local paths under a lock delegates control to whoever can write that directory: a user who can clone any repo locally and meld it from a path matching local/*/* bypasses the network restriction. allow-local = false closes that gap for machines where the lock is meant to enforce source origin, not just pattern matching. Combine it with filesystem permissions on any mirror directory that employees can read but not write to.

Require pinned

With [sources].pinned = true, every meld must resolve to a tag or ref pin (--pin <ref>, --pin tag=<name>, or a [source] pin directive that resolves to a tag or ref); a floating branch (--pin branch=<name> or the default branch) is refused (UnpinnedSourceForbidden).

Note that a tag=<name> pin is accepted but is still a moving ref: sync re-points it if the upstream tag is moved. Only --pin <ref> (which freezes a resolved commit) is truly immutable, so pinned = true alone does not guarantee a source is frozen to one commit.

With pinned = true, every [sources].auto_meld entry must declare a tag or ref. A policy whose auto_meld contains an unpinned entry or a follow_branch entry is invalid and is reported by mind review --policy.

Auto-meld (org provisioning)

[sources].auto_meld is a list of tables, each with repo (a repo spec as meld accepts: owner/repo, a URL, git@..., or a path) and an optional pin: tag, ref, or follow_branch. mind provisions these by melding any that are not already melded. It is a base set, not an exclusive one: with lock off the user may meld additional sources beyond it; when locked, only allow-matching sources are permitted.

Every auto_meld entry must satisfy allow when lock is true, matched on the host/owner/repo identity derived from its repo spec. An entry outside the allowlist, or whose repo does not parse, is an invalid policy.

Auto-meld provisioning runs during sync, using each entry’s declared pin. It is idempotent: an entry already melded at the declared pin is left unchanged. Provisioning runs only for a full mind sync with no selector; a scoped mind sync <source> skips it entirely, with no message, so a runbook that only ever runs a scoped sync never converges on the policy’s base set.

Item install. By default, provisioning registers the source only; no items are installed. Set install = true on an entry to also install every item the source offers, headlessly, after successful provisioning. This is equivalent to running mind learn '<source>#*' --yes for that source.

Build hooks for items installed via the install = true pass are skipped by default, as in any non-interactive run. Set run-build-hooks = true on the same entry to run them, equivalent to --dangerously-skip-build-hook-check. Only set this for sources you control or have audited, since build hooks are arbitrary code.

Install hooks follow the same non-TTY path: they are skipped in a non-interactive context. A failed item install is soft-failed (warn, record, continue); other items and sources still process, and sync exits non-zero if any item failed.

Lobe lock

With [lobes].lock = true, the effective agent homes are exactly [lobes].targets; config lobes add / config lobes remove are refused and the user’s lobes (from ~/.mind/config.toml) and MIND_AGENT_HOMES are ignored. With targets absent under a lock, the lock pins the default ~/.claude.

With [lobes].lock absent or false, [lobes].targets is a base set the user extends: the effective agent homes are targets unioned with the user’s configured lobes.

Binary self-update

[binary].self-update controls mind evolve (and its self-update alias). A missing [binary] table or missing key leaves evolve unrestricted.

  • self-update = false: both mind evolve and mind evolve --check fail before any network call with a policy error. The disable applies to --check as well so the binary does not nag about available updates.
  • self-update = "X.Y.Z": evolve resolves to that exact version offline (no api.github.com call), behaving as if --to X.Y.Z were passed. The pin is validated as a dotted-numeric version at policy-parse time (fail closed); a leading v is stripped before validation. If --to V is also passed and V differs from the pin, the command fails with a policy error naming the conflict. evolve --check reports against the pinned version and respects the no-downgrade logic.
  • self-update = true: identical to the absent key; evolve is unrestricted. Exists so a policy can explicitly re-enable updates in a layered deployment.

The pin is an upper bound for evolve, not a fleet version enforcement. When IT distributes a binary newer than the pin (e.g. pin 0.14.0, running binary 0.15.0), evolve and evolve --check print a human-readable warning and exit 0, rather than downgrading:

warning: running 0.15.0 differs from the managed policy pin 0.14.0; the policy
pin is an upper bound and does not downgrade

The --json path emits no warning text on stdout; the structured outcome field (not-downgrading) is the machine-readable hook for fleet skew monitoring. See IT-managed binaries in the enterprise guide for deployment guidance and the trust model for binary updates.

Schema evolution and deployment ordering

A policy file may only use keys the oldest deployed binary understands. Adding a new policy key (such as [binary] in 0.14.0) to a policy file while some fleet machines still run an older binary causes every mind command on those machines to fail with an “unknown field” error (the policy fails closed).

Upgrade binaries before deploying a policy that uses new keys.

Starting with 0.15.0, a policy may declare min-mind-version at the top level:

min-mind-version = "0.15.0"

[sources]
lock = true

mind checks this key before the strict field-validation parse. When a binary older than min-mind-version loads the policy, it reports:

error: invalid managed policy at /etc/mind/policy.toml:
managed policy requires mind >= 0.15.0, running 0.14.0; run `mind evolve`

instead of an opaque “unknown field” error. The gate only helps binaries built after 0.15.0; the ordering constraint still applies to older binaries. An invalid min-mind-version value (not a dotted-numeric string) is a hard parse error. Validate any policy with mind review --policy <path> before deploying it.

Deploying the policy file

The policy file should be owned by root and not writable by non-root users. Correct deployment on Linux:

sudo mkdir -p /etc/mind
sudo cp policy.toml /etc/mind/policy.toml
sudo chown root:root /etc/mind/policy.toml
sudo chmod 644 /etc/mind/policy.toml
sudo chown root:root /etc/mind
sudo chmod 755 /etc/mind

For macOS (/Library/Application Support/mind/):

sudo mkdir -p "/Library/Application Support/mind"
sudo cp policy.toml "/Library/Application Support/mind/policy.toml"
sudo chown root:wheel "/Library/Application Support/mind/policy.toml"
sudo chmod 644 "/Library/Application Support/mind/policy.toml"
sudo chown root:wheel "/Library/Application Support/mind"
sudo chmod 755 "/Library/Application Support/mind"

An Ansible task:

- name: deploy mind policy
  copy:
    src: policy.toml
    dest: /etc/mind/policy.toml
    owner: root
    group: root
    mode: "0644"
- name: secure policy directory
  file:
    path: /etc/mind
    owner: root
    group: root
    mode: "0755"

When mind loads the system policy file, it warns to stderr if the file or its parent directory is group/world-writable or not root-owned. The warning is not a refusal: the policy still loads and enforces, so a misprovisioned fleet stays functional while the misconfiguration is visible. Example:

warning: managed policy /etc/mind/policy.toml is group/world-writable; a local
user could alter enforced policy. chown root and chmod 644.

The check is skipped when the policy was located via $MIND_POLICY_FILE (a path the user controls).

Validation

mind review --policy <path> statically validates a managed policy file without cloning: it parses the TOML, rejects unknown keys, checks that allow patterns are well formed and auto_meld repo specs parse, enforces that every auto_meld entry is pinned when pinned = true, and that every auto_meld entry satisfies allow when locked. It reports hard errors and advisories and exits non-zero on a hard error.

Caveat: install hooks are not gated

The policy does not currently gate install-hook execution. A source’s build command is arbitrary code, and it still runs under the normal hook safety prompt rather than under any policy control. How a policy should govern install hooks is an open research item, not yet specified. See Run setup with hooks for the disclosure and prompt model that applies.

Example

A worked example is at examples/policy.

Restricted networks

mind makes network calls in two places. Source operations (meld, sync, upgrade) are git subprocesses spawned with the parent environment inherited, so your existing git proxy, CA, and credential configuration applies unchanged. mind evolve speaks HTTPS in-process (as of 0.26.0; it previously shelled out to curl or wget), reading proxy settings from the environment and verifying against the machine’s certificate store. install.sh still uses curl or wget, since it runs before mind exists. Where the three differ, each section below says so.

mind sets or strips nothing from the environment (in TUI mode it sets GIT_TERMINAL_PROMPT=0 and wraps GIT_SSH_COMMAND to suppress interactive git prompts; immaterial for non-interactive CLI use), and there is no telemetry or phone-home. This page lists the egress endpoints a firewall allowlist needs and the knobs for proxies, custom CAs, private repos, IT-managed binaries, and air-gapped installs.

For machine-wide policy enforcement (trusted-source allowlist, pinning, lobe lock), see Managed policy; this page shows how those controls fit an enterprise deployment.

Egress endpoints

A corporate allowlist needs the hosts each operation contacts:

  • Melding and syncing sources. git clone / git fetch against whatever hosts you meld (GitHub, GitHub Enterprise, an internal GitLab, an SSH remote, or a local path). No fixed host: it is exactly the source URLs you register.
  • mind evolve (binary self-update).
    • https://api.github.com/repos/jaemk/mind/releases/latest (skipped with --to <v>, see below).
    • https://github.com/jaemk/mind/releases/download/... for the release asset and its SHA256SUMS. These redirect to https://release-assets.githubusercontent.com/...; allowlist that host. *.githubusercontent.com is the most resilient pattern: GitHub has migrated release assets between CDN subdomains. objects.githubusercontent.com may still be needed for older GitHub Enterprise Server deployments.
  • install.sh (install script). The same two github.com / release-assets.githubusercontent.com download URLs, plus https://raw.githubusercontent.com/jaemk/mind/main/resources/install.sh for the script itself, and https://api.github.com/... unless you pin MIND_VERSION.

Proxies

mind reads proxy environment variables in-process for evolve, and the git, curl, and wget subprocesses read them from the parent process. Behavior differs by tool:

  • git honors HTTPS_PROXY, HTTP_PROXY, NO_PROXY, and their lowercase forms, plus its own config:

    git config --global http.proxy http://proxy.corp.example:8080
    

    For NTLM/Kerberos proxy auth, set http.proxyAuthMethod:

    git config --global http.proxyAuthMethod negotiate
    
  • mind evolve honors ALL_PROXY, HTTPS_PROXY, and HTTP_PROXY (first one set wins, in that order), each in either case, plus NO_PROXY / no_proxy. A proxy needing credentials must carry them in the URL (http://user:pass@proxy.corp.example:8080).

  • curl (used by install.sh) intentionally ignores uppercase HTTP_PROXY. Set HTTPS_PROXY or the lowercase form https_proxy.

  • wget (fallback when curl is absent) reads only lowercase forms (https_proxy, http_proxy, no_proxy); uppercase variables are ignored.

The safest approach is to export both cases:

export HTTPS_PROXY=http://proxy.corp.example:8080
export https_proxy=http://proxy.corp.example:8080
export NO_PROXY=localhost,127.0.0.1
export no_proxy=localhost,127.0.0.1

For NTLM or Kerberos proxy authentication during install.sh, curl reads ~/.curlrc (mind exposes no curl argument knob). Add proxy-negotiate there:

proxy = http://proxy.corp.example:8080
proxy-negotiate

~/.curlrc does not apply to evolve, which no longer shells out to curl; it speaks HTTP itself and supports only URL-embedded proxy credentials.

mind neither sets nor unsets any of these, so whatever works for a bare git clone works for every mind git operation.

Custom CA / TLS-intercepting proxy

Behind a proxy that re-signs TLS with a corporate root CA, the CA has to be trusted by whatever makes the connection. Installing it in the system trust store covers everything below at once and is the recommended fix.

  • mind evolve verifies against the system trust store directly: the keychain on macOS, the certificate stores on Windows, and on Linux the OpenSSL-convention bundle, which means SSL_CERT_FILE (a bundle file) and SSL_CERT_DIR (a directory of hashed certs) also work:

    SSL_CERT_FILE=/path/to/corp-ca.pem mind evolve
    

    It does not read CURL_CA_BUNDLE, and it does not shell out to curl or wget as of 0.26.0. A failure here reports invalid peer certificate and points at this setting.

  • git (every meld / sync / upgrade fetch): git config --global http.sslCAInfo /path/to/corp-ca.pem, or the system trust store.

  • install.sh still uses curl or wget, since it runs before mind exists. curl reads CURL_CA_BUNDLE or SSL_CERT_FILE; the wget fallback honors no CA environment variable at all, so behind a custom root CA the CA must be in the system trust store (or configured in wgetrc). That same wget fallback reads only lowercase proxy environment variables (https_proxy, not HTTPS_PROXY); see Proxies above.

Private repos

Private sources work with no mind-specific configuration: it shells out to git, so your credential helpers and SSH agents apply untouched.

  • SSH. Meld the git@host:owner/repo form, or set ssh = true in ~/.mind/config.toml so the owner/repo shorthand clones over SSH (see Configuration). The running ssh-agent supplies keys.
  • HTTPS. A configured git credential helper supplies the token or password git would normally prompt for.
  • Any host. GitHub Enterprise, a full clone URL, the git@ SSH form, and a local path or file:// remote all meld the same way. Source identity is host/owner/repo, so a GHE github.example.com/... source and a github.com/... source never collide. When GHES runs on a non-standard port, the identity includes the port (e.g. github.example.com:8443/owner/repo); allow patterns in a managed policy must include the port to match.

IT-managed binaries (no self-update)

To stop end users from updating the binary, either install mind to a root-owned path (so an in-place evolve cannot write it) or set the managed policy [binary].self-update knob:

[binary]
self-update = false

With self-update = false, both mind evolve and mind evolve --check fail fast with a policy error (self-update is disabled by the managed policy) before any network call. To pin a rollout to a specific version instead of blocking outright, set a version string:

[binary]
self-update = "0.14.0"

mind evolve then resolves to that exact version offline (no api.github.com call), and evolve --to with any other value is refused. Absent or self-update = true leaves evolve unrestricted. See Managed policy for where the policy file lives and how it is enforced.

The pin is an upper bound, not a fleet version enforcement. When IT distributes a binary newer than the pin (e.g. the policy says 0.14.0 but the installed binary is 0.15.0), evolve does not downgrade. Instead it prints a human-readable warning and exits 0:

warning: running 0.15.0 differs from the managed policy pin 0.14.0; the policy
pin is an upper bound and does not downgrade

To detect this skew in a script or monitoring job, run evolve --check --json and inspect the outcome field: a value of not-downgrading means the running binary is above the pin. The exit code is 0 in all cases so cron jobs are not broken.

Trust model for binary updates

SHA256SUMS is fetched over the same HTTPS channel as the release artifact itself. The checksums verify integrity (no corruption or truncation in transit) but not origin: a host-trusted TLS-terminating proxy can substitute both the artifact and its matching checksums without triggering a mismatch. Releases are not code-signed.

Origin is covered separately, and only when a gh binary is on PATH: evolve verifies the downloaded archive’s GitHub build-provenance attestation before extracting it, and aborts the swap when gh reports the artifact does not verify. That check is anchored in a public transparency log rather than in the TLS channel, so an intercepting proxy cannot forge it. Without gh the step is skipped silently and checksums are the only gate.

For environments where egress goes through an intercepting proxy you do not fully trust, either require gh on the hosts that run evolve, or set self-update = false and distribute IT-built binaries through a separately audited channel.

Note that evolve trusts the machine’s certificate store (so that it works behind an intercepting proxy at all). On a host whose store an untrusted party can write to, that party can terminate the TLS connection. The provenance check above is what closes that gap; a policy pin does not.

GH_TOKEN/GITHUB_TOKEN visibility on shared hosts. evolve sends the token as a bearer header on api.github.com requests (see Troubleshooting for why you’d set one). Since 0.26.0 it builds the request in-process, so the token never reaches a subprocess command line and is not visible to other local users through the process table. Before 0.26.0 the wget fallback did expose it there; that path is gone. The token is sent only to the configured API host, never forwarded to the artifact download across a redirect.

Air-gapped and api-blocked installs

When only github.com is allowlisted and api.github.com is blocked, pin the version so install.sh skips the API call:

curl --proto '=https' --tlsv1.2 -fsSL \
  https://raw.githubusercontent.com/jaemk/mind/main/resources/install.sh \
  | MIND_VERSION=0.14.0 sh

install.sh resolves the latest tag from api.github.com only when MIND_VERSION is unset; pinning it downloads the release asset from github.com directly. mind evolve --to <v> is the same: the version bypasses the API.

For fully air-gapped mirrors, meld from a local path or a file:// remote pointing at an internal clone. Source melding accepts any git remote git can reach, including internal hosts.

Air-gapped installs under a managed policy lock. A local-path or file:// meld gets identity local/<parent-dir>/<repo>. Under lock = true with a GHES-scoped allowlist (e.g. github.example.com/platform/*), every local meld is refused because the identity does not match. There are two ways to reconcile this:

  • Add an allow pattern that covers the mirror directory:

    [sources]
    lock = true
    allow = ["github.example.com/platform/*", "local/mirrors/*"]
    

    This allows melds from /srv/mirrors/* (identity local/mirrors/<repo>) while still blocking other local paths. Note that admitting any local-path pattern delegates control to whoever can write that directory: a user who can clone any repo into a matching path bypasses the network restriction. Combine this with filesystem permissions that allow reads but not writes to the mirror directory.

  • Set allow-local = false to block all local-path and file:// melds under lock, regardless of allow patterns:

    [sources]
    lock = true
    allow-local = false
    allow = ["github.example.com/platform/*"]
    

    Use this when the lock is meant to enforce source origin and local-path melds should never be permitted. See Managed policy for details.

Network timeouts

mind evolve reads its request budget from MIND_HTTP_TIMEOUT_SECS (seconds, default 15). It is a whole-request budget, not a connect timeout, and it applies to the release lookup; the artifact download gets 600 seconds, or the knob’s value when that is higher. Raise it for a slow proxy:

MIND_HTTP_TIMEOUT_SECS=60 mind evolve

install.sh uses a fixed 15-second connect timeout and does not read MIND_HTTP_TIMEOUT_SECS (it runs before mind is on PATH).

For slow or blackholed git clones and fetches, use git’s own knobs, for example an abort when throughput stays under a floor:

git config --global http.lowSpeedLimit 1000
git config --global http.lowSpeedTime 30

Fleet setup

  • To restrict sources, require pins, lock agent homes, or provision a baseline source on every machine, deploy a managed policy. Validate it first with mind review --policy <path>.
  • To give a team one shared set of sources without a policy, see Share a setup with your team.
  • For CI jobs, see Scripts and CI.

Publish your first source

A source is a git repo. mind melds any repo with no configuration, so publishing one is: lay the items out by convention and push.

A minimal source

myrepo/
  skills/greet/SKILL.md      # a skill (the whole directory is the item)
  agents/reviewer.md         # optional: an agent
  rules/style.md             # optional: a rule

skills/greet/SKILL.md:

---
name: greet
description: Greet the user by name and summarize the current directory.
---

When asked to greet someone, ...

The description in each item’s frontmatter is what mind probe and mind recall show, so write it for someone deciding whether to install.

Push it, and anyone can install from it:

mind meld owner/myrepo       # finds the items by convention, offers to install

Check what a meld will find before you push, from inside the repo:

mind review                  # validate the source in the current directory
mind meld . --register-only  # register it locally and browse with mind probe

See examples/starter for a minimal repo of this shape, and Source layout and item kinds for every kind and path.

Skills at the repo root

If your skill directories sit at the root with no skills/ container (<repo>/greet/SKILL.md), declare a flat layout:

# mind.toml
[source]
flat-skills = true

It applies to skills only; agents, rules, and tools keep their directories. A consumer can force it for a repo that has not declared it with mind meld owner/repo --flat-skills.

Already a Claude plugin?

A repo with .claude-plugin/plugin.json or .claude-plugin/marketplace.json is meldable as it is, with no mind.toml. See Claude plugins and marketplaces.

What to add when you need it

Everything below is optional. A mind.toml at the repo root enables it; see The mind.toml file for the full schema.

you want tosee
describe the source and set a default namespace[source]
let consumers pick a basic or full setOffer install profiles
have items reference each other safelyNamespaces and references
share a script between several skillsShare helper scripts
ship a data file or template items readShare data files
build or install tooling when the source is meldedRun setup with hooks
make one item pull in anotherDependencies between items
ship only some files, or use a non-standard layout[[items]] and [discover]
list other people’s repos as a collectionCurate other repos
require a minimum mind versionmin-mind-version in [source]

Before publishing, run mind review to catch broken references, malformed configuration, and fragile paths.

Source layout and item kinds

A source is a git repo mind melds. It offers items, discovered by convention or declared in a mind.toml. The convention layout, which works on any repo with no config:

<repo>/
  skills/<name>/SKILL.md     a skill (the whole directory is the item)
  agents/<name>.md           an agent
  rules/<name>.md            a rule
  commands/<name>.md         a slash command
  workflows/<name>.js        a workflow the harness runs
  tools/<name>/              a tool (the whole directory; no anchor file)
  resources/<entry>          a resource: a file or directory other items read
  mind.toml                  optional: metadata, export control, odd layouts

The kinds:

  • skill: a directory with a SKILL.md anchor. Bundled files (a resources/ dir, scripts) ship with it.
  • agent / rule / command: a single markdown file. A command is what the harness offers at the prompt as /<name>.
  • workflow: a single .js file the harness loads from workflows/ and offers to its Workflow tool. See Workflows.
  • tool: a directory of helper scripts or a compiled binary. A tool is store-only: other items reference it, and by default it is not linked into an agent home (a tool can opt in with an explicit link, see Share helper scripts).
  • resource: one file or directory under resources/, named by its full file name (resources/allowlist.json is resource:allowlist.json). Store-only like a tool, and offered only when another item in the source references it with {{resources:name}}. See Share data files.

The command scan is flat: commands/<name>.md, no subdirectories. Claude Code reads a grouped commands/frontend/component.md as /frontend:component, and you get the same command name from a flat file by putting the colon in the name (commands/frontend:component.md), which is also what a mind namespace produces (jk:review links as commands/jk:review.md and runs as /jk:review). Keep a real subdirectory only if you need it for Claude users directly; then declare the item in mind.toml with a full [[items]] entry (kind, name, path, and a link that preserves the nested path). Note that adding any [[items]] or [discover] entry makes the manifest authoritative for the whole repo: convention scanning turns off and only what the manifest lists is offered, so a one-entry manifest silently drops every other item. Declare everything you ship, or reach the nested file another way. See The mind.toml file. Note that Claude Code now treats custom commands as skills: a commands/ file and a skills/<name>/SKILL.md both produce /<name>, and a skill is the better shape for something new. See Commands for what happens when a source ships both.

A group segment must not be a reserved kind word (skill, agent, rule, command, workflow, tool, resource). commands/tool:build.md installs as tool:build, which an item ref reads as kind tool, name build, so mind learn tool:build fails with a not-found error naming build. Spell it command:tool:build, or pick another group name.

A mind.toml is optional enrichment, never a gate. It carries source metadata, a namespace prefix, and (when you need it) explicit [[items]] or [discover] globs for non-standard or monorepo layouts. See examples/.

A repo published for Claude Code’s plugin system needs no changes either: a .claude-plugin/plugin.json or .claude-plugin/marketplace.json is read as a discovery input. The manifest supplies the plugin’s name and metadata; mind maps the plugin root’s conventional skills/, agents/, commands/, and workflows/ directories to items. See Claude plugins and marketplaces.

Where shared helpers belong

A helper used by a single skill lives in that skill’s own directory and is addressed with {{self}}:

skills/review/
  SKILL.md           # ... run {{self}}/resources/pr.py ...
  resources/pr.py

A helper used by more than one item has two good homes. Either:

  • An install hook puts it in a known location. Declare a [[hooks]] install entry to run your install script, which installs the shared tooling wherever you want, and have your items call it there. This suits anything with a build step or a dependency to fetch, and a source onboards its build once.
  • A tool item shares it through the store. Put it once under tools/<name>/ and reference it by token ({{tools:name}}). mind carries it in the store and expands the token at install.
tools/detect/detect          # the shared script, shipped once
skills/a/SKILL.md            # ... {{tools:detect}} ...
skills/b/SKILL.md            # ... {{tools:detect}} ...

Copying a byte-identical helper into several items works too; mind review and mind init-source note it as a duplicate-tooling advisory (informational, not a defect) in case you would rather share it once.

Referencing items and resources

To reference one item from another, mind provides tokens it expands at install. They are useful mainly under a namespace prefix (which renames items) or across multiple agent homes; an unprefixed single-home source can often just use the name or a bundled path.

tokenexpands to
{{ns:name}}a sibling item’s effective name (use in prose, e.g. “hand off to {{ns:dev}}”)
{{self}}the item’s own store directory (its bundled resources)
{{tools:name}}a sibling tool’s entrypoint
{{resources:name}}a sibling resource’s store path (the file, or the directory)
{{path:ref}}a sibling item’s store directory, for a non-entrypoint file ({{path:tool:detect}}/lib.sh)

References resolve within the same source only: ship a tool or resource in the same source as the items that use it. Every token except {{self}} is also a dependency, so mind learn of one skill installs the tools and resources it names.

A token expands in a file when the file has a markdown extension, or the item is of the workflow kind, or the file is on the item’s expand: list. A token in an ordinary bundled script (a resources/pr.py) is left literal by default; list the file in the item’s expand: frontmatter to expand it there, so a script can locate its tooling without a language-specific self-locate; see Share helper scripts.

The workflow kind’s grant cuts both ways for an author. A workflow’s meta.name is not frontmatter – it is a plain JS object field – so nothing stops you from writing it as a literal string instead of a {{ns:}} token; do that under a namespace prefix and the reference silently diverges from the installed effective name, rather than failing loudly, until review/upgrade catches it. The opposite mistake also becomes possible: a literal {{ns:...}}-shaped string elsewhere in the file’s JS, written with no intention of it being a token (a template string, a comment, test fixture data), is now expanded and validated like any other reference, so an unresolvable name there is a hard BadReference install failure rather than inert text. The usual remedy for an unwanted expansion in a bundled non-markdown file – omit the file from expand: – does not apply here, since a workflow’s .js has no frontmatter to carry an expand: key in the first place; the grant is unconditional for the whole file.

Hardcoded paths

mind learn copies an item into the store (~/.mind/store/<kind>/<name>) and symlinks it into each agent home (~/.claude/skills/<name>, agents/<name>.md, rules/<name>.md, commands/<name>.md, workflows/<name>.js). Tools and resources are the exception: they are store-only and, by default, not linked into an agent home.

A path you control is fine: pointing at a location your install hook populates works as long as your hook and your items agree on it. What is fragile is hardcoding mind’s OWN install layout, since that layout shifts under you. mind review classifies those as the advisory hardcoded-path finding:

  • A skill referencing its own resources by an agent-home path (~/.claude/skills/<self>/resources/x) resolves through the skill’s symlink today, but breaks the moment a prefix renames the item (<prefix>:<self>) or a second agent home is configured. {{self}} generalizes it. Fragile, not broken.
  • A reference to a tool item by an agent-home path never resolves: a tool is not linked there. Use {{tools:name}} (or install it elsewhere via a hook).
  • Any reference under a prefix points at the wrong effective name, since a literal path does not track the rename.

A token keeps a leading ~ when the store is under your home, so a Claude settings.json permission glob such as Bash(~/.mind/store/**) matches the expansion.

mind review recognizes these install paths written with ~, $HOME, ${HOME}, or an absolute /home/<user> / /Users/<user> root, and mind review --fix rewrites the ones that map confidently to a token. The finding is advisory, so a deliberate fixed-location-via-install-hook layout is your call.

Workflows

A workflow is a JavaScript file the Claude Code harness loads from ~/.claude/workflows/ and offers to its Workflow tool. It declares export const meta = {...} and a body that orchestrates subagents with agent(), parallel(), pipeline(), phase(), and log().

The harness wraps that body when it runs a workflow, so top-level await, a top-level return, and references to harness-injected globals (phase, agent, parallel, and the rest) are normal there, even though they make the raw file invalid as a standalone ES module: a node --check or bundler run against it rejects it for exactly that reason. mind never runs or validates the body, so this only explains why the shipped example files “don’t validate” as plain JS.

mind treats it as a sixth item kind, discovered and installed like any other:

<repo>/
  workflows/review-changes.js
$ mind learn workflow:review-changes

~/.mind/store/workflow/review-changes     the copy
~/.claude/workflows/review-changes.js     the symlink

The scan is flat and the extension is exactly .js, matching the harness’s own loader: a workflows/nested/deep.js, a .mjs, or a .ts is not an item.

Descriptions come from meta

A .js file has no YAML frontmatter, so the item’s description comes from the meta object instead. probe shows whenToUse beside it, the way the harness’s own workflow list renders the pair; recall reads an installed item’s description from the manifest, where whenToUse is never recorded, and shows the description alone.

export const meta = {
  name: 'review-changes',
  description: 'Review changed files across dimensions',
  whenToUse: 'before opening a PR',
}
$ mind probe --no-tui review
  workflow:review-changes   agents  ab12cd3  Review changed files across dimensions - before opening a PR

A mind.toml [[items]].description still overrides the description, and leaves whenToUse standing.

The reader is minimal, not a JavaScript parser, and it recognizes exactly one opening form: a literal export const meta = { (whitespace or a // / /* */ comment between the tokens is fine), immediately followed by an object literal. A wrapping call (export const meta = Object.freeze({...})), a missing export, a spread expression, or an interpolated/templated value are not recognized: each of these reads as no meta found, the same as a file that has none at all. Where it cannot read a value the item simply lists without a description; it never fails a scan or an install on the shape of a workflow’s code. The harness’s own reader is the authority on what actually loads.

What mind reports but will not enforce

The harness skips a workflow with no readable meta, a missing or empty name or description, or a file over 524288 bytes. review reports each as a workflow-unloadable advisory, and learn and upgrade both warn and install it anyway: an upstream source that introduces the defect between two syncs is the case most likely to surface it at upgrade.

$ mind review ./agents
advisory [workflow-unloadable]: workflow:deploy: the harness will not load this
  workflow: `meta.description` is missing

review also emits a workflow-content advisory for every workflow item, unconditionally: it is a disclosure that mind neither reads nor validates a workflow’s body, not a defect signal, and it fires whether or not the file has any problem at all. It will inflate any CI gate that just counts review advisories.

Nothing here is a gate. mind does not judge item content for any other kind, and a disagreement between the two readers is not a reason to refuse an install. The phrasing above (“the harness will not load this workflow”) describes what mind itself could or could not read, not a guarantee about the harness’s own behavior on that file: mind has no way to run the harness’s loader to check. The size cap is the harness’s, so mind reports an overage rather than enforcing a limit of its own.

The name the harness answers to

The harness keys a workflow by the name in its meta object, not by its file name. An item’s name in mind is its file stem, as for every other file-shaped kind, so the two can diverge.

Write the namespace token in meta.name to keep them in step:

export const meta = {
  name: '{{ns:review-changes}}',
  description: 'Review changed files across dimensions',
}

It expands at install to the effective name (review-changes unprefixed, jk:review-changes under a namespace), which is also the spelling the harness uses for a plugin’s workflow. So the installed file carries a plain string literal that matches the name mind recall reports.

When the two do not match, learn and upgrade both say so and install anyway, review reports it as workflow-name, and mind recall workflow:<name> keeps showing it:

$ mind recall workflow:review-changes
workflow:review-changes
  ...
  harness the harness resolves it as 'deploy', not 'review-changes' -- ...

Two workflows whose meta.name agree are one workflow to the harness. Both install; both are reported, the same four places – review names the finding workflow-name-collision. One shared name is one report, naming the other claimants; past a few it names the first three and counts the rest, so a source with many same-named workflows produces one bounded message rather than one report per file.

A workflow has no frontmatter, so the requires: key (Dependencies between items) is not available to it. Its only dependency channel is a {{ns:}} token in its body: that is the one token family that pulls a referenced sibling into the install closure, the way requires: does for a markdown item. All four token families still expand in a workflow file, not just in meta, since a workflow’s strings are agent prompts:

agent(`follow the checklist in {{path:skill:review}}/SKILL.md`)

A path token renders in the ~ home form there, the reading an agent gets, not the absolute form an expand:-listed script gets. Note the footgun in the example above: {{path:}}, unlike {{ns:}}, forms no dependency edge, so this workflow can install with skill:review absent. Write {{ns:review}} instead of, or alongside, the path token if the workflow should actually depend on that skill.

One consequence: only four openers are live tokens in a workflow’s code – {{ns:, {{self}}, {{tools:, and {{path: – the same families a markdown item expands. Anything else starting with {{ (a template literal’s ${{, or ordinary JS object-literal syntax that happens to contain {{) passes through untouched: it is not scanned for and never read as a token. That is the same rule markdown items have lived under, and it has the same consequence for these four forms: one that resolves to no sibling is a hard install failure (BadReference), not inert text.

review reports a bad reference in a workflow the same as in any markdown item, but review --fix will not rewrite a .js file: the rewrite passes match sibling names as words, and a workflow is code, so an automated rewrite risks corrupting it. Fix a flagged workflow reference by hand.

Plugins

A Claude plugin’s workflows/ directory maps to the kind the same way its commands/ does, on a directly melded .claude-plugin/plugin.json and on each in-repo entry of a marketplace catalog:

$ mind probe --no-tui deploy
  workflow:acme-tools:deploy   acme-tools  ab12cd3  Stage, verify, and cut a release - when a release is ready to go out

The harness names a plugin’s workflow <plugin>:<meta.name>, and a plugin’s items take the plugin name as their default namespace. So the {{ns:}} token expands to exactly that spelling. See examples/marketplace-plugin/workflows/deploy.js.

A manifest’s workflows key points elsewhere on purpose, and mind ignores it as it ignores every other component-path override, so the harness loads from the declared path while mind scans only workflows/, and the two disagree. A plugin that sets the key contributes no workflow items from the declared path, and the skipped-components note says nothing about it. Setting the key is not harmless.

A workflow cannot be installed by item link either: a deep link’s blob form names a single .md file and its tree form names a skill directory, and neither matches a .js workflow file, so mind refuses the link by name. Meld the repo, or learn workflow:<name>, instead.

Lobes

A workflow links into the default Claude lobe and into any lobe whose kinds filter names workflow. A lobe with no filter admits it. The non-Claude harness presets (gemini, codex, universal, windsurf) admit skills only, so they are unaffected. That includes link-project, which resolves to one of those presets (windsurf by default) or, with --subdir, to a skill-only filter: neither links a workflow.

A project lobe still needs nothing special, but it has to be one that admits the kind. The harness reads project workflows from <project>/.claude/workflows/, and the lobe that links one there is the bare form:

$ mind config lobes add ./myproject/.claude

Be aware this registers an unfiltered lobe: with no kinds restriction it also backfills every already-installed skill, agent, rule, and command into ./myproject/.claude/, not just workflows – a bigger effect than “add a project workflow lobe” asks for. No CLI flag narrows this: every preset and every --subdir form is skill-only. To get a workflow-only project lobe, add the entry by hand in ~/.mind/config.toml with a kinds restriction instead:

lobes = ["~/.claude", { path = "./myproject/.claude", kinds = ["workflow"] }]

Offer install profiles

A large source often has a core most people want and extras some people want. Profiles let you name those sets, like cargo features, so a consumer picks full instead of choosing items one by one.

# mind.toml
[profiles]
default = ["basic"]
basic   = ["skill:review", "skill:test", "rule:style"]
full    = ["basic", "agent:*", "skill:deploy"]
all     = ["*"]

With this file:

mind meld owner/repo                     # installs basic (the default)
mind meld owner/repo --profile full      # basic plus full
mind learn owner/repo --profile all      # later: everything

Writing profiles

Each key is a profile name; each value lists its members. A member is one of:

  • kind:name, an exact item: skill:review.
  • kind:glob, every matching item: agent:*, skill:pdf-*.
  • "*", every item the source offers.
  • the name of another profile, which includes its members: basic.

Rules:

  • Names match [a-z0-9][a-z0-9_-]*, at most 64 bytes.
  • Write bare item names. A namespace in effect is applied at install, so skill:review still matches when the source installs as jk:review.
  • A member splits at its first :, so command:frontend:build is the command named frontend:build.
  • A profile may not include itself, directly or through others.
  • An exact member naming an item the source does not offer is an error when the profile is used. A glob matching nothing is not, so you can list agent:* before you ship an agent.
  • Profiles are read from the repo’s root mind.toml only.

[profiles] does not change discovery. It only selects among items the source already offers, so adding it to a convention-layout repo keeps every item available to learn.

The default profile

default is what a plain meld installs. On a terminal, the first meld of a source with default and at least one other profile asks which profiles to enable, with default preselected.

Without a default, a plain meld still offers every item and profiles are opt-in. Choose deliberately: adding default to an existing source changes what new consumers get by default.

How changes reach consumers

Consumers who enabled a profile are tracked against it:

  • An item you add to an enabled profile is offered to them on their next mind upgrade.
  • An item you remove from a profile is reported, not uninstalled.
  • An item a consumer forgot is not offered again while it stays in the profile.
  • Renaming or removing a profile drops it from consumers’ enabled sets with a warning.

So treat profile names as part of your public interface, as you would item names.

Hooks

Hooks can read the enabled profiles from MIND_PROFILES, and an item hook can read the profiles its item was installed for from MIND_ITEM_PROFILES. See Profile variables.

Check them

mind review reports invalid profile names, cycles, and exact members that name no item as hard errors, and globs that match nothing as advisories. See Validate before publishing.

Curators can select your profiles for their users with profiles and default-profile in a [discover].sources entry; see Curate other repos.

Namespaces and references

Two melded sources can each ship an item of the same name (both a review). Without a prefix they would land at the same install path. A prefix namespaces a source so every item from it installs as <prefix>:<name>: the effective name, store path, symlink, and the name mind uses in the ref all carry the prefix. Agents are the one exception, because the harness keys them by frontmatter name rather than by their link path; see Agents are not namespaced. Most sources give their items unique, descriptive names and never need a prefix; this page covers the case where a prefix is needed.

Setting a prefix

Two ways, in precedence order:

  1. Consumer-side: meld --namespace <prefix> (short -n). Stored as the source alias and takes priority over anything the repo declares. meld --namespace '' removes a prefix. (--as <prefix> is a deprecated alias for --namespace.)
  2. Repo-side: [source].prefix in mind.toml.
[source]
prefix = "jk"

With prefix jk, every item in the source installs as jk:<bare-name>. The catalog and the item’s stable identity keep the bare name; the prefix is applied at install time. To change a prefix after items are installed, forget the installed items first (mind forget) and then re-meld with the new prefix.

Agents are not namespaced

A prefix reaches skills, rules, commands, workflows, tools, and resources but not an agent’s harness identity. A skill is keyed by its directory name, so prefixing its directory and link changes the name the harness resolves. An agent is keyed by the name field in its frontmatter, not its filename, so renaming the link to <prefix>:<name> would not change the resolved name. mind therefore links an agent under its bare frontmatter name in each lobe even when the source has a prefix in effect. The prefix still applies to the agent’s store path and manifest key, so its stable identity stays collision-free and a prefix change is still a rename; only the harness-visible link is bare.

Because agents link under their bare name, two melded sources that each ship an agent with the same frontmatter name resolve to the same lobe link. mind detects this instead of silently repointing: learning an agent whose bare name already maps to an installed agent from a different source fails with an AgentCollision error telling you to mind forget the existing one first, and meld surfaces it as an advisory warning. A prefix does not avert the collision, since it does not reach the agent link.

Because a prefix cannot disambiguate agents for you, give a custom agent profile a distinctive frontmatter name so it does not clash with agents from other sources. If a generic name (dev, review, lead) is unavoidable, bake the namespace into the name itself (acme-dev, acme-review), since that is the identity the harness resolves and the only part you control.

A sibling agent’s name is the same bare name with or without a prefix, so a bare prose reference to it resolves either way and the unguarded-reference warning does not fire for it. A {{ns:}} token naming a sibling agent still expands (to the bare name) and is not an error, so tokenizing an agent reference is harmless.

Why {{ns:name}} tokens exist

The Claude harness resolves a skill at runtime by the name in the text, and a prefix changes that name. A plain-prose reference like “run the review skill” breaks once review installs as jk:review. Authors write intra-source references as {{ns:name}} instead, where name is the sibling’s bare name.

At install time, mind expands each token to the referent’s effective name:

source installed as{{ns:review}} expands to
unprefixedreview
--namespace jkjk:review

Expansion runs whether or not a prefix is in effect, so a token-using source installs correctly in both cases. A token whose referent is an agent expands to the bare name in both cases, since agents are not namespaced (Agents are not namespaced).

Minimal example

A source with an agent lead that references a skill review:

<!-- agents/lead.md -->
When implementing, run the {{ns:review}} skill.

Installed unprefixed:

When implementing, run the review skill.

Installed with meld --namespace jk:

When implementing, run the jk:review skill.

A worked multi-item source is at examples/namespacing.

Validation at install time

A token whose name is not a sibling in the same source is a BadReference error at install time, naming the referencing item and the bad referent. Expansion runs in a staging copy during the transactional install, so a bad reference fails before the live install is touched.

Content with no {{ns: tokens is copied unchanged. Non-UTF-8 files are not scanned.

Whitespace inside a token ({{ns: name }}) is trimmed before the sibling lookup. An unterminated token ({{ns: with no closing }}) is left verbatim rather than treated as a reference or an error.

Scope: prose only

{{ns:name}} is a prose name reference. It is misplaced in a fenced code block, an inline code span, adjacent to a path separator (/ or ~), or in a frontmatter structured field like name:, where name-substitution would yield broken code, a broken path, or (under a prefix) a wrong identity. For code and paths, use the path tokens ({{self}}, {{tools:name}}, {{resources:name}}, {{path:ref}}, described in Share helper scripts and Share data files) instead of {{ns:}}.

mind review flags misplaced {{ns:}} tokens; init-source --template does not create them.

Unguarded-reference warning

A source whose items reference siblings in bare prose (no token) breaks at runtime under a prefix. mind does not guess and rewrite prose, because sibling names are often common words. Instead, when melding a source with a prefix in effect, meld scans each item’s text files for sibling names that appear outside any {{ns:}} token and warns for each item where it finds one.

Matching is whole-word (alphanumeric, _, and - are word characters); an item’s own name is not reported against itself. A reference whose referent is a sibling agent is not reported either, since agents keep their bare name under a prefix and so the reference does not break. The warning is advisory and does not fail meld; it does not rewrite anything. No warning is emitted when no prefix is in effect.

Authoring tools

mind init-source --template rewrites bare sibling references to {{ns:}} tokens. mind review . --fix does the same for a working tree. Both are covered in Publish your first source, which also describes how init-source and review surface advisories when a prefixed source’s items reference each other in bare prose.

Share helper scripts

Some agent libraries ship helper scripts or compiled binaries that their skills and agents call at runtime. mind handles this with two mechanisms:

  • The tool kind: a store-only installable for helpers that are shared across items or do not belong inside a single skill directory.
  • Path-reference tokens: {{self}}, {{tools:name}}, and {{path:ref}}, expanded at install to stable store paths.

See the worked example for a complete source you can browse.

The tool kind

A tool is an item kind whose purpose is to be referenced by other items; unlike a skill, by default it is not linked into an agent home and the Claude harness does not discover it directly. A tool can opt in to a link with an explicit link field (below).

Convention discovery: every immediate subdirectory of a tools/ directory under a scan root is a tool. The whole directory is the item; no anchor file is required. A bare tools/detect/ is a tool named detect.

Store path: ~/.mind/store/tool/<effective-name>/. The manifest records the store path with an empty links set. forget removes the store copy; recall reports it as installed.

Optional link: a tool can declare an explicit link field in mind.toml [[items]] to also surface it under an agent home, but the default is store-only.

Namespacing: a prefix gives a tool the effective name <prefix>:<name>, the same as a skill or rule (agents are the exception and keep their bare link name). Stable identity is (source, kind, bare_name), so a prefix change is a rename matched by upgrade/introspect.

When to use a tool

A helper that is bundled with a single skill (a resources/ script) does not need to be a tool. Put it in the skill directory and address it with {{self}} (see source layout). Use the tool kind when:

  • The helper is shared across two or more items.
  • The helper stands alone and is not logically part of any one skill.
  • The helper must be compiled and the build belongs to the item rather than to a source-level install hook.

For a data file items read rather than run (a JSON allowlist, a prompt template), use a resource instead.

mind review treats a byte-identical helper copied into several items as a duplicate-tooling advisory (informational), suggesting it as a tool candidate. Keeping per-item copies is equally valid.

TOOL.md frontmatter

Place a TOOL.md in the tool directory to declare metadata:

---
description: Detect the project type from files in the current directory.
bin: detect.sh
build: make
---
keymeaning
descriptionshown in recall / probe
binthe entrypoint; what {{tools:name}} resolves to
buildper-item build command run in staging at install

The entrypoint resolution order is:

  1. mind.toml [[items]].bin
  2. TOOL.md frontmatter bin:
  3. Convention: a file named after the tool at the tool dir root (e.g. tools/detect/detect) when it is present in the source

A tool that nothing invokes as an executable need not resolve a bin.

A mind.toml [[items]] entry overrides TOOL.md fields. With neither, a tool has no description and no explicit bin or build. For the [[items]] form and [discover].tools globs (which match the tool directory, not an anchor file) see The mind.toml file.

The bin and build fields are valid only on a tool; on any other kind they are a mind.toml schema error.

Build hooks for compiled tooling

The common case is a source-level install hook ([[hooks]] in mind.toml) that builds all tooling once at meld, producing artifacts in the working tree; the tool directory is then copied as its final state at learn. Use a per-item build when you want an isolated, rollback-safe build that runs in staging before the store swap. See Run setup with hooks for the full hook model.


Path-reference tokens

Tokens expand at install to stable store paths. Expansion runs in the same staging pass that expands {{ns:}} (see spec/namespacing.md), so a bad reference fails before the live install is touched. The recorded content hash is of the token (source) form, so drift detection compares source with source.

A token expands in a file when the file has a markdown extension, or the item is of the workflow kind, or the file is on the item’s expand: list. “Markdown extension” means md, markdown, mdown, or mkd, case-insensitively. A token in an ordinary non-markdown bundled file – a script, data – is left exactly as written unless the item is a workflow or the file is expand:-listed; the designed use of a path token is prose, e.g. a skill telling Claude to run {{tools:detect}}. Inner whitespace is trimmed ({{ path:x }} works); an unterminated token (no closing }}) is left verbatim; non-UTF-8 files are not scanned. To expand tokens in a specific script, list it in expand: (below).

References resolve within the same source only: ship a tool in the same source as the items that use it.

Every path token except {{self}} is also a dependency: mind learn of an item that writes {{tools:detect}} installs the detect tool with it, showing the dependency tree and asking first (see Dependencies).

Expanding tokens in a script: expand:

A bundled script that must call a sibling tool or reach an adjacent file can opt that file into expansion, so its tokens resolve at install instead of being left literal. Add an expand: key to the item’s frontmatter, listing item-relative paths (whitespace-separated, the same form as requires:):

---
name: review
description: Review code changes.
expand: resources/pr.py
---

Now resources/pr.py is expanded exactly like markdown. A token in it renders as an absolute store path (/home/you/.mind/store/tool/detect/detect.sh), not the ~ form markdown uses, because the file is read by a program that does not expand a leading ~ (a Python pathlib.Path, say). So a script locates its tooling without a language-specific self-locate:

# resources/pr.py -- {{tools:detect}} is rewritten to an absolute path at install
det = Path("{{tools:detect}}")

The key lives on the item, so declaring it keeps convention discovery on and says nothing about other items (it does not make a source authoritative). An entry naming a file the item does not ship, an absolute path, or a .. segment fails the install (BadReference) and is reported by review as a hard bad-expand finding, so a typo surfaces before a meld. An unresolved token in a listed file is a hard failure, exactly as in markdown.

Cross-source tooling is still out of scope: expand: bakes an install-time path that works because the tool ships in the same source. A script needing a tool from a different source has no token mechanism; ship the tool alongside its callers.

{{self}}

Expands to the item’s own store directory. Available in every kind.

{{self}}/resources/helper.sh

A skill addresses its bundled resources with {{self}} without hardcoding its installed name. Without {{self}}, a prefix rename (e.g. voice to jk:voice) would silently point at the wrong path.

{{tools:name}}

Expands to a sibling tool’s entrypoint: the tool’s store directory joined with its resolved bin. The plural tools: is distinct from the tool: kind-qualifier used in {{path:}}.

{{tools:detect}}

A name that is not a sibling tool, or a tool with no resolvable bin, is a BadReference error.

{{path:ref}}

Expands to a sibling item’s store directory, for reaching non-entrypoint files. ref is a bare sibling name, optionally kind-qualified:

{{path:tool:detect}}/lib.sh
{{path:skill:review}}/resources/pr.py

An unqualified ref that matches items of more than one kind is a BadReference error. A ref matching no sibling is also a BadReference error.

{{tools:name}} is shorthand for {{path:tool:name}}/<bin>: use {{tools:name}} when you want the entrypoint, {{path:tool:name}} when you want the directory.

Tilde rendering

A token renders the store root with a leading ~ when the store is under the user’s home directory (the default ~/.mind/store). This keeps the expansion matchable by a Claude settings.json permission glob:

"Bash(~/.mind/store/**)"

An absolute path would not match that glob. When MIND_HOME points outside home, or when the home directory cannot be determined, the token expands to the absolute path.


Worked example

Source tree (see examples/tooling):

tools/detect/
  TOOL.md          # bin: detect.sh
  detect.sh        # the entrypoint
  lib.sh           # a library sourced by callers

skills/scan/
  SKILL.md
  resources/notes.md

tools/detect/TOOL.md:

---
description: Detect the project type from files in the current directory.
bin: detect.sh
---

skills/scan/SKILL.md (excerpt):

1. Run the shared tool: `{{tools:detect}} .`
2. Source its library: `. {{path:tool:detect}}/lib.sh`
3. Record the result: `{{self}}/resources/notes.md`

mind learn scan --yes installs the skill and, because the skill’s tokens name it, the detect tool. Afterwards:

  • {{tools:detect}} expands to ~/.mind/store/tool/detect/detect.sh – the entrypoint from bin:.
  • {{path:tool:detect}} expands to ~/.mind/store/tool/detect – the store directory, for non-entrypoint files.
  • {{self}} expands to ~/.mind/store/skill/scan – the skill’s own store directory.

All three use tilde syntax so a Bash(~/.mind/store/**) permission rule covers them.

Under a prefix (meld --namespace jk), {{tools:detect}} expands to ~/.mind/store/tool/jk:detect/detect.sh – no edits to the skill needed.

Share data files

A resource is a file or directory a source’s items read at runtime: a JSON allowlist, a prompt template, a reference document, a folder of examples. It installs to the store and is reached by the {{resources:name}} token, the same way a tool is reached by {{tools:name}}. The tooling example ships one.

Layout

Put each resource directly under resources/:

resources/
  allowlist.json        resource:allowlist.json
  reviewer-prompt.md    resource:reviewer-prompt.md
  templates/            resource:templates (the whole directory)
    pr.md
skills/review/
  SKILL.md              ... {{resources:allowlist.json}} ...
  • One entry is one resource. A file keeps its full name, extension included, so allowlist.json and allowlist.yaml are different resources. A directory is one resource holding everything under it.
  • Dot-entries (.gitkeep) and symlinks are skipped.
  • A resource has no metadata file. Nothing is read from its content: a description: line in a markdown resource’s frontmatter is just text.

Referencing a resource

Write {{resources:name}} where an item needs the path:

Check the command against {{resources:allowlist.json}} before running it.
Fill in the template at {{resources:templates}}/pr.md.

After mind learn skill:review, those read:

Check the command against ~/.mind/store/resource/allowlist.json before running it.
Fill in the template at ~/.mind/store/resource/templates/pr.md.

The token follows the same rules as the other path tokens (see Share helper scripts):

  • It expands in markdown files, in a workflow’s .js, and in files listed under expand:.
  • It renders with a leading ~ so a Bash(~/.mind/store/**) permission rule matches.
  • It resolves within the same source only.
  • A name that matches no resource fails the install, and mind review reports it as bad-reference.

{{path:resource:name}} is a longer spelling of the same path.

Which resources are offered

Under convention discovery (no authoritative mind.toml), a resource is offered only when an item in the same source references it, directly or through another offered resource. resources/ is a common directory name, and an entry nothing points at (a project’s install script, images for a README) would have no reader once installed.

mind review lists each entry it skipped:

advisory [unreferenced-resource]: resources/install.sh is not referenced by any item, so it is not offered; an item reaches it by writing {{resources:install.sh}}

If your prose mentions resources/allowlist.json as a plain path, that advisory is the signal to switch it to {{resources:allowlist.json}}.

An authoritative mind.toml offers every resource it declares, referenced or not:

[[items]]
kind = "resource"
name = "notes"
path = "data/notes.txt"
description = "Team notes"

[discover]
resources = { include = ["assets/*"] }   # each match is a file or directory

A resource cannot declare link, bin, or build.

Installing and updating

A resource is a dependency of every item that references it, so learning the item installs it:

$ mind learn skill:review
skill:review  [selected]
  resource:allowlist.json  [dependency]
  resource:templates  [dependency]

Install these 3 items? [y/N]

From there it behaves like any installed item:

  • It is store-only: the store copy lives at ~/.mind/store/resource/<name>, and nothing is linked into an agent home.
  • mind upgrade picks up content changes.
  • mind forget resource:<name> removes it.
  • A namespace prefix applies, for example resource:jk:allowlist.json. The tokens follow the rename.

Resource or tool?

toolresource
shapea directorya file or a directory
token{{tools:name}} (the entrypoint){{resources:name}} (the path)
metadataoptional TOOL.md (description, bin, build)none
offered by conventionalwaysonly when referenced

Use a tool for something an item runs, a resource for something it reads. A file only one skill uses can stay in that skill’s directory and be reached with {{self}}.

Run setup with hooks

A source can declare install hooks in mind.toml: shell commands that build or install the tooling its items rely on. A user can supply or override them with meld --install-hook <cmd>.

The full form is a [[hooks]] array-of-tables. Each entry has a required run field (the shell command), an optional name label shown in the disclosure, an optional bool (default false), and an event field ("install" for meld, "update" for upgrade, "uninstall" for unmeld; default "install"). The legacy [source].install string is shorthand for one required install hook:

# mind.toml in a source repo

[[hooks]]
run = "make build"
name = "build tooling"
event = "install"

[[hooks]]
run = "pip install -r requirements.txt"
name = "python deps"
optional = true
event = "install"

[[hooks]]
run = "make upgrade"
name = "upgrade tooling"
event = "update"

[[hooks]]
run = "make clean"
name = "cleanup"
event = "uninstall"

The safety prompt

Because a hook is arbitrary code, mind discloses the source identity, pin, commit, clone path, and exact command before running anything, and prompts [Y/n/a] with three choices: run the hook (the default, a bare Enter), skip it but still install the source and its items, or abort and install nothing. In a non-TTY context (CI, scripts) the hook is skipped and a note is printed; --dangerously-skip-install-hook-check runs it unattended. Overriding a source’s declared install hook with --install-hook is announced in the prompt, which shows both the declared and the overriding command.

The disclosure also shows a version-control browse URL pinned to the disclosed commit alongside the on-disk clone path, so you can read the exact code that will run either locally or in the forge before approving. The URL is produced only for GitHub-shaped https remotes; a GitLab or Bitbucket host, an SSH remote, or a local/file:// source shows the clone path alone (no correct web link exists for those).

Re-runs

A skipped hook is recorded and re-offered by mind upgrade, so you can run it later without the source needing to advance first. On an upgrade re-run the source is already installed, so abort is treated as skip. upgrade also re-runs the hook when a source advances to a new commit. sync --upgrade accepts --dangerously-skip-install-hook-check so a CI pipeline can run hook re-runs unattended. Without the flag, a non-TTY sync --upgrade skips hook re-runs (the same as upgrade).

Because the default is a re-run, write an install hook to be idempotent: safe to run again on a host it already set up.

Update hooks

An event = "update" hook runs at upgrade INSTEAD of re-running the install hooks. Declare one when a step cannot be made idempotent (a first-run migration, a one-shot registration):

[[hooks]]
run = "make install"          # runs at meld

[[hooks]]
run = "make upgrade"          # runs at upgrade, and `make install` does not
event = "update"

A source that declares no update hook is unaffected: its install hooks re-run as before. An update hook never runs at meld or on a re-meld, is recorded against the commit it ran at exactly as an install hook is (so it is not re-offered until the source advances again), and is disclosed and prompted the same way.

Update hooks supersede install hooks for the source that declares them: while a source declares any update hook, upgrade offers its pending update hooks and does not re-offer that source’s install hooks. An install hook skipped at meld (the default in a non-TTY run such as CI) is therefore not re-offered by a later upgrade. Run it explicitly: mind hooks run <source> --event install --force.

The same applies per item: an item that declares an update hook runs it in place of its install hooks when the item is re-installed over an existing install, which in practice is upgrade’s in-place swap. A learn naming an already-installed item is a no-op and runs no hook; to run an item’s update hooks on demand, use mind hooks run <source>#<item> --event update. A build hook is unaffected and always runs, since it produces the item’s content.

Uninstall hooks

Uninstall hooks (event = "uninstall") run at unmeld, in the source’s clone, before the clone is removed. They use the same safety-prompt model as install hooks: required hooks prompt run / skip / abort-the-unmeld; optional hooks prompt run / skip; a non-TTY unmeld skips them and notes it. unmeld --uninstall-hook <cmd> supplies or overrides the source’s declared uninstall hooks. unmeld --dangerously-skip-hook-check runs them unattended (--dangerously-skip-install-hook-check, the original spelling, is kept as a hidden alias). A required uninstall hook that fails or is aborted leaves the source melded.

Running hooks on demand

Hooks normally run as a step of another verb: install hooks at meld/upgrade, uninstall hooks at unmeld, and item hooks at learn/forget/upgrade. mind hooks run runs them outside those flows, so you can run a hook you earlier skipped, re-run one whose effect was later lost (a deleted build output or side effect), or retry one that failed transiently, without a full re-meld or reinstall. Every hook it runs goes through the same disclosure and consent prompt as an automatic run.

mind hooks run <source>                     # the source's pending install hooks
mind hooks run <source> --force             # every install hook, even already-run ones
mind hooks run <source> --event uninstall   # the source's uninstall hooks
mind hooks run <source>#<item>              # an installed item's install hooks
mind hooks run <source>#<item> --event build  # rebuild the item (transactional)
mind hooks list <source>                    # list hooks in effect, run nothing

<target> is a source filter (the source’s own [[hooks]]) or an owner/repo#item ref (that item’s hooks); there is no ambiguity check, so a filter matching several sources, or a ref matching several items, runs the hook for each in turn. --event selects the lifecycle event (install, update, uninstall, or build); build is valid only for an item target. --event update behaves as --event install does at that target: only pending hooks run, and --force runs them all.

An item-link instance’s own identity is owner/repo#<path> (see Item links), which is spelled the same way as “item <path> in source owner/repo”. When a target matches both a registered source and an installed item, mind refuses it rather than picking one, and names both disambiguated forms. Force either reading:

mind hooks run source:<target>              # the source's own hooks
mind hooks run <source>#<kind>:<name>       # that item's hooks

For a source install run, only pending install hooks run by default (a hook that never ran, was skipped, or whose recorded commit is behind the source’s current commit); --force re-runs every install hook regardless. An item target runs the item’s hooks in place against its installed store copy and requires the item to be installed. --event build rebuilds the item through the normal transactional install path, so a failed rebuild leaves the existing copy untouched.

The --dangerously-skip-install-hook-check and --dangerously-skip-build-hook-check flags apply exactly as they do to the automatic flows: without them a non-TTY run skips the hooks, and a required hook’s failure or abort is a non-zero exit.

mind hooks list <target> reports the hooks in effect for a source and its installed items – each hook’s event, required/optional flag, and command, and for a recorded source install or update hook whether it is pending and the commit it last ran at – without running any. It is the read-only companion to hooks run.

Profile variables

Every hook runs with two environment variables describing the source’s install profiles. Each holds a sorted, comma-separated list, or is empty when there is nothing to report:

  • MIND_PROFILES: the profiles enabled for the hook’s source.
  • MIND_ITEM_PROFILES: for an item hook or build hook, the profiles the item is installed for. Always empty for a source hook.
# skills/deploy/setup.sh, run by `install: sh setup.sh`
case ",$MIND_PROFILES," in
  *,full,*) ./setup-full.sh ;;
  *) ./setup-basic.sh ;;
esac

The values are the state the command leaves behind. During meld, learn <source> --profile, and upgrade, they include the profiles being enabled. During forget <source> --profile <name>, MIND_PROFILES is the set that stays enabled.

A source’s install hooks see the selection the meld is about to apply, so on a terminal meld asks which profiles to enable before it runs them. Enabling or disabling a profile later does not run the source’s install hooks again. Put setup that depends on the profile set in an item hook, which runs when its item is installed or removed.

Visibility

recall --sources marks a source that carries hooks with a count-aware token in its status bracket (e.g. 1 hook or 3 hooks). mind review <repo> lists every declared hook (source and item, whichever event), showing each hook’s command, event, and whether it is required or optional. mind hooks list <target> shows the same detail plus the pending/last-ran state of recorded install and update hooks.

Where an item declares its hooks

An item’s own lifecycle hooks (install / update / uninstall) can be declared in three places. They do not merge: the first one that declares anything supplies the item’s hooks. A tool’s build hook is not one of the three: it is declared only as the [[items]].build field in the source’s root mind.toml or as the build: key in a tool’s TOOL.md frontmatter, and a scoped item mind.toml rejects event = "build".

  1. The source’s root mind.toml, in the item’s [[items]] entry (scalar install/update/uninstall, or an [[items.hooks]] array).
  2. A scoped mind.toml in the item’s own directory, for a skill or a tool. Its only table is [[hooks]], with the same fields as a source’s; any other key is an error.
  3. The item’s frontmatter, in whatever file describes it (SKILL.md, TOOL.md, or an agent’s or rule’s .md): the scalar install:, update:, and uninstall: keys, one required hook each.
# skills/scanner/mind.toml
[[hooks]]
run = "./setup.sh"
name = "Set up"

[[hooks]]
run = "./migrate.sh"
event = "update"
<!-- skills/scanner/SKILL.md -->
---
description: scanner
install: ./setup.sh
update: ./migrate.sh
---

An item hook runs in the item’s store directory when the item is directory-backed (a skill or a tool), so a relative ./setup.sh resolves against the item’s own files. A single-file kind (an agent, a rule, or a command) has no directory of its own: its hook runs in the shared kind directory (~/.mind/store/agent/, ~/.mind/store/rule/, ~/.mind/store/command/), and the item ships no scripts, so its hook must be a command on PATH or an absolute path. A relative script there fails the install with a HookFailed hard stop that rolls the install back.

An item manifest is part of the item’s content: it is copied into the store and hashed like any other file, so editing it upstream is drift upgrade picks up. No agent harness reads it; only mind does. These keys are mind’s, not the harness’s. An agent harness reads the same frontmatter block, and mind does not control how a given harness treats keys it does not define, so use the scoped item mind.toml if you would rather keep the item’s frontmatter to what the harness itself defines.

[source].install is deprecated in favor of the [[hooks]] form. See The mind.toml file for the schema and spec/install-hooks.md for the full behavior.

Dependencies between items

Items in a source can reference siblings via {{ns:name}} tokens (see Namespaces and references) and path tokens like {{tools:name}}. Those tokens are dependency edges. When you install a partial selection, mind follows those edges to the full transitive closure so you get a working set, not a dangling one.

What counts as a dependency

A dependency is an intra-source reference. There are two ways to declare one, and the closure that learn installs is their union.

{{ns:name}} tokens – a token appearing in a file where it expands names a sibling as a dependency. A token expands in a file when the file has a markdown extension, or the item is of the workflow kind, or the file is on the item’s expand: list. In practice that means the whole skill directory’s .md/.markdown/.mdown/.mkd files, or the agent/rule file, as before, plus a workflow’s own .js file: a {{ns:}} token there forms a dependency edge the same as one in markdown does, because the dependency scan reads exactly the files install expands, and install expands a workflow’s .js. A token in an ordinary non-markdown bundled file (a shell script, a data file) still forms no dependency edge. This is the inline form: the reference lives in the prose (or, for a workflow, the meta.name field or body) and is also rewritten to the effective name on install. See Namespaces and references for token expansion rules.

requires: frontmatter key – a top-level scalar in the item’s frontmatter (SKILL.md for a skill, the .md for an agent or rule), listing whitespace-separated intra-source refs:

---
description: My skill
requires: skill:plan agent:test
---

This is the pure-metadata form: it adds a dependency edge without any prose reference. Use it when an item needs a sibling at runtime but does not mention it in its text.

Each entry is either a qualified ref (kind:name, e.g. skill:plan) or a bare name. A bare name matches across kinds; if more than one sibling shares that bare name across different kinds, the ref is ambiguous and must be qualified. A source-qualified ref (owner/repo#name) is rejected – requires is intra-source only. An entry that resolves to no sibling (typo, unknown item, ambiguous bare name, or source-qualified ref) is a hard error at install.

Path tokens: {{tools:name}}, {{resources:name}}, and {{path:kind:name}} are dependencies on the tool, resource, or item they name, under the same expansion rule as {{ns:}}. A bare {{path:name}} depends on the one sibling with that name; if several kinds share it, install rejects the token as ambiguous. {{self}} names the item itself and is not a dependency. See Share helper scripts and Share data files.

Dependencies never cross sources; tokens and requires: entries are always resolved within the one source.

One consequence: an item link offers a single item (a skill, or – for a file link – an agent, rule, or command), so a reference to a sibling has nothing to resolve against. A requires: entry there is dropped and the item still installs; the drop is warned about and recorded, so mind recall <item> and mind introspect show it afterwards. A token ({{ns:}}, {{tools:}}, {{path:}}) is an error, since it is rewritten into the item’s text. Both point at the same kind-qualified remedy: mind unmeld <identity> --yes && mind meld <repo-url> --learn '<kind>:<name>' --yes (adding --add-root <dir> before --learn when the repo’s own inventory does not declare the item). For a file link whose parent directory is not its kind’s container, no whole-repo meld can reach the item at all, so no command is printed; see Item links for that case and the full command for every other form.

Partial learn pulls in the closure

When learn selects every item in a source (e.g. learn owner/repo#*), resolution is a no-op: every referent installs anyway. The work is for a partial selection.

Example: a source ships skill:review, agent:dev, and agent:test. review references dev via {{ns:dev}}, and dev references test via {{ns:test}}.

mind learn skill:review

mind resolves the closure – review -> dev -> test – and installs all three, dependencies first.

What you see before install

When the closure adds items beyond your explicit selection, mind prints the dependency tree and prompts before changing anything:

skill:review  [selected]
  agent:dev   [dependency]
    agent:test  [dependency]

Install these 3 items? [y/N]

Nodes already in the manifest are marked [installed] and are not re-installed. Cycles are shown as marked back-edges rather than expanded again.

--yes (or answering y) confirms without prompting. Under --json there is no prompt at all: --yes is mandatory whenever the closure adds items beyond the explicit selection, and learn --json without it refuses with confirmation-required instead of installing unprompted. See Commands for the full flag reference.

--dry-run preview

--dry-run renders the same dependency tree and lists the full closure that would be installed, then exits without installing anything:

mind learn --dry-run skill:review

In the terminal UI

Choosing to install an available item in mind probe shows the same dependency tree in the confirm step before applying, with the same selected / dependency / already-installed distinction. Confirming installs the whole closure in dependency-first order; declining installs nothing.

forget does not cascade

forget removes exactly the one named item. It does not automatically remove its dependencies, because a dependency may be shared by another installed item. Uninstall is always a per-item operation.

Claude plugins and marketplaces

Claude Code ships its own plugin system: a repo can carry a .claude-plugin/plugin.json (a single plugin) or a .claude-plugin/marketplace.json (a catalog of plugins). mind reads these manifests as a discovery input, so a repo published for Claude’s plugin system melds like any other source, with no re-packaging by its author.

mind meld owner/claude-plugin-repo   # a repo with .claude-plugin/plugin.json
mind probe                           # its skills, agents, commands, and workflows show up as items
mind learn <plugin-name>:<skill>     # install one, same as any source

A marketplace is a source, not a sink

mind treats a plugin manifest strictly as a way to discover items. Discovered items go to ~/.mind/store and are symlinked into each lobe exactly like a convention- or mind.toml-discovered item. mind never writes into Claude’s plugin cache (~/.claude/plugins/cache/...), its settings.json enabledPlugins, or known_marketplaces.json. Consuming a marketplace does not turn mind into a plugin publisher: it does not emit .claude-plugin/ manifests or register anything with Claude.

This keeps everything mind gives a source: namespacing, {{ns:}} reference expansion, the broader rule/tool taxonomy, and the source-hash drift model that sync, upgrade, and introspect use.

A single plugin (plugin.json)

A plugin is a directory with .claude-plugin/plugin.json whose component directories sit at the plugin root. Claude’s layout for skills, agents, commands, and workflows is byte-for-byte mind’s convention layout, so the mapping is direct:

Plugin componentmind item
skills/<name>/SKILL.mda skill
agents/<name>.mdan agent
commands/<name>.mda command
workflows/<name>.jsa workflow
hooks/, .mcp.json, LSP, monitors, themes, output-stylesnot installed (no mind equivalent)

A plugin has no rules or tools component, so nothing maps to those kinds.

The projection is lossy and says so, with one gap: when a plugin declares components mind cannot represent (hooks/, .mcp.json, and the rest of the table above), meld prints a count of what it skipped, for example 2 hooks, 1 mcp server not installed (no mind equivalent). That count only covers components found by the conventional directory scan, though. A plugin.json may override a component’s path with a key like "workflows": "./somewhere" (or experimental.workflows, and likewise for the other components); mind does not read that override key at all, so a plugin using one contributes zero items from the declared path and produces no skipped-count entry for it either – the projection silently omits the whole component, with no visible signal. The escape hatch is meld --add-root <dir> naming the declared parent directory, which convention-scans it directly (see Installing items the manifest does not list).

Naming: the plugin name is the default prefix

A plugin’s name becomes the default namespace prefix for its items, mirroring Claude’s mandatory plugin:skill naming. So a plugin named acme-tools shipping a greet skill installs as acme-tools:greet. Unlike the native system the prefix stays optional and consumer-overridable: meld --namespace <p> overrides it and meld --namespace '' clears it.

The prefix does not reach agents. An agent links under its bare frontmatter name regardless of the prefix (the harness keys agents by that name, not by the plugin scope), so two sources shipping a same-named agent collide and mind surfaces the collision rather than silently resolving it. See namespacing.

A plugin’s version and description are read as metadata (the description overrides frontmatter; the version is recorded for display). Drift and upgrade still compare source content, not the declared version, so bumping version alone is not what triggers an upgrade.

A marketplace catalog (marketplace.json)

A marketplace.json lists several plugins, each either in-repo (a path inside the catalog repo) or an external git source. This is the native analog of a mind curated super-source, so it reuses that machinery: each listed plugin becomes a sub-source.

  • An in-repo plugin is a scan root inside the catalog repo, read per the single-plugin rules above. Like a normal source’s own items, in-repo plugins are offered for install on meld.
  • An external plugin is a nested git source, melded and registered like any [discover].sources entry, tracking its own upstream commit. Like other nested sources it is registered and left available; meld --recursive extends install to the whole chain.

Each entry’s name namespaces that plugin’s items, and an entry may carry a version read as metadata. Where a marketplace entry and an in-repo plugin.json supply the same field, the entry wins (mirroring Claude’s non-strict mode, where the marketplace overrides the plugin manifest).

The post-meld probe hint and the sync re-walk apply to a marketplace exactly as they do to any super-source. A melded catalog is a curator for mind curate too: its listed entries count toward what is still listed (so one is never proposed for unlist while the catalog still names it), and a registered one joins the upgrade sweep once you own it. A catalog entry declares no install directive of its own, though, so curate never proposes install for a newly published plugin the way it does for a [discover].sources entry with install = true – sync still picks that up as a register-only entry, same as before.

Installing items the manifest does not list

A marketplace.json often lists a curated subset of what the repo actually contains. Two ways to reach the rest:

Compose extra scan roots: meld --add-root <dir> (repeatable). The manifest keeps defining its items and each added root is convention-scanned in addition, so unlisted skills under those directories become installable:

mind meld owner/marketplace-repo --add-root community --add-root experimental

Each added root is scanned in both skill layouts at once (the skills/ container and flat bare-name directories), plus agents/, rules/, commands/, workflows/, and tools/. An item both the manifest and an added root contribute is offered once, under the manifest’s namespace. Unlike --root (which replaces the scan layout and suppresses the manifest), --add-root never suppresses anything; it also composes with an authoritative mind.toml. The roots persist on the source and apply to later scans and sync.

Link one item directly. Paste the skill’s tree/blob URL (or, for an agent, rule, or command file, its blob URL); the manifest does not gate it because you named the exact path:

mind learn https://github.com/owner/marketplace-repo/tree/main/community/foo

See item links.

Precedence: when a plugin manifest is used

The plugin-manifest layer slots alongside the other discovery layers. A source’s own mind.toml still wins:

  • A mind.toml that declares [[items]] or [discover] kind globs is authoritative and suppresses the plugin-manifest layer, the same way it suppresses convention scanning. meld prints a note that a .claude-plugin/ manifest was found and ignored.
  • A [source]-only mind.toml (metadata, no item globs) composes: its metadata is read and the plugin manifest supplies the items.
  • With no authoritative mind.toml, a plugin manifest (if present) is authoritative for the items it declares, and convention scanning is skipped for the paths it covers.

Safety

Manifests are attacker-controlled content shipped by a melded repo and are held to the same guards as mind.toml:

  • They are parsed strictly; a malformed manifest is rejected rather than partially trusted.
  • Every path a manifest contributes (a plugin root, an in-repo plugin path, a component path, a link target) is validated by the same safe-relative-path rule as [[items]] path/link: an absolute path, a leading ~, a .. component, or a NUL byte is rejected. A melded marketplace cannot read files outside its clone or place a symlink outside a lobe.
  • An external plugin source is pinned and validated through the same path as a [discover].sources spec.
  • Names and descriptions taken from a manifest have ANSI escapes and control characters stripped before display, so catalog-controlled text cannot inject terminal sequences.

Provenance

recall --sources and the probe source view label a source whose items came from a plugin manifest with its origin (claude-plugin or claude-marketplace), so you can tell a native-plugin source from a convention or mind.toml source at a glance.

Authoring a plugin or marketplace

You don’t need a mind.toml to make a Claude plugin repo meldable: the manifest layer is enough on its own, and everything below is Claude’s own format, not something mind adds.

A single plugin. Put .claude-plugin/plugin.json at the repo root (or at a scan root) with name, and optionally version and description:

{
  "name": "acme-tools",
  "version": "1.0.0",
  "description": "Acme developer tools plugin"
}

Lay out skills/<name>/SKILL.md, agents/<name>.md, commands/<name>.md, and workflows/<name>.js next to it, same as any mind source (Source layout). hooks/, .mcp.json, and the other Claude-only component kinds are fine to keep in the repo for Claude users; mind just skips them (with a printed count) rather than erroring, so one repo layout serves both consumers. There is no plugin-level place for a rule or a tool - if you want mind users to get those, add a mind.toml with [[items]] for them; it composes with the plugin manifest as long as it stays metadata-only or covers different items (Precedence).

Pick name deliberately: it becomes every consumer’s default namespace prefix (acme-tools:greet), so treat it like a package name - short, unique enough to avoid colliding with other plugins someone might meld, and stable across releases (renaming it renames every item for existing consumers).

A marketplace catalog. Put .claude-plugin/marketplace.json at the repo root listing each plugin by name and source (a repo-relative path for an in-repo plugin, or a git URL for an external one), plus optional version / description:

{
  "name": "Acme Marketplace",
  "plugins": [
    { "name": "alpha", "source": "./plugins/alpha", "description": "Alpha plugin" },
    { "name": "beta", "source": "./plugins/beta", "description": "Beta plugin" }
  ]
}

Each in-repo entry’s source must resolve inside the repo (no .., no absolute path, no ~) or mind rejects it (Safety). An entry’s version/description override the same fields in that plugin’s own plugin.json if it has one, so a curator can relabel a plugin without editing its manifest.

Validate either shape the same way you’d validate any mind source: run mind meld <path-or-repo> against a local clone and mind probe to confirm the items and namespacing came out as expected.

Try it

Two runnable fixtures live in the repo:

  • examples/marketplace-plugin
    • a single plugin: one skill (namespaced by the plugin name), one agent (bare), a commands/ slash command, a workflows/ workflow, and an unsupported hooks/ that reports a skipped count.
  • examples/marketplace-catalog
    • a catalog listing two in-repo plugins, each with its own name and items.

The normative behavior is spec/marketplace.md.

Curate other repos

A source can list other repos instead of, or as well as, shipping its own items. Melding it registers every listed repo, optionally installs from them, and mind curate keeps consumers in step when you change the list. Use it for a team’s standard set, a themed collection, or your own dotfiles-style setup.

# mind.toml
[source]
description = "Acme's agent toolkit"

[discover]
sources = [
  { source = "acme/agents" },                               # registered, nothing installed
  { source = "acme/skills", namespace = "acme", install = true },
  { source = "anthropics/skills", install-items = ["skill:pdf", "skill:docx"] },
  { source = "acme/big-library", profiles = ["basic"] },
]

mind dump writes this shape from an existing setup, so you can start from what you already have installed; see Back up and reproduce your setup.

Entry keys

keymeaning
sourceany repo meld accepts: owner/repo, a URL, a local path, or an item link
namespaceinstall that source’s items under a namespace, as meld --namespace does
installtrue offers all its items for install at meld (default false)
install-itemsoffer only these kind:name items (literal names, no globs)
profilesenable these install profiles of that source
default-profilefalse leaves that source’s default profile out
follow-branch, pin-tag, pin-refpin that source (at most one)
kindfor an item link to a .md file: agent, rule, or command

install = true cannot be combined with install-items or profiles. install-items with profiles installs the union. An install = true entry whose source declares a default profile installs that profile, as a plain meld would.

The same list can be written as tables:

[[discover.sources]]
source = "acme/skills"
namespace = "acme"
install = true

Ship your own items too

A bare [discover].sources does not turn off convention discovery, so the repo can ship its own skills/ and agents/ alongside the list. Only [[items]] or [discover] kind globs make the file authoritative; see The mind.toml file.

Configure a repo that has no mind.toml

An entry can supply settings for a listed repo that ships none of its own:

keymeaning
rootsscan these directories instead of the repo root
add-rootsscan these directories in addition
flat-skillsskills sit at the root with no skills/ directory
[[discover.sources.hooks]]hooks to run for that repo, same shape as [[hooks]]
[[discover.sources]]
source = "owner/unonboarded"
follow-branch = "main"
roots = ["packages/agents"]

[[discover.sources.hooks]]
run = "make build"
name = "build tooling"

These four apply only when the listed repo has no mind.toml at all. If it has one, even one that sets none of them, its own file wins and mind warns that yours were ignored. This keeps a curator from reaching past a repo’s own export control. The pin keys, namespace, install, install-items, and profiles always apply.

How changes reach consumers

When you push a change to the list, consumers run mind curate to apply it: new entries are registered and installed as declared, changed pins re-pin, curated sources upgrade, and dropped entries are reported (removed only with --prune). sync alone registers new entries but installs nothing from them. See Follow a curated collection.

A Claude marketplace is a curator too

A .claude-plugin/marketplace.json catalog is read the same way, and a repo can be both a marketplace and a mind curator. See Claude plugins and marketplaces.

Examples: super-source, marketplace-curator.

Validate before publishing

Two commands help a maintainer: mind review checks a source the way a consumer’s meld would see it, and mind init-source scaffolds a mind.toml and reports how the items reference each other.

mind review                  # check the source in the current directory
mind init-source             # scaffold mind.toml and report references

review

mind review                      # the current directory
mind review ./path/to/repo       # a local path
mind review owner/repo           # a melded source, or a repo to shallow-clone
mind review . --namespace jk     # check it as it would install under a namespace
mind review . --fix              # rewrite what it can fix, in place

review changes nothing except under --fix. A path to an existing directory is read as that directory, unless it first matches a melded source’s name.

Findings are hard (non-zero exit) or advisory (exit 0):

Hard

  • a malformed mind.toml, or an unknown item kind
  • a min-mind-version newer than the running mind
  • an unresolved {{ns:}}, {{self}}, {{tools:}}, {{resources:}}, or {{path:}} reference
  • an expand: entry naming a file the item does not ship
  • an invalid install profile, a profile cycle, or a profile member naming no item

Advisory

  • a missing description
  • a hardcoded install path (hardcoded-path); see Source layout
  • a sibling tool named in prose without a token
  • a file or directory under resources/ that no item references, so it is not offered (unreferenced-resource); see Share data files
  • a {{ns:}} token in a code block or path, where it does not belong
  • an item that names a sibling in plain text when a namespace is in effect
  • the same helper script copied into several items (duplicate-tooling)
  • a deprecated [source].install field
  • each declared source or item hook, so a consumer sees the source runs code
  • workflow defects the harness would reject (see Workflows)

--fix works on a local working tree only. It replaces recognized hardcoded paths with tokens, unwraps misplaced {{ns:}} tokens, and wraps bare sibling names in {{ns:}}. It leaves structural findings such as duplicate-tooling to you, and leaves workflow .js files alone.

review --json returns the findings as one document; see Scripts and CI. Run it in your source’s CI to catch breakage before consumers do:

mind review . --json

review --policy <path> validates a managed policy file instead; see Managed policy.

init-source

mind init-source                 # report and scaffold
mind init-source path/to/repo
mind init-source --template      # also rewrite bare sibling names to {{ns:}} tokens
mind init-source --marketplace   # also generate .claude-plugin/marketplace.json
mind init-source --flat-skills   # skills sit at the repo root
mind init-source -N acme         # set the namespace in the scaffold

Each run:

  1. Discovers the items exactly as a meld would, so what it reports is what a consumer gets.
  2. Writes a mind.toml if there is none, with a [source] table and a description placeholder. An existing mind.toml is never overwritten.
  3. Reports each item’s references to its siblings: those already written as {{ns:name}} tokens, and bare mentions in prose. Bare mentions are reported as unguarded-reference advisories only when a namespace is in effect, since without one they resolve as written.
  4. Reports duplicate-tooling for a helper file copied byte-for-byte into two or more items.

--template rewrites each bare whole-word sibling name in item text to its {{ns:name}} token. It skips text already in a token, code blocks, inline code, paths, and frontmatter fields, and leaves workflow .js files alone. Sibling names are often ordinary words, so review the result with git diff before committing.

init-source makes no network calls and touches no store or agent home. Without --template it only creates an absent mind.toml; with it, it edits item files in the target repo.

Commands

Every verb, its flags, and where to read more. Run mind <verb> --help for the same text in the terminal. For the vocabulary, see Core concepts.

verbdoes
meldregister a source repo and offer to install its items
unmelddrop a source and uninstall its items
learninstall items
forgetuninstall items
recalllist what is installed, or one item’s details
probebrowse and search what sources offer
syncfetch sources
upgradebring installed items up to date
curateapply what your curators declare
introspectreport drift and broken links
hooksrun or list hooks on demand
configview config and manage agent homes
link-projectadd a project’s agent home
absorbmove a hand-placed item into a source you own
dumpwrite your setup as a mind.toml
reviewvalidate a source or a policy file
init-sourcescaffold a source’s mind.toml
evolveupdate the mind binary
completions, manshell completions and the man page

Global flags

Accepted before or after any verb.

flageffect
--jsonone JSON document on stdout; see Scripts and CI
-y, --yesanswer every confirmation
--asciiplain ASCII output, no color or Unicode
-v, --verboseextra advisory output, such as unguarded-reference warnings
--max-metadata-size <SIZE>metadata size cap for this run; see Configuration

Selecting items and sources

Verbs take either a ref, which must match exactly one thing, or a filter, which may match several.

patternselects
reviewthe item named review
skill:reviewthe skill named review
owner/repo#reviewreview from one source
'*'every item
'skill:*'every skill
'owner/repo#*'every item of one source
'review*'items whose name starts with review

Quote globs so the shell does not expand them. A glob matches the installed (effective) name.

  • Refs error rather than guess: unmeld <name> must match one source, and a bare item name given to learn, forget, or upgrade that exists in two kinds (skill:greet and agent:greet) must be qualified.
  • Filters act on every match: sync [source], --source <filter> on recall and probe, hooks run <target>, any glob, and the owner/repo# part of an upgrade argument.
  • A source filter is an exact name, a trailing part (repo, owner/repo), or a glob over the full host/owner/repo identity.

With no match: sync errors, upgrade reports up to date, --source lists nothing, and meld --learn errors with the source left melded.

meld

mind meld [REPO] [OPTIONS]

Register a source and offer to install its items. REPO defaults to . and may be owner/repo, an https:// or git@ URL, a local path, a file:// URL, or an item link. Melding a source you already have installs any missing items, or shows each item’s state. Alias add.

flageffect
--register-onlyregister without offering to install
--learn <NAME|GLOB>install only matching items (repeatable); see Choose what a source installs
--profile <NAME>enable an install profile in addition to default (repeatable or comma list)
--no-default-profileleave the default profile out
--all-profilesenable every declared profile
--pin <HEAD|REF|branch=NAME|tag=NAME>set the version tracked; also re-pins a melded source; see Pin a source to a version
-N, --namespace <PREFIX>install items as <PREFIX>:<name>; a different namespace registers a separate instance; see Fix name collisions
-r, --recursiveoffer the items of every source a curator lists
-f, --forceoverwrite link targets mind did not create; on a re-meld, also re-offer every install hook
--localinstall into the current project’s lobe only
--root <DIR>scan these directories instead of the source’s own roots (repeatable)
--add-root <DIR>scan these directories in addition to the source’s own discovery (repeatable)
--flat-skillsskills are directories at the root, with no skills/ container
--install-hook <CMD>supply or replace the source’s install and update hooks
--kind <agent|rule|command>the kind of a file named by an item link
--dangerously-skip-install-hook-checkrun install hooks without asking
--dangerously-skip-build-hook-checkrun item build hooks without asking

--root, --add-root, --flat-skills, and --install-hook apply only at the meld that first registers a source; a re-meld notes them as ignored. Without a terminal and without --yes, meld registers the source and installs nothing.

mind meld owner/repo
mind meld owner/repo --learn review --yes
mind meld owner/repo --profile full --pin branch=stable
mind meld https://github.com/owner/repo --namespace acme
mind meld . --register-only

unmeld

mind unmeld <NAME> [OPTIONS]

Uninstall every item a source installed, then drop it. NAME must match one source unless it is a glob. Use the full identity for one instance: github.com/acme/skills@jk, github.com/acme/skills#skills/foo. Alias remove, rm.

flageffect
--keep-itemsdrop the source, leave its items installed
--uninstall-hook <CMD>supply or replace the source’s uninstall hooks
--dangerously-skip-hook-checkrun uninstall hooks without asking
mind unmeld acme/agents
mind unmeld acme/agents --keep-items
mind unmeld '*agents' --yes

See Remove items and sources.

learn

mind learn <ITEM> [OPTIONS]

Install items into every lobe. ITEM is a ref, a glob, a source name with --all or --profile, or an item link URL. Installing part of a source also installs the siblings it depends on, after showing the tree. Alias install.

flageffect
--allevery item of the named source
--profile <NAME>install and enable that profile of a melded source (repeatable or comma list)
--all-profilesinstall and enable every declared profile
-n, --dry-runshow what would install, change nothing
-f, --forceoverwrite a link target mind did not create
--localinstall into the current project’s lobe only
--pinfor an item link, freeze the URL’s branch at its current commit (takes no value)
--kind <agent|rule|command>the kind of a file named by an item link
--dangerously-skip-install-hook-checkrun an item’s install hook without asking
--dangerously-skip-build-hook-checkrun an item’s build hook without asking

--no-default-profile is accepted for symmetry with meld and changes nothing. Installing an item by exact name marks it as asked for, so disabling a profile later keeps it.

mind learn review
mind learn 'skill:*'
mind learn owner/repo --all
mind learn owner/repo --profile full
mind learn https://github.com/owner/repo/tree/main/skills/foo

See Find and install items.

forget

mind forget [ITEM] [OPTIONS]

Uninstall items: the store copy and every link. Alias uninstall, unlearn.

flageffect
--profile <NAME>disable a profile of the source named by ITEM, forgetting items installed only for it
--unmanagedact on unmanaged lobe items only; with no ITEM, all of them
-f, --forceskip the confirmation when other items depend on the one removed
--dangerously-skip-hook-checkrun uninstall hooks without asking

A glob matching several items asks once; --yes skips that and the dependents question.

mind forget review
mind forget 'skill:*' --yes
mind forget owner/repo --profile full
mind forget --unmanaged

recall

mind recall [ITEM] [OPTIONS]

List each source with its items, marked installed, available, or out of date, followed by unmanaged items. With ITEM, show that item’s details. Alias list, status.

flageffect
--sourceslist sources instead, with their hooks and profiles
--kind <KIND>only items of this kind
--source <FILTER>only items from matching sources
--treeinstalled items as a dependency forest (scoped to ITEM if given)

--kind and --source apply to the item listing only.

probe

mind probe [QUERY] [OPTIONS]

Browse and search every melded source’s items. On a terminal it opens the terminal UI; with --no-tui, --json, or piped output it prints a listing. QUERY is a case-insensitive substring of names and descriptions. Alias search.

flageffect
--kind <KIND>only items of this kind
--source <FILTER>only items from matching sources
--no-tuiprint the listing instead of opening the UI

sync

mind sync [SOURCE] [OPTIONS]

Fetch every source, or those matching SOURCE, and refresh their catalogs. Installed items do not change. Alias update.

flageffect
--upgradethen run upgrade over the same sources (prefer plain upgrade, which syncs first)
--dangerously-skip-install-hook-checkwith --upgrade, run hook re-runs without asking
--dangerously-skip-build-hook-checkwith --upgrade, run build hooks without asking

An unscoped sync also provisions managed policy sources and registers newly listed curated sources. sync exits 1 if any fetch failed.

upgrade

mind upgrade [ITEM] [OPTIONS]

Fetch the involved sources, report each installed item that changed, and apply on confirmation. ITEM is a filter.

flageffect
--no-syncskip the fetch; compare against the clones on disk
--dangerously-skip-install-hook-checkrun install or update hook re-runs without asking
--dangerously-skip-build-hook-checkrun build hooks without asking

See Keep everything up to date.

curate

mind curate [OPTIONS]

Apply what your curators declare: register newly listed sources, install declared items, re-pin, upgrade curated sources, and report dropped entries. Alias reconcile.

flageffect
--checkreport the plan, change nothing
--prunealso uninstall and drop sources a curator no longer lists
--no-syncplan against the clones on disk
--adopt <IDENTITY>hand an existing source to the curator that lists it
--dangerously-skip-install-hook-checkrun install and update hooks without asking
--dangerously-skip-build-hook-checkrun build hooks without asking

See Follow a curated collection.

introspect

mind introspect [--fix]

Report drift, broken links, and unsynced sources. --fix recreates missing links without changing versions. Alias doctor.

hooks

mind hooks run <TARGET> [OPTIONS]
mind hooks list <TARGET>

Run a source’s or item’s hooks outside the usual verbs, or list them. TARGET is a source filter or a source#item ref.

flag (run)effect
--event <install|update|uninstall|build>which hooks (default install; build is for items)
--force, --rerunre-run hooks already recorded at the current commit
--dangerously-skip-install-hook-checkrun install or update hooks without asking
--dangerously-skip-build-hook-checkrun a build hook without asking

See Run setup with hooks.

config

mind config show
mind config lobes list
mind config lobes add [PATH] [--preset NAME] [--subdir REL] [--snapshot] [-f]
mind config lobes remove <PATH> [--snapshot]
mind config lobes detect

show prints the config file’s location and effective settings. lobes manages agent homes:

flageffect
--preset <NAME>gemini, codex, universal, or windsurf
--subdir <REL>a subdirectory under PATH (skills only)
--snapshotadd: write real-file copies, register nothing; remove: replace links with copies first
-f, --forceoverwrite a colliding file

detect reports which harnesses are installed and offers to add them. See Use more than one agent and Configuration and paths.

mind link-project [DIR] [--preset NAME] [--subdir REL] [--snapshot] [-f]

Shorthand for config lobes add DIR --preset windsurf, with DIR defaulting to the current directory. See Project lobes.

absorb

mind absorb <ITEM> [--to PATH] [-f]

Move one unmanaged item into a git repo you own, commit it, and install it as a managed item. --to sets the destination; --force overwrites a collision there. See Adopt items you placed by hand.

dump

mind dump [--output PATH] [--whole-sources]

Write a mind.toml that reproduces your sources, pins, and installed items. --whole-sources marks every source install = true. See Back up and reproduce your setup.

review

mind review [TARGET] [-N PREFIX] [--fix]
mind review --policy <PATH>

Validate a source (a path, a melded source, or a repo to shallow-clone; default .) or a managed policy file. Exits 1 on a hard finding. See Validate before publishing.

init-source

mind init-source [PATH] [--template] [--marketplace] [--flat-skills] [-N NAME]

Scaffold a mind.toml and report the references between items. See Validate before publishing.

evolve

mind evolve [--check] [--to VERSION]

Update the mind binary to the latest release, or to VERSION (the only way to reach a prerelease). --check reports without changing anything. Alias self-update. See Install.

completions and man

mind completions <bash|elvish|fish|powershell|zsh>
mind man

Print a shell completion script or the roff man page.

Older spellings

These still work as hidden aliases:

oldcurrent
meld --link-onlymeld --register-only
unmeld --unlink-onlyunmeld --keep-items
--follow-branch B, --pin-tag T, --pin-ref C--pin branch=B, --pin tag=T, --pin C
--as PREFIX--namespace PREFIX
evolve --version Vevolve --to V
--dangerously-skip-install-hook-check on unmeld or forget--dangerously-skip-hook-check

The mind.toml file

A source repo may place a mind.toml at its root to declare what it offers and how mind should treat it. It is always optional: a repo with no mind.toml is discovered by convention. mind.toml is enrichment, never a gate.

There are four discovery layers, in precedence order:

  1. Convention (default, no file): the scanner finds skills/<name>/SKILL.md, agents/<name>.md, rules/<name>.md, commands/<name>.md, workflows/<name>.js, tools/<name>/, and resources/<entry> (a resource is offered only when an item references it).
  2. Frontmatter (always read): each item’s description (and a tool’s bin / build) come from the frontmatter it already carries – except a workflow, which has no frontmatter at all; its description and whenToUse come from its export const meta object instead.
  3. Claude plugin manifest (optional): a .claude-plugin/plugin.json or .claude-plugin/marketplace.json in the repo is read as a discovery input. It is authoritative for the items it declares (convention scanning is skipped for the paths it covers) but an authoritative mind.toml overrides it. See Claude plugins and marketplaces.
  4. mind.toml (optional): [source] metadata is read regardless. Declaring [[items]] or [discover] item globs makes the file authoritative: convention scanning and the plugin-manifest layer are both turned off and only what the file lists is offered.

When does mind.toml take over discovery?

mind.toml containsconvention scanresult
nothing / no fileonitems found by convention (or by a .claude-plugin/ manifest, if present)
[source] onlyonconvention items, plus metadata (namespace, pin, …)
[discover].sources onlyonconvention items, plus a curated chain
[[items]]offexactly the declared items
[discover] with kind globsoffexactly the glob matches

The last two are authoritative. A bare [discover].sources list (curating other repos) does not turn off convention scanning, so a repo can ship its own items by convention and curate others at the same time (see Regular plus super-source).

When a repo has no authoritative mind.toml but does carry a Claude .claude-plugin/plugin.json or .claude-plugin/marketplace.json, that manifest supplies the items instead of convention scanning. An authoritative mind.toml overrides it (and meld notes the manifest was ignored). See Claude plugins and marketplaces.

[source] - repo metadata

All keys are optional.

[source]
description = "James's agent library"   # shown in recall/probe
namespace = "jk"                         # items install as jk:<name>
min-mind-version = "0.5"                 # refuse to scan under an older mind
follow-branch = "main"                   # pin: track a branch ...
# pin-tag = "v2"                          # ... or fix to a tag ...
# pin-ref = "a1b2c3d"                     # ... or to an exact commit (pick one)
roots = ["packages"]                     # scan under these dirs, not the repo root
ignore = ["scratch/", "**/*.tmp"]        # exclude from install + hash
  • namespace (previously prefix): every item installs as <namespace>:<name> (identity, store path, symlink, and ref), except an agent’s harness link, which stays bare. A consumer’s meld --namespace <ns> overrides it; meld --namespace '' removes it. See namespacing. The old key prefix still parses as a deprecated alias with identical behavior; new sources should use namespace.

  • install (deprecated): a shell command run once on meld, after checkout, to build or install the tooling the source’s items rely on. It is disclosed and prompted before it runs (--dangerously-skip-install-hook-check runs it unattended). Deprecated in favor of a [[hooks]] install entry below; still parsed, but new sources should use [[hooks]].

  • follow-branch / pin-tag / pin-ref: how sync tracks upstream. Declare at most one; two is an error. A consumer meld --pin overrides it (see Pinning a source version).

  • roots: convention discovery scans under each listed directory instead of the repo root, for a monorepo or subtree layout. Ignored when the file is authoritative ([[items]]/[discover] paths are always repo-root-relative).

  • ignore: glob patterns, relative to each item’s own path, excluded from both the store copy and the content hash. Needed when an item’s path holds more than the item, such as a top-level SKILL.md declared with path = ".": without it the store gets the repo’s .git/ and the item reads as drifted after every commit. A [[items]].ignore replaces this list for that item. Version-control directories (.git, .hg, .svn, .bzr) are excluded with or without a declaration; build output such as target/ or node_modules/ is not, and must be listed.

    A pattern is matched item-relative, with / as the separator: * stops at a /, ** crosses it, and a pattern with no / at all anchors at the ITEM ROOT. That last rule means ignore = ["target/"] does NOT exclude sub/target/: it excludes only a top-level target/. Verified against the binary: with ignore = ["target", "docs/**"], sub/target/o.bin was still copied into the store and hashed, with no error and no warning. To exclude build output at any depth, write **/target/ and **/node_modules/ instead of target/ and node_modules/. Also note foo/ removes the directory itself, while foo/** removes only its contents and leaves an empty foo/ in the store copy. ignore is a silent no-op on an agent or rule item: the item’s path IS a single file, so there is no path inside it for a pattern to match. See spec/ignore.md.

[[hooks]] - lifecycle hooks

Zero or more named hooks the maintainer declares. Each runs on the host (gated by a disclosure prompt) at the bound lifecycle event.

[[hooks]]
run = "make build"          # the shell command (required)
name = "build tooling"      # label shown in the disclosure (else the command)
optional = false            # required (run/skip/abort) vs optional (run/skip)
event = "install"           # "install" (on meld, default), "update" (on upgrade),
                            # or "uninstall" (on unmeld)

[[hooks]]
run = "make upgrade"        # runs at upgrade INSTEAD of re-running the install hooks
event = "update"

[[hooks]]
run = "make clean"
event = "uninstall"
optional = true

[[hooks]] is the canonical form. The legacy [source].install is a deprecated shorthand for one required install hook (still parsed); use [[hooks]] instead.

An install hook re-runs whenever the source advances, so write it to be idempotent; declare an event = "update" hook for a step that cannot be. See Run setup with hooks.

[[items]] - explicit inventory (authoritative)

List items explicitly when convention does not fit (a non-standard layout, export control, custom link targets). Declaring any [[items]] turns off convention scanning for the source.

[[items]]
kind = "rule"                    # skill | agent | rule | command | workflow | tool | resource (required)
name = "style"                   # the bare name (required)
path = "guidelines/style.md"     # path relative to the repo root; a dir for skills/tools (required)
link = "rules/house-style.md"    # optional: link target relative to the agent home
description = "House style"       # optional: overrides frontmatter

[[items]]
kind = "tool"
name = "detect"
path = "tools/detect"
bin = "detect.sh"                # tool only: what {{tools:detect}} resolves to
build = "make"                   # tool only: per-item build, run in staging at install
install = "./setup.sh"           # any kind: host side effect run after install
update = "./migrate.sh"          # any kind: replaces `install` on a re-install
uninstall = "./teardown.sh"      # any kind: host cleanup run before removal
ignore = ["scratch/"]            # replaces [source].ignore for this item

# ...or the array form, for multiple, named, or optional item hooks:
[[items.hooks]]
run = "./setup.sh"
name = "Set up"
event = "install"
  • bin and build are valid only on a tool; on any other kind they are a schema error.
  • A resource entry’s path names a file or a directory. It takes no link, bin, or build, and its description comes only from this entry, since nothing is read from a resource’s content. A declared resource is offered whether or not an item references it. See Share data files.
  • install / update / uninstall are per-item lifecycle hooks (valid on any kind), distinct from build (which produces the item’s content) and from the source-level [[hooks]]. An item that declares none here can declare its own, next to the item: a scoped mind.toml in a skill or tool directory ([[hooks]] only), or the install: / update: / uninstall: frontmatter keys in the item’s own file. See Run setup with hooks. A hook runs in the item’s store directory for a skill or a tool, and in the shared ~/.mind/store/<kind>/ directory for a single-file kind (agent, rule, command, workflow), so a relative ./script only works for a directory-backed item. A workflow’s hooks can come only from a root [[items]] entry: a .js has no frontmatter, and a single-file kind has no directory for a scoped mind.toml.
  • ignore on an item REPLACES [source].ignore for that item rather than adding to it; the built-in VCS-directory set (.git, .hg, .svn, .bzr) still applies either way. ignore = [] is the only way to opt one item out of a source-wide [source].ignore list.

[discover] - glob-based discovery

For odd or monorepo layouts where the items exist but not at the convention paths. Declaring any kind globs makes the file authoritative.

[discover]
skills = { include = ["packages/*/skill"], exclude = ["packages/internal/*"] }
agents = { include = ["agents/**/*.md"] }
rules  = { include = ["rules/*.md"] }
commands = { include = ["commands/**/*.md"] }   # name is the file STEM: a nested match flattens
workflows = { include = ["workflows/**/*.js"] }   # name is the file STEM, as for commands
tools  = { include = ["packages/*/tool"] }   # globs match the tool DIRECTORY
resources = { include = ["assets/*"] }   # each match (file or directory) is one resource

Within a kind, include globs are matched first, then anything also matched by an exclude glob is dropped. Tool globs match the tool directory (its TOOL.md, if present, supplies metadata), not an anchor file. A resource glob match keeps its full file name as the resource name (assets/logo.svg is resource:logo.svg).

A [discover].commands glob names each command by its file stem, so commands/frontend/component.md installs as component and is offered as /component, not the /frontend:component the harness gives it in the source repo; two files with the same stem collide with a DuplicateItem error at meld. To keep a grouped name or a nested layout, declare the item with an [[items]] entry (kind/name/path, plus a link when the subdirectory itself must be preserved); see Source layout.

[profiles] - install profiles

A profile is a named set of a source’s items that a consumer can enable at meld or learn, like cargo features. Each key is a profile name and each value is an array of members.

[profiles]
default = ["basic"]
basic   = ["skill:review", "skill:test", "rule:style"]
full    = ["basic", "agent:*", "skill:deploy"]
all     = ["*"]
  • Names match [a-z0-9][a-z0-9_-]* and are at most 64 bytes. Anything else, including a name with :, is an error.
  • Members are one of: an item pattern kind:name where name is a bare name or a glob (agent:*, skill:pdf-*); "*" for every item the source offers; or the name of another profile, which includes its members. A member splits at its first :, so command:frontend:build is the command named frontend:build. Write bare names; a namespace in effect is applied at install.
  • No cycles. A profile that includes itself, directly or through others, is an error naming the cycle.
  • Misses. An exact kind:name that the source does not offer is an error when the profile is expanded. A glob that matches nothing is not, so a source can list agent:* before it ships an agent.
  • default names the profile enabled when the consumer names none and does not opt out. A plain meld then offers the default expansion instead of every item. If [profiles] has no default, meld still offers to install everything and the profiles are opt-in only.
  • Root file only. Profiles are read from the repo’s root mind.toml. A scoped item mind.toml that declares [profiles] is an error.
  • Not authoritative. [profiles] is metadata like [source]: it does not turn off convention scanning, and a profile can only select items the source already offers.

Consumers pick profiles with meld --profile, learn --profile, and forget --profile (see Commands).

[discover].sources - curated super-source

A repo can list other repos to meld, acting as a curated registry. Melding it recursively melds each listed source (skipping any already registered; cycles terminate). Each nested source is registered independently and tracks its own upstream commit. mind dump generates exactly this shape of mind.toml from your installed state, so you can author a super-source automatically rather than by hand (see dump).

[discover]
sources = [
  { source = "owner/repo" },                        # melded, items left available
  { source = "github:foo/bar", namespace = "fb" },   # imposed namespace (like meld --namespace)
  { source = "owner/recommended", install = true }, # offered for install on meld
]

The equivalent table-array form is also valid:

[[discover.sources]]
source = "owner/recommended"
install = true
  • source: any repo spec meld accepts (owner/repo, a host-qualified spec, or a local path).
  • namespace (previously as): impose a namespace on that nested source. The old key as still parses as a deprecated alias.
  • install (default false): when true, melding the super-source offers that nested source’s items for install (the same preview-and-prompt as the top-level source), instead of leaving them registered but not installed.
  • install-items: a list of bare kind:name refs selecting only a subset of the nested source’s items to offer for install, e.g. install-items = ["skill:review", "agent:dev"]. install = true and a non-empty install-items are mutually exclusive (declaring both is an error). The empty-list form (install-items = []) is never used in practice; that case is install = false. This is source truth and takes literal kind:name refs only: globs are not expanded here (install-items = ["skill:*"] fails at meld), unlike the consumer-side meld --learn <NAME|GLOB>, which does accept globs and matches an item’s bare or effective name (see Commands).
  • profiles and default-profile: install directive for the nested source’s profiles. profiles = ["full"] enables those profiles, and default-profile = false leaves default out, as meld --profile and --no-default-profile do. profiles with install = true is an error; with install-items it installs the union. An install = true entry whose nested source declares default offers the default expansion. A profile name the nested source does not declare is an error at meld.
  • kind: "agent", "rule", or "command" – declares what an item link’s linked file is, the curator-side form of meld --kind. Only valid when source is itself an item link (a deep tree/blob URL); on an ordinary source it is a LinkKindMismatch error. Without it, the kind comes from the file’s containing directory (agents/, rules/, commands/), then from the file’s own frontmatter kind:. workflow is not a valid item-link kind, for the same .md-only reason.

By default a melded super-source registers the whole chain but installs only its own items plus the install = true (or install-items) entries. meld --recursive (-r) offers every nested source for install.

Adopting un-onboarded sources

A [[discover.sources]] entry may supply configuration for a nested source that has no mind.toml of its own:

  • follow-branch / pin-tag / pin-ref: curator-supplied pin directive for the nested source. Declare at most one; two is an error. sync uses whichever is set, the same as if the source had declared it in its own [source] table. A consumer’s explicit meld --pin still overrides this.
  • roots: convention scan roots for the nested source, for a monorepo or subtree layout.
  • add-roots: extra convention scan roots that compose with the nested source’s own discovery, the curator-side form of meld --add-root.
  • [[discover.sources.hooks]]: one or more hooks to run for the nested source. Each entry has the same shape as a source’s own [[hooks]] entry: a required run field, and optional name, optional, and event fields. They run under the same disclosure and safety prompt as the source’s own hooks (including the non-TTY skip and --dangerously-skip-install-hook-check).

roots, add-roots, flat-skills, and the hooks apply ONLY when the nested source ships no mind.toml. If the nested source has one, that file is authoritative for its own discovery and hooks and the curator-supplied values are ignored (a warning is emitted). The gate is whole-file: a nested mind.toml, even one declaring none of them, suppresses all four. add-roots is gated even though the consumer-side meld --add-root is not, so a curator cannot reach past a nested source’s authoritative export control on the consumer’s behalf.

The pin directive is NOT gated: it is authoritative whether or not the nested source ships a mind.toml, overriding that source’s own [source] pin. namespace and install are likewise unaffected; they always apply.

# Adopt a source that has no mind.toml: supply config it lacks.
# follow-branch, roots, and [[discover.sources.hooks]] apply only because
# this source ships no mind.toml of its own.
[[discover.sources]]
source = "owner/unonboarded"
follow-branch = "main"           # track this branch
roots = ["packages/agents"]      # scan under this subdir, not the repo root

[[discover.sources.hooks]]       # build hook, same shape as [[hooks]]
run = "make build"
name = "build tooling"
event = "install"

Scenarios

A regular source

A repo that ships its own skills, agents, rules, commands, workflows, and tools. The simplest form is no mind.toml at all (pure convention). Add a mind.toml to attach metadata or a namespace:

[source]
description = "James's agent library"
namespace = "jk"

Convention scanning still finds skills/<name>/SKILL.md, agents/<name>.md, etc., and each installs as jk:<name>. Use [[items]] or [discover] instead only when the layout is non-standard (those turn convention off).

A super-source

A curated registry that ships no items of its own, only a list of other repos:

[source]
description = "Curated agent registry"

[discover]
sources = [
  { source = "acme/agents" },
  { source = "acme/skills", namespace = "acme", install = true },
]

Melding it registers acme/agents and acme/skills (the latter namespaced acme: and offered for install). It has no items of its own, so without install = true (or meld --recursive) nothing installs; mind probe browses what the chain offers.

Regular plus super-source

A repo that both ships its own items and curates others. Because a bare [discover].sources is not authoritative, convention scanning still runs for this repo’s own items:

[source]
description = "James's library and registry"
namespace = "jk"

# This repo's own items are still found by convention (skills/, agents/, ...)
# because only [discover].sources is declared, not [[items]] or kind globs.

[discover]
sources = [
  { source = "acme/skills", namespace = "acme", install = true },
]

Melding installs this repo’s own jk:<name> items (per the default install prompt) and registers acme/skills (offered for install via its install = true flag). If you instead need to declare this repo’s items explicitly while also curating, add [[items]] or [discover] kind globs alongside sources; that turns convention off, so list every item the repo offers.

Configuration and paths

mind reads its settings from ~/.mind/config.toml, created on first use. An unknown key is an error. mind config show prints the file’s location and the settings in effect.

lobes = ["~/.claude", { path = "~/.gemini/config", kinds = ["skill"] }]
ssh = true
absorb-to = "~/dev/my-agent-library"
max-metadata-size = "32MiB"
keymeaningsee
lobesagent homes to link into, each optionally limited by kindsUse more than one agent
sshclone the owner/repo shorthand over SSHSSH cloning
absorb-todefault destination for mind absorbAdopt items you placed by hand
max-metadata-sizecap on each metadata file read from a sourceMetadata size cap

The legacy spelling absorb_to is still read.

Environment variables

variableeffect
MIND_HOMEroot of the ~/.mind tree
MIND_DEFAULT_LOBEthe default lobe instead of ~/.claude (CLAUDE_HOME is a legacy alias)
MIND_AGENT_HOMESa :-separated list of lobes for this command, replacing lobes
MIND_ABSORB_TOdestination for mind absorb, above absorb-to
MIND_MAX_METADATA_SIZEmetadata size cap, above max-metadata-size
MIND_POLICY_FILEa managed policy file, used only when none exists at the system path
MIND_HTTP_TIMEOUT_SECSrequest budget for mind evolve (default 15)
GH_TOKEN, GITHUB_TOKENtoken evolve sends to api.github.com
NO_COLORplain output

SSH cloning

To authenticate with an SSH key instead of an https username/password, meld the git@host:owner/repo form, or set ssh = true in ~/.mind/config.toml so the owner/repo shorthand clones over SSH. An https remote still prompts (or uses a credential helper) as git normally does.

Metadata size cap

Every metadata file mind reads from a source is size-capped: a mind.toml, an item’s frontmatter, a plugin or marketplace manifest, and a workflow’s meta object (which is its whole .js body). The default is 8 MiB, and a file past it is refused rather than read.

Raise it for a source with a legitimately large metadata file, or lower it to bound how much a source you do not trust can make mind allocate while scanning it:

mind meld owner/repo --max-metadata-size 32MiB
max-metadata-size = "32MiB"

The flag outranks MIND_MAX_METADATA_SIZE, which outranks the config key. A value is a byte count (16777216), a binary size (32MiB, 512KiB, 2GiB, or the bare 32M/512K/2G), a decimal size (16MB = 16000000), an explicit 512B, or unlimited, none, or any zero value (0, 0B, 0MiB) for no ceiling. Case and internal whitespace are not significant. A fractional size (1.5MiB) is refused rather than rounded; write a whole number of a smaller unit (1536KiB). mind config show reports the cap in force.

An unparseable value is an error naming which of the three origins supplied it, never a silent fall back to the next one down. That includes a MIND_MAX_METADATA_SIZE whose value is not valid UTF-8; only an unset, empty, or whitespace-only variable reads as unset. Only the origin that wins the precedence above is read at all, so a bad MIND_MAX_METADATA_SIZE (undecodable or not) is ignored on a run that passes --max-metadata-size.

Only the workflow kind treats an over-cap file as recoverable: it lists with no description and installs, as any workflow whose meta cannot be read does. For every other kind an over-cap file fails the scan of its source.

Paths

~/.mind/
  config.toml                   persistent settings (lobes, ssh, absorb-to,
                                max-metadata-size)
  sources.json                  source registry (melded repos)
  manifest.json                 installed-item manifest and file registry
  sources/<host>/<owner>/<repo> clone of each melded repo
  store/<kind>/<name>/          installed copy of each item (name is effective)
  personal/                     built-in absorb destination, created on demand
  .tmp/staging/                 scratch for new copies during transactional installs
  .tmp/backup/                  previous copy held during a swap, for rollback
  .lock                         global advisory lock

Override the roots with MIND_HOME (the ~/.mind tree) and MIND_DEFAULT_LOBE (the default lobe). CLAUDE_HOME is a legacy alias for MIND_DEFAULT_LOBE; MIND_DEFAULT_LOBE takes precedence when both are set.

Concurrency

A global advisory lock (~/.mind/.lock) is held by every mutating command (meld, unmeld, learn, forget, sync, upgrade, introspect --fix, config lobes add|remove). A second concurrent mind invocation blocks until the first finishes. The lock is released when the holding process exits, even on crash, so an aborted run never wedges the next one. Read-only commands (recall, probe, introspect, config show) take a shared lock and proceed concurrently with each other, but never observe a writer mid-update.

Install and upgrade are transactional

A failed learn or upgrade never leaves you worse off. The new copy is built in a staging directory first; the previous version is moved to a backup and only dropped after the swap succeeds. A failure at any point restores the previous version from backup.

When an installed item’s effective name changes, upgrade reports rename old -> new and handles it the same way: the new name is installed before the old one is removed. A different --namespace on an already-melded repo does not rename anything; it registers a separate instance (see Fix name collisions).

For diagnosing a failed install or broken links, see Troubleshooting.

Example sources

The examples/ directory in the repo holds runnable sources, each a small but valid mind source with its own README.md and a test that melds it so it cannot rot. This page maps each use case, consumer and maintainer, to the example that demonstrates it.

Every example is a directory inside the mind repo, not its own git repo, so clone the repo, copy the example out, and init a repo before melding (each README shows the exact commands):

git clone --depth 1 https://github.com/jaemk/mind /tmp/mind-repo
cp -r /tmp/mind-repo/examples/<name> /tmp/<name>-demo
cd /tmp/<name>-demo && git init -q && git add -A && git commit -qm init
mind meld /tmp/<name>-demo
mind probe

Example catalog

ExampleShowsKey spec
starterConvention discovery: a repo with no mind.toml, items found by skills/<n>/SKILL.md, agents/<n>.md, rules/<n>.md, commands/<n>.md, workflows/<n>.jsdiscovery.md
helloThe repo-root mind.toml’s [source].roots scanning examples/hello; what mind meld jaemk/mind offersdiscovery.md
toolingThe tool and resource kinds plus path tokens {{self}}, {{tools:name}}, {{resources:name}}, {{path:ref}}tooling.md, resources.md
hooksSource [[hooks]]: build/install tooling at meld, tear down at unmeld, with the disclosure promptinstall-hooks.md
explicitAuthoritative [[items]] inventory: export control, custom path/link, per-item install/uninstall hooksdiscovery.md
monorepo[source].roots: convention discovery rooted at per-package subtreesdiscovery.md
discover[discover] kind globs with per-kind include/exclude lists, for layouts roots cannot expressdiscovery.md
namespacingA prefix plus {{ns:name}} reference tokens that survive a renamenamespacing.md
super-source[discover].sources: a curated registry that melds other repos, optionally namespaced or auto-installed, plus mind curate reconciling it after the list changesdiscovery.md, curate.md
marketplace-pluginA Claude .claude-plugin/plugin.json: skills, agents, commands, and workflows mapped to items, unsupported components reported, a workflows component-path override ignoredmarketplace.md
marketplace-catalogA Claude .claude-plugin/marketplace.json: a catalog of in-repo plugins, each a namespaced sub-sourcemarketplace.md
marketplace-curatorA repo that is both a Claude marketplace and a mind curator: [discover].sources composes with the plugin manifest instead of suppressing itmarketplace.md
policyAn enterprise managed policy: trusted-source allowlist, require-pinned, lobe lockpolicy.md
driftItem drift end to end: recall/introspect flag an out-of-date item, upgrade re-links itlifecycle.md
multi-lobeLinking one item into more than one agent home (lobe) via config lobesstorage.md
absorbClaiming an unmanaged lobe item into a version-controlled source with absorbabsorb.md

Which example for which task

taskexampleguide
install from a plain repostarterFind and install items
resolve a name collisionnamespacingFix name collisions
follow a curated listsuper-sourceFollow a curated collection
meld a Claude plugin or marketplacemarketplace-plugin, marketplace-catalogClaude plugins and marketplaces
link into more than one agent homemulti-lobeUse more than one agent
see drift and upgradedriftKeep everything up to date
adopt a hand-placed itemabsorbAdopt items you placed by hand
run under a managed policypolicyManaged policy
export only some filesexplicitThe mind.toml file
lay out a monorepomonorepo, discoverThe mind.toml file
share a script between skillstoolingShare helper scripts
share a data file between skillstoolingShare data files
build tooling at meldhooksRun setup with hooks
curate other repossuper-source, marketplace-curatorCurate other repos

Troubleshooting

Find the symptom below. mind introspect is a good first step for anything installed: it reports missing links, drift, and unsynced sources.

Installing

An item did not show up in ~/.claude

Run mind introspect. It reports missing links and drift, and mind introspect --fix recreates missing links. If the item links into a non-Claude lobe only, check that lobe’s kinds filter (next entry).

Presets link skills only. Rules, commands, and workflows have no directory equivalent in those harnesses, so they stay in ~/.claude. See Which kinds link where.

learn refuses with LinkOccupied

mind never overwrites a file or link it did not create. Move the existing entry aside and learn again, pass learn --force to replace it, or absorb it if it is something you want to keep under version control.

Two sources ship an item with the same name

Namespace one: mind meld <repo> --namespace <prefix> installs its items as <prefix>:<name>. Agents are different, since the harness keys them by frontmatter name. See Fix name collisions.

An install is refused as an unsupported platform

mind links with real symlinks and supports unix-like systems only. On Windows, run it under WSL.

learn asks to install a tool or resource I did not select

The item you chose names it with {{tools:name}}, {{resources:name}}, or {{path:...}}, and those tokens are dependencies, so learn offers the whole set. Confirm with --yes. Without the tool or resource, the item’s expanded path would point at nothing. See Items that depend on other items.

An agent’s tool permissions do not work in Gemini or Codex

mind links files as they are and does not translate frontmatter. Keys such as Claude’s tools: allow-list may be ignored by another harness. See Frontmatter is not translated.

Updating and versions

An item shows as out of date with no upstream change

Its content no longer matches the source, usually because a store or source file was edited by hand. sync then upgrade the item, or restore the file.

Roll back to an older version

Re-pin the source to the older commit, then upgrade the item:

mind meld <repo> --pin <old-sha>
mind upgrade <item> --yes

See Pin a source to a version.

upgrade did nothing after a curator changed its list

sync and upgrade do not apply a curator’s decisions. Run mind curate. See Follow a curated collection.

Sources

A melded local source was moved or deleted

Listing commands warn and continue. Find its name with mind recall --sources, then mind unmeld <name>, or restore the directory.

A source’s items disappeared after setting roots

roots = [] scans nothing. Omit roots to scan the repo root, or list the real subdirectories.

A mind.toml change fails to parse

The file is strict, so a misspelled key is an error. Common slips: pin keys are hyphenated (follow-branch, pin-tag, pin-ref), as is min-mind-version. Run mind review <path> to check the file. See The mind.toml file.

A file under resources/ is not offered

Without an authoritative mind.toml, a resource is offered only when an item in the same source references it with {{resources:name}}. A plain mention of resources/<name> in prose does not count. mind review lists each skipped entry as unreferenced-resource. See Share data files.

A meld finds no items

The repo does not use the conventional layout. The meld prints the paths it scanned and the flags that reach other layouts (--add-root, --root, --flat-skills). See Source layout and item kinds.

Network and authentication

meld or sync says “git executable not found”

mind runs git for every clone and fetch. Install it and put it on PATH.

A private repo fails to authenticate

mind uses your git setup. Configure a git credential helper for HTTPS, or meld the SSH form (git@host:owner/repo, or ssh = true in ~/.mind/config.toml) with a key in ssh-agent. See Restricted networks.

meld or sync fails behind a proxy

Set HTTPS_PROXY (and NO_PROXY for internal hosts), or git’s http.proxy:

git config --global http.proxy http://proxy.corp:8080

See Proxies.

A TLS or certificate error behind a company proxy

Install the corporate CA in the system trust store; that fixes every case. Otherwise point git at it with http.sslCAInfo and evolve with SSL_CERT_FILE. See Custom CA.

evolve fails with a 403 from api.github.com

That is GitHub’s unauthenticated rate limit, often hit behind a shared egress IP. Set GH_TOKEN (or GITHUB_TOKEN) to any GitHub token, or skip the lookup with mind evolve --to <version>.

Agent homes

A Gemini lobe at ~/.gemini stopped working

The gemini preset path is ~/.gemini/config. Replace an older entry:

mind config lobes remove ~/.gemini
mind config lobes add --preset gemini

A lobe points at a directory that does not exist

Adding a lobe creates its directory and links installed items into it. For an older config whose lobe is unreachable, mind introspect --fix prunes it.

Where things live

~/.mind holds the config, store, and source clones; see Paths. Override the roots with MIND_HOME and MIND_DEFAULT_LOBE.

The terminal UI will not exit

Press q in the main view, or Ctrl-C twice from anywhere.

FAQ

Why “meld”, “learn”, and “forget”?

They are mind’s names for add, install, and uninstall. Each verb also answers to the conventional name (add, install, uninstall, list, …); see Verb aliases.

Does a source repo need a manifest?

No. mind finds items by their layout (skills/<name>/SKILL.md, agents/<name>.md, …). A mind.toml is optional and adds metadata, profiles, hooks, or an explicit inventory. See Source layout and item kinds.

Why does mind copy items instead of linking straight into the clone?

So a sync never changes what your agent loads. The store copy changes only when you upgrade, after mind shows you what changed. It also lets an upgrade roll back if the new version fails to install.

What is the difference between sync and upgrade?

sync fetches sources; installed items stay as they are. upgrade fetches the sources it needs and then updates installed items, after confirmation. mind update is an alias for sync. See Keep everything up to date.

Can I edit an installed skill?

Edits to the store copy show up as drift and are replaced on the next upgrade. To change a skill, edit it in a repo you own: absorb moves a hand-made item into one, and for someone else’s skill, fork the repo and meld your fork.

Does it work with harnesses other than Claude Code?

Yes, for skills: Gemini CLI, Antigravity, Codex CLI, Windsurf, and any harness that reads skills/<name>/SKILL.md from a directory you name. Agents link anywhere a lobe admits them. Rules, commands, and workflows are Claude-only. See Use more than one agent.

Can I use a Claude plugin marketplace?

Yes. A repo with .claude-plugin/plugin.json or marketplace.json melds as it is, and its skills, agents, commands, and workflows become items. See Claude plugins and marketplaces.

Does it run code from the repos I meld?

Only hooks a source declares, and only after showing you the exact command and asking. Without a terminal, hooks are skipped unless you pass a --dangerously-skip-... flag. mind review <repo> lists a source’s hooks before you meld it. See Run setup with hooks.

Does it make network calls on its own?

Only git operations against the sources you meld, and evolve against GitHub releases. There is no telemetry. See Restricted networks.

Can my organization restrict what gets installed?

Yes, with a managed policy file: a source allowlist, required pins, locked agent homes, and provisioned baseline sources. See Managed policy.

Does it work on Windows?

Under WSL. mind links items with symlinks and supports Linux and macOS.

Where does it keep things?

~/.mind: the config, the store, and source clones. See Paths.