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.tomladds a namespace, install profiles, hooks, or an explicit inventory.
Where to go next
- New here: Install, then the Quickstart.
- The words
minduses (meld, learn, lobe, store): Core concepts. - A specific task: the Using mind section in the sidebar, one page per goal.
- Publishing a repo: Publish your first source.
- Every verb and flag: the Commands reference.
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 --unmanagedalso 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-itemsdrops the source without touching installed items.rm -rf ~/.mind(or theMIND_HOMEoverride) removes the store, source clones, manifest, registry, and config – everything mind tracks on disk.forget/unmeldthe items you want cleaned up first: deleting~/.mindleaves lobe symlinks dangling, andmind introspecton a lobe with no~/.mindreports every link as broken.- Remove the
mindbinary itself:- Install script: delete it from
~/.local/bin/mind, or from$MIND_INSTALL_DIR/mindif you overrode the target directory. - Homebrew:
brew uninstall mind. cargo install:cargo uninstall mind-cli.
- Install script: delete it from
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
- Install into Gemini CLI, Codex CLI, or Windsurf too:
Use more than one agent.
mind config lobes detectfinds the harnesses on this machine. - Install only part of a large repo: Choose what a source installs.
- Install a single skill from a GitHub link: Install one item from a link.
- Learn the vocabulary: Core concepts.
- Try
mindagainst a source you can edit: the starter example and the other example sources.
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:
| kind | in a source repo | what the harness does with it |
|---|---|---|
skill | skills/<name>/SKILL.md plus any bundled files | loads the skill; offers it as /<name> |
agent | agents/<name>.md | a subagent the harness can delegate to |
rule | rules/<name>.md | context the harness reads |
command | commands/<name>.md | a slash command, /<name> |
workflow | workflows/<name>.js | a script offered to the harness’s Workflow tool (see Workflows) |
tool | tools/<name>/ | nothing directly: a helper other items call (see Share helper scripts) |
resource | resources/<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 syncfetches every source. Installed items do not change.mind upgradeshows what changed upstream for each installed item and, once you confirm, swaps in the new versions. It syncs the sources it needs first.mind evolveupdates themindbinary itself.
See Keep everything up to date.
Naming things
Commands take refs to name items and sources.
| form | names |
|---|---|
review | an item named review, of any kind |
skill:review | the skill named review |
owner/repo#review | the item review from that source |
'skill:*' | every skill (a glob; quote it) |
owner/repo or repo | a source, by its trailing path |
github.com/owner/repo | a source, by its full identity |
github.com/owner/repo@jk | the instance of that repo melded under namespace jk |
github.com/owner/repo#skills/foo | a 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
| term | meaning |
|---|---|
| meld | register a source (clone it) and optionally install its items |
| unmeld | drop a source and, by default, uninstall its items |
| learn | install an item |
| forget | uninstall an item |
| recall | list what is installed |
| probe | browse and search what sources offer |
| introspect | check for drift and broken links |
| evolve | update the mind binary |
| lobe | an agent home mind links into |
| store | ~/.mind/store, the installed copies |
| curator | a source that lists other sources (a super-source or a Claude marketplace) |
| profile | a named set of a source’s items, chosen at meld time |
| unmanaged item | something in a lobe that mind did not install |
| drift | an 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.
| alias | primary verb |
|---|---|
add | meld |
remove, rm | unmeld |
install | learn |
uninstall, unlearn | forget |
update | sync |
list, status | recall |
search | probe |
doctor | introspect |
reconcile | curate |
self-update | evolve |
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.
Try a skill from a GitHub link
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
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
- A single skill from a pasted GitHub or GitLab URL: Install one item from a link.
- A Claude plugin or marketplace repo melds as it is; its skills, agents, commands, and workflows show up as items. See Claude plugins and marketplaces.
- A curated list of repos registered in one meld: Follow a curated collection.
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:
--kind agent,--kind rule, or--kind commandon the command line.- The file’s directory:
agents/,rules/, orcommands/. - 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;upgradepicks up new commits. - a 40-character commit pins it.
--pinfreezes 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,mindwarns which entries it dropped, andmind recall <item>andmind introspectkeep 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.
Links in a curated list
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 want | use |
|---|---|
| to decide later, item by item | meld --register-only, then learn |
| specific items by name or pattern | meld --learn <pattern> |
| a set the source’s author defined | meld --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 reviewfinds an item that installs asteam:review. - Each match brings the siblings it depends on (see
Find and install items).
The rest of the source stays registered for
learnlater. - 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.--learnconflicts 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
defaultprofile keeps the install-everything offer on a plainmeld; its profiles are opt-in. --all-profilesenables every profile but does not install items that are in no profile.learn owner/repo --allinstalls the whole source.--learnand--profilecompose: 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:
| value | effect |
|---|---|
HEAD | freeze 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.
Pin a single-item link
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
upgradeor a source-wide one, not onupgrade <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.
Check for drift and broken links
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
Uninstall mind entirely
mind unmeld '*' --yesuninstalls every item and drops every source. Do this first: deleting~/.mindalone leaves dangling links in your lobes.- Optionally
mind forget --unmanagedto remove items you placed by hand. rm -rf ~/.mind(or yourMIND_HOME) removes the store, clones, and config.- Remove the binary: delete
~/.local/bin/mind(or$MIND_INSTALL_DIR/mind) for the install script,brew uninstall mind, orcargo 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:
| preset | lobe path | kinds linked |
|---|---|---|
gemini | ~/.gemini/config (Gemini CLI and Antigravity) | skill |
codex | ~/.agents | skill |
universal | ~/.agents | skill |
windsurf | <dir>/.windsurf, default the current directory | skill |
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.
| harness | skills directory | agents directory | lobe |
|---|---|---|---|
| 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 |
Which kinds link where
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.mdorGEMINI.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 overlobesinconfig.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 itsmind.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:
| change | meaning | applying it |
|---|---|---|
register | the curator lists a source you do not have | registers it, then installs what the entry declares |
install | the entry declares items or profiles that are not installed | installs them |
repin | the entry’s pin no longer matches the source’s recorded pin | re-pins and checks out the new commit |
upgrade | a curated source’s installed items are out of date | runs the upgrade for those sources |
unlist | a source you registered from the list is no longer on it | --prune only: uninstalls its items and drops it |
namespace | the entry declares a different namespace than your instance has | report only; prints the unmeld and meld that adopt it |
adopt | the curator lists a source you already have but does not manage | report 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 recalllists them after your sources, under “unmanaged: not installed by mind”, with each item’skind:nameand lobe paths.--kindfilters them;--sourcehides them, since they have no source.mind probeincludes them in its listing and search. In--jsoneach carries"unmanaged": trueand nosourceorhash.recall --jsoncovers 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:
--to <path>- the
MIND_ABSORB_TOenvironment variable absorb-toin~/.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 = [...](anddefault-profile = falsewhendefaultis off), plusinstall-itemsfor anything you installed by name on top.
- every item:
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 probewith no opt-out launches the TUI. It requires a TTY on stdout.- It falls back to the non-interactive catalog listing when
--no-tuiis given, when--jsonis given, or when stdout is not a terminal (piped or redirected). - The
query,--kind, and--sourcearguments 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
recallreports. - 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.
Navigation keys
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.
Search
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
minput takes a repo spec followed by anymind meldflags, parsed exactly as the CLI parses them, for exampleowner/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>andmind 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
Enterto return. The verbs that suspend aremeldandunmeld. - While the TUI holds the terminal, every
gitchild runs non-interactively (GIT_TERMINAL_PROMPT=0and an sshBatchMode=yeswrapper), 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
qquits 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:
| hook | flag | verbs |
|---|---|---|
| a source’s or item’s install/update hook | --dangerously-skip-install-hook-check | meld, learn, upgrade, sync --upgrade, curate, hooks run |
| an item’s build hook | --dangerously-skip-build-hook-check | meld, learn, upgrade, sync --upgrade, curate, hooks run |
| an uninstall hook | --dangerously-skip-hook-check | unmeld, 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.completionsandman, 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:
recallandprobe:{"schema": 1, "items": [...]}.introspect:{"schema": 1, "issues": [...], "sources": N, "items": N}.- Every changing verb (
meld,learn,forget,sync,upgrade,unmeld,config lobes addandremove) returns an object with"schema": 1and at leastaction,target, andoutcome. 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 whoseoutcomeisclean,pending, orapplied.
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
| exit | meaning |
|---|---|
| 0 | success, including “nothing to do” and curate with pending changes |
| 1 | any runtime error |
| 2 | a command-line usage error |
Specific cases:
syncexits 1 if any source failed to fetch, after saving the ones that succeeded.reviewexits 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:
| OS | Path |
|---|---|
| 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,meldrefuses any repo whose identity does not matchallow(SourceNotAllowed) before any clone or egress.learn,sync, andupgradeoperate only on allow-matching sources. A registered source that is no longer allowed is reported and skipped, not updated or installed from. - With
lockabsent or false,allowis advisory: a non-matchingmeldis 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: bothmind evolveandmind evolve --checkfail before any network call with a policy error. The disable applies to--checkas well so the binary does not nag about available updates.self-update = "X.Y.Z":evolveresolves to that exact version offline (noapi.github.comcall), behaving as if--to X.Y.Zwere passed. The pin is validated as a dotted-numeric version at policy-parse time (fail closed); a leadingvis stripped before validation. If--to Vis also passed and V differs from the pin, the command fails with a policy error naming the conflict.evolve --checkreports against the pinned version and respects the no-downgrade logic.self-update = true: identical to the absent key;evolveis 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 fetchagainst 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 itsSHA256SUMS. These redirect tohttps://release-assets.githubusercontent.com/...; allowlist that host.*.githubusercontent.comis the most resilient pattern: GitHub has migrated release assets between CDN subdomains.objects.githubusercontent.commay still be needed for older GitHub Enterprise Server deployments.
install.sh(install script). The same twogithub.com/release-assets.githubusercontent.comdownload URLs, plushttps://raw.githubusercontent.com/jaemk/mind/main/resources/install.shfor the script itself, andhttps://api.github.com/...unless you pinMIND_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:8080For NTLM/Kerberos proxy auth, set
http.proxyAuthMethod:git config --global http.proxyAuthMethod negotiate -
mind evolvehonorsALL_PROXY,HTTPS_PROXY, andHTTP_PROXY(first one set wins, in that order), each in either case, plusNO_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 uppercaseHTTP_PROXY. SetHTTPS_PROXYor the lowercase formhttps_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 evolveverifies against the system trust store directly: the keychain on macOS, the certificate stores on Windows, and on Linux the OpenSSL-convention bundle, which meansSSL_CERT_FILE(a bundle file) andSSL_CERT_DIR(a directory of hashed certs) also work:SSL_CERT_FILE=/path/to/corp-ca.pem mind evolveIt does not read
CURL_CA_BUNDLE, and it does not shell out tocurlorwgetas of 0.26.0. A failure here reportsinvalid peer certificateand points at this setting. -
git (every
meld/sync/upgradefetch):git config --global http.sslCAInfo /path/to/corp-ca.pem, or the system trust store. -
install.shstill usescurlorwget, since it runs beforemindexists.curlreadsCURL_CA_BUNDLEorSSL_CERT_FILE; thewgetfallback honors no CA environment variable at all, so behind a custom root CA the CA must be in the system trust store (or configured inwgetrc). That samewgetfallback reads only lowercase proxy environment variables (https_proxy, notHTTPS_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/repoform, or setssh = truein~/.mind/config.tomlso theowner/reposhorthand clones over SSH (see Configuration). The runningssh-agentsupplies 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 orfile://remote all meld the same way. Source identity ishost/owner/repo, so a GHEgithub.example.com/...source and agithub.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/*(identitylocal/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 = falseto block all local-path andfile://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 to | see |
|---|---|
| describe the source and set a default namespace | [source] |
| let consumers pick a basic or full set | Offer install profiles |
| have items reference each other safely | Namespaces and references |
| share a script between several skills | Share helper scripts |
| ship a data file or template items read | Share data files |
| build or install tooling when the source is melded | Run setup with hooks |
| make one item pull in another | Dependencies between items |
| ship only some files, or use a non-standard layout | [[items]] and [discover] |
| list other people’s repos as a collection | Curate other repos |
require a minimum mind version | min-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.mdanchor. Bundled files (aresources/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
.jsfile the harness loads fromworkflows/and offers to itsWorkflowtool. 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.jsonisresource: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
toolitem shares it through the store. Put it once undertools/<name>/and reference it by token ({{tools:name}}).mindcarries 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.
| token | expands 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:reviewstill matches when the source installs asjk:review. - A member splits at its first
:, socommand:frontend:buildis the command namedfrontend: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.tomlonly.
[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:
- 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.) - Repo-side:
[source].prefixinmind.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 |
|---|---|
| unprefixed | review |
--namespace jk | jk: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
toolkind: 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
---
| key | meaning |
|---|---|
description | shown in recall / probe |
bin | the entrypoint; what {{tools:name}} resolves to |
build | per-item build command run in staging at install |
The entrypoint resolution order is:
mind.toml[[items]].binTOOL.mdfrontmatterbin:- 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 frombin:.{{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.jsonandallowlist.yamlare 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 underexpand:. - It renders with a leading
~so aBash(~/.mind/store/**)permission rule matches. - It resolves within the same source only.
- A name that matches no resource fails the install, and
mind reviewreports it asbad-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 upgradepicks 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?
| tool | resource | |
|---|---|---|
| shape | a directory | a file or a directory |
| token | {{tools:name}} (the entrypoint) | {{resources:name}} (the path) |
| metadata | optional TOOL.md (description, bin, build) | none |
| offered by convention | always | only 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".
- The source’s root
mind.toml, in the item’s[[items]]entry (scalarinstall/update/uninstall, or an[[items.hooks]]array). - A scoped
mind.tomlin 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. - The item’s frontmatter, in whatever file describes it (
SKILL.md,TOOL.md, or an agent’s or rule’s.md): the scalarinstall:,update:, anduninstall: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 component | mind item |
|---|---|
skills/<name>/SKILL.md | a skill |
agents/<name>.md | an agent |
commands/<name>.md | a command |
workflows/<name>.js | a workflow |
hooks/, .mcp.json, LSP, monitors, themes, output-styles | not 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].sourcesentry, tracking its own upstream commit. Like other nested sources it is registered and left available;meld --recursiveextends 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.tomlthat declares[[items]]or[discover]kind globs is authoritative and suppresses the plugin-manifest layer, the same way it suppresses convention scanning.meldprints a note that a.claude-plugin/manifest was found and ignored. - A
[source]-onlymind.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].sourcesspec. - 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, aworkflows/workflow, and an unsupportedhooks/that reports a skipped count.
- a single plugin: one skill (namespaced by the plugin name), one agent (bare),
a
- 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
| key | meaning |
|---|---|
source | any repo meld accepts: owner/repo, a URL, a local path, or an item link |
namespace | install that source’s items under a namespace, as meld --namespace does |
install | true offers all its items for install at meld (default false) |
install-items | offer only these kind:name items (literal names, no globs) |
profiles | enable these install profiles of that source |
default-profile | false leaves that source’s default profile out |
follow-branch, pin-tag, pin-ref | pin that source (at most one) |
kind | for 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:
| key | meaning |
|---|---|
roots | scan these directories instead of the repo root |
add-roots | scan these directories in addition |
flat-skills | skills 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-versionnewer than the runningmind - 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].installfield - 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:
- Discovers the items exactly as a meld would, so what it reports is what a consumer gets.
- Writes a
mind.tomlif there is none, with a[source]table and a description placeholder. An existingmind.tomlis never overwritten. - Reports each item’s references to its siblings: those already written as
{{ns:name}}tokens, and bare mentions in prose. Bare mentions are reported asunguarded-referenceadvisories only when a namespace is in effect, since without one they resolve as written. - Reports
duplicate-toolingfor 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.
| verb | does |
|---|---|
meld | register a source repo and offer to install its items |
unmeld | drop a source and uninstall its items |
learn | install items |
forget | uninstall items |
recall | list what is installed, or one item’s details |
probe | browse and search what sources offer |
sync | fetch sources |
upgrade | bring installed items up to date |
curate | apply what your curators declare |
introspect | report drift and broken links |
hooks | run or list hooks on demand |
config | view config and manage agent homes |
link-project | add a project’s agent home |
absorb | move a hand-placed item into a source you own |
dump | write your setup as a mind.toml |
review | validate a source or a policy file |
init-source | scaffold a source’s mind.toml |
evolve | update the mind binary |
completions, man | shell completions and the man page |
Global flags
Accepted before or after any verb.
| flag | effect |
|---|---|
--json | one JSON document on stdout; see Scripts and CI |
-y, --yes | answer every confirmation |
--ascii | plain ASCII output, no color or Unicode |
-v, --verbose | extra 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.
| pattern | selects |
|---|---|
review | the item named review |
skill:review | the skill named review |
owner/repo#review | review 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 tolearn,forget, orupgradethat exists in two kinds (skill:greetandagent:greet) must be qualified. - Filters act on every match:
sync [source],--source <filter>onrecallandprobe,hooks run <target>, any glob, and theowner/repo#part of anupgradeargument. - A source filter is an exact name, a trailing part (
repo,owner/repo), or a glob over the fullhost/owner/repoidentity.
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.
| flag | effect |
|---|---|
--register-only | register 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-profile | leave the default profile out |
--all-profiles | enable 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, --recursive | offer the items of every source a curator lists |
-f, --force | overwrite link targets mind did not create; on a re-meld, also re-offer every install hook |
--local | install 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-skills | skills 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-check | run install hooks without asking |
--dangerously-skip-build-hook-check | run 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.
| flag | effect |
|---|---|
--keep-items | drop the source, leave its items installed |
--uninstall-hook <CMD> | supply or replace the source’s uninstall hooks |
--dangerously-skip-hook-check | run uninstall hooks without asking |
mind unmeld acme/agents
mind unmeld acme/agents --keep-items
mind unmeld '*agents' --yes
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.
| flag | effect |
|---|---|
--all | every item of the named source |
--profile <NAME> | install and enable that profile of a melded source (repeatable or comma list) |
--all-profiles | install and enable every declared profile |
-n, --dry-run | show what would install, change nothing |
-f, --force | overwrite a link target mind did not create |
--local | install into the current project’s lobe only |
--pin | for 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-check | run an item’s install hook without asking |
--dangerously-skip-build-hook-check | run 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
forget
mind forget [ITEM] [OPTIONS]
Uninstall items: the store copy and every link. Alias uninstall, unlearn.
| flag | effect |
|---|---|
--profile <NAME> | disable a profile of the source named by ITEM, forgetting items installed only for it |
--unmanaged | act on unmanaged lobe items only; with no ITEM, all of them |
-f, --force | skip the confirmation when other items depend on the one removed |
--dangerously-skip-hook-check | run 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.
| flag | effect |
|---|---|
--sources | list sources instead, with their hooks and profiles |
--kind <KIND> | only items of this kind |
--source <FILTER> | only items from matching sources |
--tree | installed 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.
| flag | effect |
|---|---|
--kind <KIND> | only items of this kind |
--source <FILTER> | only items from matching sources |
--no-tui | print 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.
| flag | effect |
|---|---|
--upgrade | then run upgrade over the same sources (prefer plain upgrade, which syncs first) |
--dangerously-skip-install-hook-check | with --upgrade, run hook re-runs without asking |
--dangerously-skip-build-hook-check | with --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.
| flag | effect |
|---|---|
--no-sync | skip the fetch; compare against the clones on disk |
--dangerously-skip-install-hook-check | run install or update hook re-runs without asking |
--dangerously-skip-build-hook-check | run 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.
| flag | effect |
|---|---|
--check | report the plan, change nothing |
--prune | also uninstall and drop sources a curator no longer lists |
--no-sync | plan against the clones on disk |
--adopt <IDENTITY> | hand an existing source to the curator that lists it |
--dangerously-skip-install-hook-check | run install and update hooks without asking |
--dangerously-skip-build-hook-check | run 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, --rerun | re-run hooks already recorded at the current commit |
--dangerously-skip-install-hook-check | run install or update hooks without asking |
--dangerously-skip-build-hook-check | run 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:
| flag | effect |
|---|---|
--preset <NAME> | gemini, codex, universal, or windsurf |
--subdir <REL> | a subdirectory under PATH (skills only) |
--snapshot | add: write real-file copies, register nothing; remove: replace links with copies first |
-f, --force | overwrite a colliding file |
detect reports which harnesses are installed and offers to add them. See
Use more than one agent and Configuration and paths.
link-project
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:
| old | current |
|---|---|
meld --link-only | meld --register-only |
unmeld --unlink-only | unmeld --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 V | evolve --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:
- 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>/, andresources/<entry>(a resource is offered only when an item references it). - Frontmatter (always read): each item’s
description(and a tool’sbin/build) come from the frontmatter it already carries – except a workflow, which has no frontmatter at all; its description andwhenToUsecome from itsexport const metaobject instead. - Claude plugin manifest (optional): a
.claude-plugin/plugin.jsonor.claude-plugin/marketplace.jsonin 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 authoritativemind.tomloverrides it. See Claude plugins and marketplaces. 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 contains | convention scan | result |
|---|---|---|
| nothing / no file | on | items found by convention (or by a .claude-plugin/ manifest, if present) |
[source] only | on | convention items, plus metadata (namespace, pin, …) |
[discover].sources only | on | convention items, plus a curated chain |
[[items]] | off | exactly the declared items |
[discover] with kind globs | off | exactly 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(previouslyprefix): every item installs as<namespace>:<name>(identity, store path, symlink, and ref), except an agent’s harness link, which stays bare. A consumer’smeld --namespace <ns>overrides it;meld --namespace ''removes it. See namespacing. The old keyprefixstill parses as a deprecated alias with identical behavior; new sources should usenamespace. -
install(deprecated): a shell command run once onmeld, 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-checkruns 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: howsynctracks upstream. Declare at most one; two is an error. A consumermeld --pinoverrides 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-levelSKILL.mddeclared withpath = ".": without it the store gets the repo’s.git/and the item reads as drifted after every commit. A[[items]].ignorereplaces this list for that item. Version-control directories (.git,.hg,.svn,.bzr) are excluded with or without a declaration; build output such astarget/ornode_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 meansignore = ["target/"]does NOT excludesub/target/: it excludes only a top-leveltarget/. Verified against the binary: withignore = ["target", "docs/**"],sub/target/o.binwas 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 oftarget/andnode_modules/. Also notefoo/removes the directory itself, whilefoo/**removes only its contents and leaves an emptyfoo/in the store copy.ignoreis 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"
binandbuildare valid only on atool; on any other kind they are a schema error.- A
resourceentry’spathnames a file or a directory. It takes nolink,bin, orbuild, and itsdescriptioncomes 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/uninstallare per-item lifecycle hooks (valid on any kind), distinct frombuild(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 scopedmind.tomlin a skill or tool directory ([[hooks]]only), or theinstall:/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./scriptonly works for a directory-backed item. A workflow’s hooks can come only from a root[[items]]entry: a.jshas no frontmatter, and a single-file kind has no directory for a scopedmind.toml.ignoreon an item REPLACES[source].ignorefor 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].ignorelist.
[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:namewherenameis 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:, socommand:frontend:buildis thecommandnamedfrontend: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:namethat the source does not offer is an error when the profile is expanded. A glob that matches nothing is not, so a source can listagent:*before it ships an agent. defaultnames the profile enabled when the consumer names none and does not opt out. A plainmeldthen offers thedefaultexpansion instead of every item. If[profiles]has nodefault,meldstill 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 itemmind.tomlthat 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 specmeldaccepts (owner/repo, a host-qualified spec, or a local path).namespace(previouslyas): impose a namespace on that nested source. The old keyasstill parses as a deprecated alias.install(defaultfalse): whentrue, 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 barekind:namerefs selecting only a subset of the nested source’s items to offer for install, e.g.install-items = ["skill:review", "agent:dev"].install = trueand a non-emptyinstall-itemsare mutually exclusive (declaring both is an error). The empty-list form (install-items = []) is never used in practice; that case isinstall = false. This is source truth and takes literalkind:namerefs only: globs are not expanded here (install-items = ["skill:*"]fails at meld), unlike the consumer-sidemeld --learn <NAME|GLOB>, which does accept globs and matches an item’s bare or effective name (see Commands).profilesanddefault-profile: install directive for the nested source’s profiles.profiles = ["full"]enables those profiles, anddefault-profile = falseleavesdefaultout, asmeld --profileand--no-default-profiledo.profileswithinstall = trueis an error; withinstall-itemsit installs the union. Aninstall = trueentry whose nested source declaresdefaultoffers thedefaultexpansion. 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 ofmeld --kind. Only valid whensourceis itself an item link (a deeptree/blobURL); on an ordinarysourceit is aLinkKindMismatcherror. Without it, the kind comes from the file’s containing directory (agents/,rules/,commands/), then from the file’s own frontmatterkind:.workflowis 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.syncuses whichever is set, the same as if the source had declared it in its own[source]table. A consumer’s explicitmeld --pinstill 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 ofmeld --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 requiredrunfield, and optionalname,optional, andeventfields. 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"
| key | meaning | see |
|---|---|---|
lobes | agent homes to link into, each optionally limited by kinds | Use more than one agent |
ssh | clone the owner/repo shorthand over SSH | SSH cloning |
absorb-to | default destination for mind absorb | Adopt items you placed by hand |
max-metadata-size | cap on each metadata file read from a source | Metadata size cap |
The legacy spelling absorb_to is still read.
Environment variables
| variable | effect |
|---|---|
MIND_HOME | root of the ~/.mind tree |
MIND_DEFAULT_LOBE | the default lobe instead of ~/.claude (CLAUDE_HOME is a legacy alias) |
MIND_AGENT_HOMES | a :-separated list of lobes for this command, replacing lobes |
MIND_ABSORB_TO | destination for mind absorb, above absorb-to |
MIND_MAX_METADATA_SIZE | metadata size cap, above max-metadata-size |
MIND_POLICY_FILE | a managed policy file, used only when none exists at the system path |
MIND_HTTP_TIMEOUT_SECS | request budget for mind evolve (default 15) |
GH_TOKEN, GITHUB_TOKEN | token evolve sends to api.github.com |
NO_COLOR | plain 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
| Example | Shows | Key spec |
|---|---|---|
| starter | Convention 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>.js | discovery.md |
| hello | The repo-root mind.toml’s [source].roots scanning examples/hello; what mind meld jaemk/mind offers | discovery.md |
| tooling | The tool and resource kinds plus path tokens {{self}}, {{tools:name}}, {{resources:name}}, {{path:ref}} | tooling.md, resources.md |
| hooks | Source [[hooks]]: build/install tooling at meld, tear down at unmeld, with the disclosure prompt | install-hooks.md |
| explicit | Authoritative [[items]] inventory: export control, custom path/link, per-item install/uninstall hooks | discovery.md |
| monorepo | [source].roots: convention discovery rooted at per-package subtrees | discovery.md |
| discover | [discover] kind globs with per-kind include/exclude lists, for layouts roots cannot express | discovery.md |
| namespacing | A prefix plus {{ns:name}} reference tokens that survive a rename | namespacing.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 changes | discovery.md, curate.md |
| marketplace-plugin | A Claude .claude-plugin/plugin.json: skills, agents, commands, and workflows mapped to items, unsupported components reported, a workflows component-path override ignored | marketplace.md |
| marketplace-catalog | A Claude .claude-plugin/marketplace.json: a catalog of in-repo plugins, each a namespaced sub-source | marketplace.md |
| marketplace-curator | A repo that is both a Claude marketplace and a mind curator: [discover].sources composes with the plugin manifest instead of suppressing it | marketplace.md |
| policy | An enterprise managed policy: trusted-source allowlist, require-pinned, lobe lock | policy.md |
| drift | Item drift end to end: recall/introspect flag an out-of-date item, upgrade re-links it | lifecycle.md |
| multi-lobe | Linking one item into more than one agent home (lobe) via config lobes | storage.md |
| absorb | Claiming an unmanaged lobe item into a version-controlled source with absorb | absorb.md |
Which example for which task
| task | example | guide |
|---|---|---|
| install from a plain repo | starter | Find and install items |
| resolve a name collision | namespacing | Fix name collisions |
| follow a curated list | super-source | Follow a curated collection |
| meld a Claude plugin or marketplace | marketplace-plugin, marketplace-catalog | Claude plugins and marketplaces |
| link into more than one agent home | multi-lobe | Use more than one agent |
| see drift and upgrade | drift | Keep everything up to date |
| adopt a hand-placed item | absorb | Adopt items you placed by hand |
| run under a managed policy | policy | Managed policy |
| export only some files | explicit | The mind.toml file |
| lay out a monorepo | monorepo, discover | The mind.toml file |
| share a script between skills | tooling | Share helper scripts |
| share a data file between skills | tooling | Share data files |
| build tooling at meld | hooks | Run setup with hooks |
| curate other repos | super-source, marketplace-curator | Curate 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).
A skill links into Gemini or Codex but a rule, command, or workflow does not
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.