Introduction
mind is a manager for agent tooling: skills, agents, rules, and tools. It melds
arbitrary git repos and links the items they offer into one or more agent homes
(default ~/.claude).
- A source is a melded git repo (
mind meld). It offers items: skills, agents, rules, and tools, found by convention or declared in amind.toml. mind learn <item>copies an item into the store (~/.mind/store) and symlinks it into each lobe (agent home). A tool is the exception: store-only helper tooling reached by reference, not linked into a lobe by default. Lobes can be non-Claude homes (Gemini CLI, Codex CLI, Antigravity) with a per-kind filter so only the compatible item types link in; see Configuration.mind recallandmind probeinspect what is installed and what is available;mind syncandmind upgradekeep sources and installed items current.- For authoring,
mind init-sourceandmind reviewscaffold and validate a source for publishing.
This site is the reference for installing, using, and authoring mind. Start
with Install and the Quickstart; Commands
is the full verb reference. For authoring a source, see Source layout
and Authoring a source. The normative behavior is the
spec.
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.
Linux (install script)
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 / aarch64) and installs it
to ~/.local/bin. 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.2.0 sh
If ~/.local/bin is not on your PATH, the script prints the line to add. The
same script also serves Apple Silicon macOS; Intel macOS should use the tap below.
Homebrew (macOS / 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.
Updating
mind evolve updates the binary itself to the latest release. It reports the
target version and prompts before downloading, unless --yes is given (--check
reports without changing anything, --version <v> pins a target). It uses the
same download path as the install script.
Quickstart
Meld a source, install an item, and see it linked into ~/.claude:
mind meld owner/repo # clone and register a source repo
mind probe # browse and search available items (interactive)
mind learn <item> # install one into each agent home
mind recall # list what's installed
Agent homes can be Claude Code, Gemini CLI, Codex CLI, or Antigravity – not just
~/.claude. See Configuration for the
per-harness path table and preset commands.
For a self-contained first run with no remote, use the bundled starter source (a plain convention layout, see examples/starter/):
cp -r examples/starter /tmp/starter
cd /tmp/starter && git init -q && git add -A && git commit -qm init
mind meld /tmp/starter
mind probe
mind learn greet
Commands is the full verb reference. Source layout covers how a source repo exposes items.
Examples
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 copy
it out and init a repo before melding (each README shows the exact commands):
cp -r 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 | discovery.md |
| tooling | The tool kind plus path tokens {{self}}, {{tools:name}}, {{path:ref}} | tooling.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 |
| 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 | discovery.md |
| marketplace-plugin | A Claude .claude-plugin/plugin.json: skills and agents mapped to items, unsupported components reported | marketplace.md |
| marketplace-catalog | A Claude .claude-plugin/marketplace.json: a catalog of in-repo plugins, each a namespaced sub-source | marketplace.md |
| policy | An enterprise managed policy: trusted-source allowlist, require-pinned, lobe lock | policy.md |
Consumer use cases
You are installing and managing tooling that other people authored.
- Install an item from a source. Meld a repo, then learn an item. See the
Quickstart and the starter
example (
mind meld,mind learn). - Browse and search what is available.
mind probeopens an interactive browser, or prints a listing when piped. See Commands. - Resolve a name collision between two sources. Namespace one on install with
mind meld <repo> --namespace <prefix>, so its items install as<prefix>:<name>. See namespacing and Troubleshooting. - Pull from a curated registry. Meld a super-source to register a whole chain
of repos at once;
meld --recursiveoffers every nested source for install. See super-source. - Meld a Claude plugin or marketplace. A repo with a
.claude-plugin/plugin.jsonor.claude-plugin/marketplace.jsonmelds with no re-packaging; its skills and agents show up as items. See Claude plugin marketplaces and the marketplace-plugin / marketplace-catalog examples. - Install into more than one agent home. Configure
lobesin~/.mind/config.toml. See Configuration. - Stay up to date.
mind syncrefreshes every source;mind upgradeupgrades installed items and reports deltas first. See Commands. - Run under an enterprise policy. A fixed-path managed policy restricts a client to trusted sources and locks related settings. See policy.
Maintainer use cases
You are authoring a source repo for others to meld.
- Ship items with zero config. Use the convention layout; no
mind.tomlneeded. See Source layout and starter. - Declare an explicit inventory, or control what is exported. List items with
[[items]]to turn convention off, set custompath/link, and omit files you do not want offered. See explicit. - Lay out a monorepo or subtree. Point
[source].rootsat the package subtrees, or use[discover]kind globs for layouts roots cannot express. See monorepo. - Share helper tooling across items. Ship a
toolitem and reference it with{{tools:name}}/{{path:ref}}, or bundle a script with one skill and address it with{{self}}. See tooling and Source layout. - Build or install tooling at meld. Declare a source
[[hooks]]install entry (and an uninstall entry for teardown). See Install hooks and hooks. - Run a host side effect per item. Declare per-item
install/uninstallhooks (or[[items.hooks]]). See explicit. - Make intra-source references survive a prefix. Write sibling references as
{{ns:name}}tokens. See namespacing and Authoring a source. - Curate other repos into a registry. List them in
[discover].sources; a bare list keeps your own convention items too. See super-source. - Validate and scaffold before publishing.
mind init-sourcescaffolds amind.tomland reports references;mind reviewvalidates a source. See Authoring a source.
Commands
Mental model
mind connects sources (git repos full of agent tooling) and lobes
(your agent homes, like ~/.claude/~/.agents).
Source. A melded git repo. mind meld <repo> clones the repo into
~/.mind/sources/<host>/<owner>/<repo> and records it in ~/.mind/sources.json.
This initializes the source and makes its items available to mind learn.
mind sync (or re-melding, re-running mind meld <repo>) refreshes the clone.
Item. A unit offered by a source, one of four kinds:
skill- askills/<name>/directory containing aSKILL.mdand any associated resources (scripts, templates, etc).agent- anagents/<name>.mdfile.rule- arules/<name>.mdfile.tool- atools/<name>/directory containing aTOOL.md, a script/executable, any associated resources. Tools are an optional feature to assist with managing and referencing shared scripts/executables utilized by multiple skills.
Items are discovered by convention (the paths above) or declared in a
mind.toml.
Lobe. An agent home mind links items into: the directory holding
skills/, agents/, and rules/. The default lobe is ~/.claude; you can add
Gemini, Codex, Antigravity, or any directory, each with an optional per-kind
filter (see Configuration). The gemini preset (path
~/.gemini/config) covers both Gemini CLI and the Antigravity IDE. Users of the
Antigravity CLI who previously used an antigravity-cli preset should configure a
custom lobe path manually.
Learn. mind learn <item> copies the item out of the source clone into the
store (~/.mind/store/<kind>/<name>) and symlinks that store copy into every
lobe. The store copy is the stable thing your agent homes point at, so a later
sync cannot change an installed item under you until you choose to upgrade.
forget reverses it.
What each step puts on disk
mind meld jaemk/mind clones the source. Nothing is linked yet:
$ mind meld jaemk/mind
~/.mind/
sources.json # registry: `mind` is now melded
sources/
github.com/jaemk/mind/ # the clone (a staging area)
examples/hello/skills/hello-mind/ # the offered skill
SKILL.md
... # the rest of the repo
store/ # empty - nothing learned yet
mind probe browses what the melded sources offer before you learn anything
(kind:name, source, content hash, description):
$ mind probe
skill:hello-mind github.com/jaemk/mind c62e88cc A hello-world skill; confirms mind melded this repo
mind learn jaemk/mind#skill:hello-mind copies that one item into the store and
symlinks it into the lobe:
$ mind learn jaemk/mind#skill:hello-mind
~/.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 (agent home)
skills/
hello-mind -> ~/.mind/store/skill/hello-mind # symlink the harness discovers
The harness now resolves /hello-mind through that symlink. A tool the skill
referenced would instead land in ~/.mind/store/tool/<name> with no symlink
under ~/.claude, present for the skill to call but invisible to the harness.
Stay current. sync refreshes every source clone; upgrade moves installed
items to the refreshed version, reporting hash and commit deltas before changing
anything; evolve updates the mind binary itself.
Inspect. recall and probe show what is installed and what is available;
introspect reports drift and broken links.
Verbs
| command | does |
|---|---|
mind meld [<repo>] [--link-only] [--yes] [-f|--force] [-r|--recursive] [-n|--namespace <prefix>] [--root <dir>] [--follow-branch <branch> | --pin-tag <tag> | --pin-ref <commit>] [--install-hook <cmd>] [--dangerously-skip-install-hook-check] | clone and register a source (default .), then prompt to install its items (--link-only registers only; --yes installs without prompting; -f/--force overwrites conflicting non-mind link targets; -r/--recursive offers to install items from every nested source a super-source curates). Re-melding an already-melded source installs any missing items, else shows each item’s install state and commit |
mind init-source [<path>] [--template] | scaffold mind.toml + report references; --template rewrites bare refs as {{ns:}} (maintainer) |
mind unmeld <name> [--unlink-only] [--yes] [--uninstall-hook <cmd>] [--dangerously-skip-install-hook-check] (alias detach) | drop a source and forget its items (--unlink-only keeps them) |
mind learn [--yes] [-f|--force] [-n|--dry-run] [--all] <item> | install a skill/agent/rule/tool (glob installs many); a partial selection also pulls in the source siblings it references. --force overwrites a conflicting non-mind link target (without it, a conflict prompts on a TTY); --all installs every item of the named source (shorthand for <source>#*); -n/--dry-run previews the dependency closure without installing anything |
mind forget [--yes] [-f|--force] [--unmanaged] [--dangerously-skip-install-hook-check] [<item>] (alias unlearn) | remove an installed item (glob removes many; a multi-match glob confirms first, --yes skips). --unmanaged scopes removal to unmanaged lobe items only; with no <item>, removes every unmanaged item across all lobes. -f/--force skips the dependents confirmation when the item being removed has dependents. --dangerously-skip-install-hook-check runs uninstall hooks without the safety prompt |
mind sync [--upgrade] [--dangerously-skip-install-hook-check] | refresh every source (optionally upgrade after; flag allows unattended hook re-runs) |
mind upgrade [--yes] [--dangerously-skip-install-hook-check] [item] | upgrade installed items to their latest source version (re-runs install hooks on sources that advance) |
mind evolve [--check] [--yes] [--version <v>] | update the mind binary itself to the latest release (or –version) |
mind recall [item] [--sources] [--kind K] [--source S] [--tree] [--json] (alias status) | status: each source with its items, marked installed or available; --sources narrows to sources; <item> shows one item’s details; --tree renders installed items as a dependency forest (with an item ref, scopes to that item’s subtree) |
mind probe [query] [--kind K] [--source S] [--json] [--no-tui] | browse and search items (interactive TUI on a terminal) |
mind review <target> [-n|--namespace <prefix>] / mind review --policy <path> | validate a source for publishing, or validate a managed policy file (read-only) |
mind introspect [--fix] [--json] | report drift and broken links (optionally repair) |
mind config show / mind config lobes add [<path> | --preset <name>]|list|remove <path>|detect [--yes] [--json] (alias config target) | view config and manage agent homes (lobes). add --preset <name> adds a preset lobe with a preconfigured path and kinds filter (presets: gemini, codex, universal). detect reports which known harness homes exist on the machine and offers to add their presets; adds only with --yes or an interactive TTY confirm; --json emits detection results as structured JSON. config lobes list and config show include the kinds filter for each lobe, e.g. ~/.gemini/config [skill]. See Configuration for the preset table and per-harness path details. |
mind dump [--output <path>] [--whole-sources] | write a super-source mind.toml reproducing the current melded and installed state (to stdout or --output <path>); each source is pinned to its recorded commit; item-filtered by default (--whole-sources emits install = true for every source regardless of install count) |
mind absorb <ref> [--to <path>] [-f|--force] | claim a single unmanaged lobe item into a version-controlled source and install it as a managed item; --to sets the destination (see absorb for full destination precedence); --force overwrites a kind:name collision at the destination |
mind completions <shell> / mind man | shell completions / man page |
A source repo exposes items by convention (skills/<n>/SKILL.md,
agents/<n>.md, rules/<n>.md, tools/<n>/), via a mind.toml, or via a Claude
.claude-plugin/ manifest (see Claude plugin marketplaces). See
Source layout and the
examples/: starter for the
plain convention layout, namespacing for {{ns:}} reference tokens under a
prefix, and policy for an enterprise managed policy. The full behavioral spec
is at spec/.
probe
mind probe with no flags opens an interactive browser of melded sources and
items (search, install, remove, meld, unmeld, sync, upgrade) when stdout is a
terminal. -n / --no-tui or --json, or a piped or redirected stdout, prints
the listing instead.
Selecting items (globs)
learn, forget, upgrade, unmeld, probe, and recall all accept a glob
in place of an exact item ref. The kind prefix, source qualifier, and glob
compose:
| pattern | selects |
|---|---|
'*' | every item across all sources |
'skill:*' | all skills |
'owner/repo#*' | all items of one source |
'review*' | items whose name starts with review |
The glob is matched against the effective (installed) name. A glob matching
nothing is ItemNotFound (for items) or SourceNotFound (for sources). The
exception is upgrade: a glob (or exact ref) that matches no installed item
reports up-to-date rather than erroring, since upgrading nothing is a no-op.
Shell-quoting caveat: quote the glob so the shell does not expand it before
mind sees it:
mind learn 'skill:*'
mind forget 'owner/repo#*'
Spec: CLI-31, CLI-41, CLI-65.
Filtering with –kind and –source
recall and probe accept two composable filters:
--kind <skill|agent|rule|tool>narrows to one item kind.--source <selector>narrows to items from a matching source. The selector is an exact name, an unambiguous trailing suffix (repoorowner/repo), or a glob matched against the fullhost/owner/repoidentity (so*spans/):
mind recall --kind skill
mind probe --source '*agents'
mind recall --source my-repo --kind rule
For recall, these filters apply to the installed-items listing only, not to
--sources or a single-item lookup. Spec: CLI-83, CLI-86.
Global flags and output
--json, --yes (-y), and --ascii are global flags accepted before or after
any verb. Position does not matter: mind --json recall and mind recall --json
are equivalent (CLI-150).
Color and Unicode. Output uses ANSI color and Unicode glyphs when all of the
following hold: stdout is a TTY, the locale is UTF-8, NO_COLOR is unset, and
neither --json nor --ascii is in effect. Any one of those conditions being
false forces plain ASCII output with no color escapes. The ASCII fallback
substitutes visually equivalent characters (+ installed, ! warning, x
error, - available) so no information is lost (CLI-151, CLI-152, CLI-154).
NO_COLOR set to any value (including empty), a non-UTF-8 or unset locale, or
--ascii each independently force plain ASCII regardless of the others.
--json output. Read-only verbs (recall, probe, introspect) emit their
existing JSON shapes. Every mutating verb (meld, learn, forget, sync,
upgrade, unmeld, config lobes add/remove) emits a structured result
object with at minimum action, target, and outcome fields (CLI-153).
Exit status
Exit 0 on success. Any MindError is printed to stderr and exits non-zero
(CLI-100).
sync exits non-zero (SyncFailed) when any per-source fetch fails, even if
other sources succeeded; successfully fetched commits are persisted and reported
(CLI-54).
review distinguishes hard errors (malformed mind.toml, unknown item kind,
unresolved {{ns:}} token) from advisory findings (unguarded references, missing
descriptions). Hard errors exit non-zero; advisory-only exits zero. Neither mode
writes to disk, except review --fix on a local-path target (CLI-132).
Running unattended / in CI
Pass --yes (-y) to skip confirmation prompts. Without it, any command that
would prompt on a TTY instead exits non-zero with ConfirmationRequired when
stdin is not a TTY (CLI-23, CLI-42).
Install and uninstall hooks are skipped in non-TTY contexts and a note is
printed. To run them unattended, pass --dangerously-skip-install-hook-check.
This executes arbitrary code from the source; only use it for sources you trust
(HOOK-22).
dump
mind dump writes a super-source mind.toml to stdout (or --output <path>)
that reproduces the current melded and installed state. Melding the output
recreates the same source set at the same revisions. It is the inverse of
melding a curated super-source.
mind dump # write to stdout
mind dump --output snapshot.toml # write to a file
mind dump --whole-sources # include all items, not just installed ones
Each entry in the emitted [discover].sources references a melded source and
pins it to its currently recorded commit as a pin-ref, overriding any pin the
source itself declares (DUMP-1). The meld-time settings are carried through:
prefix (as), scan roots, and the resolved commit pin (DUMP-4).
Item filtering. By default each source entry is stamped with the install directive that reproduces exactly which items are installed (DUMP-2):
- Every offered item installed:
install = true - No items installed:
install = false - A subset installed:
install_items = [...]listing those items bykind:name
--whole-sources disables this filtering and emits install = true for every
source, offering the full catalog instead of the recorded subset (DUMP-3).
With no melded sources, dump emits a valid super-source with an empty
[discover].sources and exits 0 (DUMP-8).
Interactive TUI
mind probe opens an interactive terminal UI: a browsable, searchable view of
every source and item, and the interactive front end for the rest of the CLI
(meld, learn, sync, upgrade, config). For the probe verb and its flags, see
Commands.
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-tui(short-n) is 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 Unmanaged items.
Under each group the hierarchy is source -> kind (skills, agents, rules) -> 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. 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.
- Sync / upgrade: sync all or the selected source; upgrade pending or the selected items, showing the same deltas and confirming before applying.
- Config lobes: view and manage agent homes (list / add / remove).
An install preview arms the same dependency closure as the direct install action. See Dependencies.
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, and the description. It offers Install when the item is not installed, else Forget.
- For a source: its name and installed/available item counts, and Install all available items, Uninstall all installed items, and Unmeld. An action is omitted when it would do nothing.
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.
Configuration
Agent homes (lobes)
learn links items into every configured agent home (a lobe). Each item is
linked under its kind subdirectory: skills/, agents/, rules/. The default
lobe is ~/.claude. Configure more in ~/.mind/config.toml:
lobes = ["~/.claude", "~/.config/some-other-agent"]
The file is created with the default lobe (~/.claude) on first use. For a single
invocation, set MIND_AGENT_HOMES to a :-separated path list instead.
Lobe precedence (STO-14): MIND_AGENT_HOMES wins over lobes in
config.toml, which wins over the default ~/.claude. An unknown key in
config.toml is a hard error.
Use mind config lobes add <path> and mind config lobes remove <path> to
manage lobes without hand-editing the file; see Commands for the
full verb list.
Kinds filter
A lobe may carry a kinds list so only items of the listed kinds link into it.
A lobe without a kinds field receives all kinds (existing behavior; a bare string
is equivalent):
lobes = ["~/.claude", { path = "~/.gemini/config", kinds = ["skill"] }]
Linking an item into a lobe whose kinds excludes its kind is a no-op for that
lobe, not an error. The manifest records only the lobes that actually received a
link, so forget, upgrade, and introspect never expect a link a filtered lobe
was never given (HARN-1, HARN-2).
Cross-harness lobes
Skills (skills/<n>/SKILL.md) and agents (agents/<n>.md) use layouts that are
now cross-harness conventions. mind links them verbatim – no content transform is
needed. Rules (rules/<name>.md) have no cross-harness directory equivalent (the
analog in other harnesses is a single concatenated context file like AGENTS.md
or GEMINI.md, not a directory of per-rule files), so rules are Claude-only and
are never linked into a lobe added via a non-Claude preset (HARN-3).
Per-harness path table:
| Harness | Skills dir | Agents dir | mind lobe (parent) |
|---|---|---|---|
| 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 |
~/.agents is a vendor-neutral alias: Codex reads it as its user skills path, so
one ~/.agents lobe serves Codex and any harness that follows the same convention.
Presets
mind config lobes add --preset <name> adds a lobe with the preset’s path and
kinds in one step. Presets:
| preset | path | kinds |
|---|---|---|
gemini | ~/.gemini/config | skill |
codex | ~/.agents | skill |
universal | ~/.agents | skill |
Example:
mind config lobes add --preset gemini
# + added gemini lobe ~/.gemini/config [skill]
Note (migration): The
geminipreset path changed from~/.geminito~/.gemini/configin a previous release. If you added this preset earlier, your~/.mind/config.tomlmay still have the old path. Update it by running:mind config lobes remove ~/.gemini && mind config lobes add --preset geminiOr hand-edit
~/.mind/config.tomland replace~/.geminiwith~/.gemini/config.
mind config lobes detect detects which known harness homes exist on the machine
and reports the matching presets it could add. It never mutates config on its own:
it only adds a lobe with --yes or an interactive TTY confirm. --json emits the
detection result as structured JSON (HARN-5).
mind config lobes list shows the kinds filter for each lobe (e.g.
~/.gemini/config [skill]); a lobe with no filter shows just the path. mind config show uses the same format.
Frontmatter portability
mind links skill and agent files verbatim. Frontmatter portability across
harnesses – for example, Gemini’s mcp_* tool-permission wildcards vs Claude’s
tools: schema – is the author’s responsibility; mind does not rewrite
frontmatter to fit a target harness (HARN-6). An item whose frontmatter uses
Claude-specific keys will link into a Gemini or Codex lobe correctly, but those
keys may be ignored or produce a warning in the target harness.
Absorb destination
mind absorb moves an unmanaged item into a version-controlled source you own.
The destination source is resolved from three places, in order – the first one
set wins:
--to <path>flag on the command line.MIND_ABSORB_TOenvironment variable.absorb_tokey in~/.mind/config.toml.
When none of the three is set and the run is interactive, absorb prompts and
offers ~/.mind/personal as the default. That directory is created and
git init-ed on demand if it does not exist. After an interactive resolution,
absorb offers to save the chosen path as absorb_to in config.toml so
future runs skip the prompt. A --to flag, MIND_ABSORB_TO, or an existing
absorb_to value is used as-is and never triggers a save.
A non-TTY run with no destination configured (none of the three sources set) is an error; there is no silent default to assume.
Set the persistent default in ~/.mind/config.toml:
absorb_to = "~/dev/my-agent-library"
~ is expanded at use. The destination must be a git repository; a path that is
not a git repo is an error.
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.
Config example
A single ~/.mind/config.toml may contain any combination of the keys:
lobes = ["~/.claude", { path = "~/.gemini/config", kinds = ["skill"] }]
ssh = true
absorb_to = "~/dev/my-agent-library"
Paths
~/.mind/
config.toml persistent settings (lobes, ssh, absorb_to)
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 CLAUDE_HOME (the
default lobe).
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 (STO-40..43).
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 (LIFE-1..4).
A prefix change (adding or removing --namespace <prefix> on a source) causes upgrade
to report rename old -> new and is handled the same way: the new name is
installed before the old one is removed (LIFE-14). This is normal, not an error.
For diagnosing a failed install or broken links, see Troubleshooting.
Unmanaged items
An agent home (lobe) often holds skills, agents, and rules that mind did not
install: files written by hand or placed by another tool. mind surfaces these
as unmanaged items so the listing reflects everything the agent actually sees,
and lets forget remove one after a distinct confirmation.
Tools are store-only (not linked into a lobe by default), so unmanaged detection covers skills, agents, and rules only (spec UNM-1).
What counts as unmanaged
A lobe entry is unmanaged when its path is not recorded in any manifest item’s
links (spec UNM-1). A managed symlink and an unmanaged file cannot occupy the
same path simultaneously; a given path is always one or the other.
mind scans every configured lobe. An item present in more than one lobe is one
logical item that records each occupied path.
Listing unmanaged items
recall
mind recall (no arguments) lists unmanaged items after the melded sources,
in a group labeled “unmanaged: not installed by mind”, one row per item showing
kind:name and the lobe path(s) it occupies (spec UNM-2). No flag is needed to
see them; --kind filters them the same way it filters managed items; --source
excludes them because they have no source.
recall --json covers managed sources only (its schema is unchanged); unmanaged
items are exposed machine-readably through probe --json (spec UNM-2, UNM-3).
probe
mind probe includes unmanaged items in its listing and substring search
(spec UNM-3). In the non-interactive listing each unmanaged row is labeled. In
--json output each carries "unmanaged": true with no source or hash field.
--kind filters; --source excludes them.
In the interactive TUI, unmanaged items appear under an Unmanaged group node, browsable and searchable like a source’s items (spec UNM-6). See Interactive TUI.
Forgetting an unmanaged item
mind forget <kind:name>
When the ref resolves to an unmanaged item, forget removes the lobe entry
itself (the file or directory the user owns) and leaves the manifest unchanged
(spec UNM-4).
Exact ref only. An unmanaged item is matched only by its exact kind:name.
A glob such as forget '*' removes managed items only and never deletes a user’s
own files (spec UNM-4).
Confirmation required. Every unmanaged removal prompts first, regardless of
count. The prompt states explicitly that the item is not managed by mind and
that removal deletes the user’s own file or directory, not a symlink (spec UNM-5).
--yesproceeds after displaying that statement.- A non-TTY run without
--yesrefuses withConfirmationRequiredand removes nothing (spec UNM-5).
The --force / clobber flags do not apply here; nothing is being overwritten.
When a bare name matches both a managed and an unmanaged item, add a kind prefix
to disambiguate (for example skill:review vs agent:review). See
Commands for the full forget verb reference.
Bulk removal of unmanaged items
mind forget --unmanaged [<ref>]
--unmanaged scopes removal to unmanaged lobe items only – the deliberate
opt-in inverse of the default, where a glob matches managed items only.
Managed (mind-installed) items are never matched in this mode; it cannot
delete a mind-installed link or store copy.
Ref forms.
- With a glob
<ref>(e.g.'skill:*'), every matching unmanaged item is removed. A kind prefix composes with the glob. - With an exact
kind:name, that single item is removed. - With no
<ref>, every unmanaged item across all configured lobes is removed.
A <ref> that matches no unmanaged item is an error.
Confirmation. Before removing anything, mind lists all matched items and
asks once to confirm, making clear these are not managed by mind and that
removal deletes the user’s own files or directories (not symlinks). --yes
(-y) skips the prompt. A non-TTY run without --yes refuses with
ConfirmationRequired and removes nothing.
The manifest is not touched; these items were never in it. The --force /
clobber flags do not apply.
absorb
mind absorb <ref> claims a single unmanaged lobe item (skill, agent, or rule)
into a version-controlled source and installs it as a managed item. It is the
constructive inverse of forget --unmanaged: instead of deleting an unmanaged
file, absorb moves it into a source you own, commits it there, and replaces
the lobe entry with a managed symlink. After absorb the item participates in
sync, upgrade, and forget like any installed item.
See Unmanaged items for how unmanaged items are detected and listed.
Basic usage
mind absorb skill:my-skill
mind absorb agent:my-agent
mind absorb rule:my-rule
mind absorb my-item --to /path/to/my-source
The ref must resolve to exactly one unmanaged item. A glob ref is an error; bulk absorb is not supported. A kind prefix disambiguates when the same name appears in more than one kind.
Tools are never absorbed: they are store-only and never appear as unmanaged lobe items (spec ABS-1).
Choosing the destination
The destination source is resolved in this precedence order:
--to <path>– the explicit destination flag.MIND_ABSORB_TOenvironment variable.absorb_tokey in~/.mind/config.toml.
The first one set wins; later sources are not consulted (spec ABS-2).
When none of the three is set and the run is interactive, absorb prompts for a
destination and offers ~/.mind/personal as the built-in default. That directory
is created and git init-ed on demand if it does not already exist. After a
prompted choice, absorb offers to save it as absorb_to in config.toml so
future invocations skip the prompt (spec ABS-3, ABS-4).
A non-interactive (non-TTY) run with no destination configured is an error
(ConfirmationRequired) and changes nothing (spec ABS-3).
When --yes is given on a TTY and no destination is configured, absorb
automatically uses (and persists) ~/.mind/personal. On a non-TTY this does not
apply: with no destination configured the run errors regardless of --yes (see
Running unattended below).
Flags
| flag | effect |
|---|---|
--to <path> | Destination source directory (highest precedence). |
-f / --force | Overwrite the destination convention path if a kind:name collision already exists there. Without --force, a collision is an error and nothing is changed (spec ABS-6). |
The global -y / --yes flag skips the [y/N] confirmation described below.
Confirmation and multi-lobe cleanup
An unmanaged item may occupy more than one lobe. absorb takes the content from
one occupied path; because learn relinks the item into every configured lobe
(spec STO-14), the other unmanaged copies must be removed so the managed link can
take their place.
Before acting, absorb lists what it will move and which stray copies it will
delete and prompts [y/N] once. This covers deleting files the user owns, so
the prompt runs even when only one lobe is affected (spec ABS-7).
--yes(-y) skips the prompt and proceeds.- A non-TTY run without
--yesrefuses withConfirmationRequiredand changes nothing. - Declining the prompt leaves the original lobe entry in place and the manifest unchanged.
What absorb does internally
- Resolves
<ref>to a single unmanaged item (ABS-1). - Confirms the destination source is a git repository. The
--to/MIND_ABSORB_TO/absorb_topath must already be a git repo; a non-repo path is an error.~/.mind/personalis the one pathabsorbwill create andgit initon demand (spec ABS-5). - Lists the move and any stray copies to delete; prompts
[y/N](skipped with--yes). - Moves the item into the destination source at its convention path
(
skills/<name>/,agents/<name>.md,rules/<name>.md). Melds the source first if it is not yet registered. - Stages and commits the moved item in the destination repo with the message
absorb <kind>:<name>(spec ABS-5). - Runs
learnon the item; the lobe path is now a managed symlink. - Records the item in the manifest keyed
kind:effective-namewith the destination source as its source (spec ABS-8).
A failure at any step before learn completes leaves the original lobe entry in
place and the manifest unchanged. A failed absorb never loses the user’s file
(spec ABS-10).
After absorb
The item’s effective name follows the destination source’s prefix when one is in
effect (--namespace or [source].prefix in mind.toml). It then appears in
mind recall, participates in mind upgrade and mind sync, and can be removed
with mind forget <kind:name> like any managed item (spec ABS-8).
Running unattended
Pass --yes (-y) to skip all prompts. In a non-TTY context without --yes,
absorb refuses at any point that would normally prompt (destination resolution
or the multi-lobe confirmation). You must also supply a destination via --to or
MIND_ABSORB_TO or absorb_to in config, because a missing destination in a
non-TTY context is itself an error (spec ABS-3).
Install 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,
"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 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.
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).
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-install-hook-check runs them unattended (the flag name is
reused deliberately). A required uninstall hook that fails or is aborted leaves
the source melded.
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 (install and uninstall), showing each hook’s command, event, and
whether it is required or optional.
[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.
Managed policy (enterprise)
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 (POL-1):
| OS | Path |
|---|---|
| Linux | /etc/mind/policy.toml |
| macOS | /Library/Application Support/mind/policy.toml |
| Windows | %PROGRAMDATA%\mind\policy.toml |
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 (POL-2).
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 (POL-2).
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 (POL-3).
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 (POL-4).
A policy file that does not parse, has unknown keys, or violates an internal rule
is a hard error on every command (fail closed), naming the problem; mind does
not silently fall back to unmanaged (POL-5). Validate a 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
# 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.
[[sources.auto_meld]]
repo = "acme/agent-baseline"
tag = "v1.4.0"
[[sources.auto_meld]]
repo = "https://github.example.com/platform/security-rules"
ref = "9f3a1c2e7b1d0a4c5e6f8a9b0c1d2e3f40516273"
[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"]
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 (POL-10).
[sources].lock is the enforcement switch:
- With
lock = true,meldrefuses any repo whose identity does not matchallow(SourceNotAllowed); nothing is cloned or registered (POL-11).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 (POL-12). - With
lockabsent or false,allowis advisory: a non-matchingmeldis warned about but not refused, and the verbs above are not restricted (POL-13).
Require pinned
With [sources].pinned = true, every meld must resolve to a tag or ref pin
(--pin-tag / --pin-ref, or a [source] pin directive that resolves to a tag
or ref); a floating branch (--follow-branch or the default branch) is refused
(UnpinnedSourceForbidden) (POL-20).
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 (POL-21).
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 (POL-30). 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 (POL-31).
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
(POL-32).
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
(POL-40).
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 (POL-41).
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 (POL-21), and that every auto_meld entry
satisfies allow when locked (POL-31). It reports hard errors and advisories and
exits non-zero on a hard error (POL-50).
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 Install hooks for the disclosure and prompt model that applies.
Example
A worked example is at examples/policy.
Troubleshooting
- An item didn’t show up in
~/.claude. Runmind introspect; it reports missing links and drift, andmind introspect --fixrecreates missing symlinks. learnrefused to overwrite a path. mind will not clobber a file or link it did not create (the clobber guard). Move the existing one aside, thenlearnagain.- Two sources ship an item with the same name. Namespace one with
mind meld <repo> --namespace <prefix>, so its items install as<prefix>:<name>. See examples/namespacing/. - Where things live: see Configuration. Override the
roots with
MIND_HOMEandCLAUDE_HOME. - Before publishing a source, run
mind review <path>to check itsmind.toml, item kinds,{{ns:}}references, and pin directive. See Authoring a source. - To authenticate with an SSH key, see Configuration.
- Stuck in the TUI. Press
qin the main view, or Ctrl-C twice from anywhere (search box, dialogs) to force exit. - A
mind.tomlchange is rejected as a parse error. The file is strict (deny_unknown_fields), so a misspelled key is a hard error, not silently ignored (DSC-30). Common near-misses: pin keys are hyphenated (follow-branch,pin-tag,pin-ref), not underscored;min-mind-versionlikewise uses a hyphen; and entry keys under[discover].sourcesare exact (DSC-31). Runmind review <path>to check the file before melding. - A source’s items are not discovered after setting
rootsinmind.toml.roots = [](an explicit empty list) is distinct from omitting the key: it scans zero roots and finds nothing. Omittingroots(or removing the key) keeps the default behavior of scanning the repo root (DSC-50). Set the actual subdirectory paths, or removerootsentirely. learnreportsLinkOccupiedand refuses to overwrite. The clobber guard will not replace a path that mind did not create (LIFE-41). Move the existing file aside, thenlearnagain, or passlearn --force(CLI-35) to replace it unconditionally. Note: on non-unix platforms mind cannot always recognize its own copies (symlink ownership is not detectable), so a reinstall orupgrademay reportLinkOccupiedfor items mind did install; this is a documented platform limitation (see the lifecycle.md platform note).- An item shows as out of date in
recall/probewithout an upstream change. Editing a store or source file by hand changes its content hash; mind compares source-content hashes and reports the delta as drift (LIFE-33, CLI-75). Either re-syncand thenupgradethe item, or restore the edited file to its original content. See Configuration for where store and source files live. meldorsyncfails with “git executable not found”. mind shells out togitfor all clone and fetch operations; putgiton your PATH first. See the Install page.- A skill links to a Gemini or Codex lobe but a rule does not. Rules have no
cross-harness directory equivalent and are Claude-only (HARN-3). Only skills
and agents are linked into non-Claude lobes. If the lobe was added via a
preset, this is expected; rules remain in
~/.claudeonly. - An agent’s tool permissions don’t work in Gemini or Codex after linking. mind
links files verbatim and does not rewrite frontmatter. A skill or agent whose
frontmatter uses Claude-specific keys (e.g. the
tools:allow-list schema) will link correctly but those keys may be ignored or produce a warning in the target harness. Adapt the frontmatter for the target harness by hand (HARN-6). See Configuration.
Source layout
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
tools/<name>/ a tool (the whole directory; no anchor file)
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: a single markdown file.
- 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 Tooling).
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, mapping the plugin’s skills and agents to mind items. See
Claude plugin 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 |
{{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 in the same source as the items that use it.
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). A tool is the exception: it is 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.
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, andtools/<name>/. - Frontmatter (always read): each item’s
description(and a tool’sbin/build) come from the frontmatter it already carries. - 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 plugin 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 (prefix, 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 plugin marketplaces.
[source] - repo metadata
All keys are optional.
[source]
description = "James's agent library" # shown in recall/probe
prefix = "jk" # namespace: 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
prefix: every item installs as<prefix>:<name>(identity, store path, symlink, and ref), except an agent’s harness link, which stays bare. A consumer’smeld --namespace <prefix>overrides it;meld --namespace ''removes it. See namespacing.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 --follow-branch|--pin-tag|--pin-refoverrides it.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).
[[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) or "uninstall" (on unmeld)
[[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.
[[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 | tool (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
uninstall = "./teardown.sh" # any kind: host cleanup run before removal
binandbuildare valid only on atool; on any other kind they are a schema error.install/uninstallare per-item lifecycle hooks (valid on any kind), distinct frombuild(which produces the item’s content) and from the source-level[[hooks]].
[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"] }
tools = { include = ["packages/*/tool"] } # globs match the tool DIRECTORY
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.
[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", as = "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).as: impose a namespace prefix on that nested source.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.
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 (DSC-59/60/61)
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 (DSC-41). A consumer’s explicitmeld --follow-branch,--pin-tag, or--pin-refstill overrides this.roots: convention scan roots for the nested source, for a monorepo or subtree layout (DSC-50).[[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).
These fields apply ONLY when the nested source ships no mind.toml. If the
nested source has a mind.toml, that file is authoritative for its pin, roots,
and hooks, and the curator-supplied values are ignored (a warning is emitted). The
gate is whole-file: a nested mind.toml, even one that does not declare a
pin/roots/hooks, suppresses all three. as and install are 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 (DSC-60).
[[discover.sources]]
source = "owner/unonboarded"
follow-branch = "main" # track this branch (DSC-41)
roots = ["packages/agents"] # scan under this subdir, not the repo root (DSC-50)
[[discover.sources.hooks]] # build hook, same shape as [[hooks]] (HOOK-50)
run = "make build"
name = "build tooling"
event = "install"
Scenarios
A regular source
A repo that ships its own skills, agents, rules, and tools. The simplest form is
no mind.toml at all (pure convention). Add a mind.toml to attach metadata or a
prefix:
[source]
description = "James's agent library"
prefix = "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", as = "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"
prefix = "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", as = "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.
Authoring a source
A source is a git repo. Publishing one is mostly “lay the items out and push”;
mind melds arbitrary repos with no config, so there is no manifest to write and
nothing to register.
Publish in two minutes
A repo with a skills/ directory is already a source. Nothing else is required.
myrepo/
skills/greet/SKILL.md # a skill (the directory is the item)
agents/reviewer.md # optional: an agent
rules/style.md # optional: a rule
Push it, and anyone can meld it:
mind meld owner/myrepo # discovers the items by convention
mind probe # they show up, ready to learn
See examples/starter for a minimal repo of this shape and Source layout for the full convention.
Flat skill layout
If your repo keeps skill directories at the root with no skills/ container
(<repo>/greet/SKILL.md), set [source].flat-skills so convention discovery
finds them:
[source]
flat-skills = true
A consumer can also force it at meld time with mind meld owner/repo --flat-skills (useful for adopting a flat-layout repo that has not set the flag
itself). It applies to skills only; agents, rules, and tools keep their
agents/ / rules/ / tools/ directories. See DSC-74..77.
A Claude plugin or marketplace
A repo published for Claude Code’s plugin system is meldable as-is: a
.claude-plugin/plugin.json or .claude-plugin/marketplace.json is read as a
discovery source, no mind.toml needed. See Authoring a plugin or
marketplace.
Preparing a source
Two commands help a maintainer prepare a repo for melding: init-source
scaffolds and reports, review validates.
init-source
Run mind init-source in the repo. It discovers the items the repo offers
(exactly as melding would), reports the references among them, scaffolds a
mind.toml if none exists, and surfaces advisories:
mind init-source # report + scaffold
mind init-source --template # also rewrite bare sibling refs to {{ns:}}
It is read-only except for creating an absent mind.toml and, with
--template, editing item files. It registers nothing and touches no agent home.
review
mind review <target> validates a source for publishing without changing
anything (--fix is the one exception). <target> is a melded source name, a
local path, or a repo spec; with no target it reviews the current directory.
Findings are hard (non-zero exit) or advisory:
- hard: a malformed
mind.toml, an unknown item kind, amin-mind-versionthe runningminddoes not meet, or an unresolved{{ns:}}/{{self}}/{{tools:}}/{{path:}}token. - advisory: a missing description, a hardcoded install path (
hardcoded-path, classified by what it resolves to), a sibling tool named in prose without a token, a misplaced{{ns:}}token, a helper script duplicated across items (duplicate-tooling), a deprecated[source].installfield (deprecated-field, pointing to the[[hooks]]form), and each declared install/uninstall hook (so a consumer sees the source will run code).
mind review # review the current dir
mind review owner/repo # review a melded source or repo spec
mind review . --fix # rewrite confidently-mapped findings in place
--fix rewrites a local working tree only: recognized hardcoded paths become
tokens, misplaced {{ns:}} tokens are un-wrapped, and bare sibling names are
templatized. Structural advisories like duplicate-tooling are left for you to
resolve by hand.
Resources and tooling
Where an item’s resources and shared tooling belong (bundled with {{self}}, put
in a known location by an install hook, or shared through the store as a tool
item) is covered in Source layout. mind review’s
duplicate-tooling, bare-tool-reference, and hardcoded-path findings are
advisory: each of those layouts is valid.
Namespacing
Most sources give their items unique, descriptive names and are never prefixed, so
this does not come up. A prefix exists only for the collision case: two sources
that both ship a review would land at the same path, so a prefix namespaces one
(<prefix>:<name>). The effective prefix is, in order: the consumer’s
meld --namespace <prefix>, the repo’s [source].prefix, else none.
A prefix renames items, so if a prefixed source’s items reference each other by
name, those references must be tokens: {{ns:name}} in prose, and the path tokens
({{self}} / {{tools:name}} / {{path:ref}}) for code and paths. mind
expands each at install. review and init-source warn (advisory) when a source
that is being prefixed references a sibling in bare prose. An unprefixed source,
or one with no intra-source references, needs none of this. Agents are the
exception: they link under their bare name even under a prefix, so a reference to
a sibling agent is not renamed and does not warn.
See the spec for the normative rules and examples/namespacing for a worked source.
Namespacing
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 (NS-1, NS-2)
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 (NS-3). 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, and tools but not an agent’s harness identity
(NS-40, NS-41, NS-42). 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
(NS-40). 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 (NS-41). 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 (NS-42). 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 (NS-10, NS-11)
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 (NS-14), 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 (NS-12, NS-13)
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 (NS-13).
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 (NS-15).
Scope: prose only (NS-24)
{{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}}, {{path:ref}}
described in Tooling) instead of {{ns:}}.
mind review flags misplaced {{ns:}} tokens (CLI-139); init-source --template does not create them (INIT-5).
Unguarded-reference warning (NS-20 to NS-23)
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 (NS-20).
Matching is whole-word (alphanumeric, _, and - are word characters); an
item’s own name is not reported against itself (NS-21). 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 (NS-42). The warning is
advisory and does not fail meld; it does not rewrite anything (NS-22). No
warning is emitted when no prefix is in effect (NS-23).
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
Authoring a source, which also describes how init-source and
review surface advisories when a prefixed source’s items reference each other in
bare prose.
Claude plugin 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 and agents 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 and agents 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/, 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: when a plugin declares components mind
cannot represent, meld prints a count of what it skipped, for example
2 hooks, 1 mcp server not installed (no mind equivalent). You are never left
believing the plugin installed in full when part of it was dropped.
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.
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 and agents/<name>.md next to it, same as any
mind source (Source layout). commands/, 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),
and unsupported
commands//hooks/that report a skipped count.
- a single plugin: one skill (namespaced by the plugin name), one agent (bare),
and unsupported
- examples/marketplace-catalog
- a catalog listing two in-repo plugins, each with its own name and items.
The normative behavior is spec/marketplace.md (MKT-1..11).
Tools and path tokens
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 (TOOL-1..7)
A tool is a fourth item kind alongside skill, agent, and rule. Its 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 (TOOL-3). A
tool can opt in to a link with an explicit link field (TOOL-4, below).
Convention discovery (TOOL-1): 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 (TOOL-3).
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 (TOOL-4).
Namespacing (TOOL-6): 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 evolve/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.
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 (TOOL-2, TOOL-5)
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 (TOOL-5) |
build | per-item build command run in staging at install (HOOK-70..73) |
The entrypoint resolution order is (TOOL-5):
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 (TOOL-7).
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 (TOOL-1). Use a
per-item build when you want an isolated, rollback-safe build that runs in
staging before the store swap. See Install hooks for the full
hook model (HOOK-70..73).
Path-reference tokens (TOOL-10..16)
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 (TOOL-13).
Tokens expand in every text file of every item kind, including tool directories
and bundled scripts (TOOL-14). Inner whitespace is trimmed ({{ path:x }} works);
an unterminated token (no closing }}) is left verbatim; non-UTF-8 files are not
scanned.
References resolve within the same source only: ship a tool in the same source as the items that use it (TOOL-15).
{{self}} (TOOL-10)
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}} (TOOL-12)
Expands to a sibling tool’s entrypoint: the tool’s store directory joined with
its resolved bin (TOOL-5). 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}} (TOOL-11)
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 (TOOL-16)
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`
After mind learn scan:
{{tools:detect}}expands to~/.mind/store/tool/detect/detect.sh– the entrypoint frombin:(TOOL-5, TOOL-12).{{path:tool:detect}}expands to~/.mind/store/tool/detect– the store directory, for non-entrypoint files (TOOL-11).{{self}}expands to~/.mind/store/skill/scan– the skill’s own store directory (TOOL-10).
All three use tilde syntax so a Bash(~/.mind/store/**) permission rule covers
them (TOOL-16).
Under a prefix (meld --namespace jk), {{tools:detect}} expands to
~/.mind/store/tool/jk:detect/detect.sh – no edits to the skill needed.
Within-source dependency resolution
Items in a source can reference siblings via {{ns:name}} tokens (see
Namespacing). 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 the item’s text files (the whole
skill directory, or the agent/rule file) names a sibling as a dependency. This is
the inline form: the reference lives in the prose and is also rewritten to the
effective name on install. See Namespacing 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.
Dependencies never cross sources; both {{ns:}} tokens and requires: entries
are always resolved within the one source.
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 (DEP-10). 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 (DEP-11, DEP-21, DEP-30).
What you see before install
When the closure adds items beyond your explicit selection, mind prints the
dependency tree and prompts before changing anything (DEP-31):
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
(DEP-23). Cycles are shown as marked back-edges rather than expanded again
(DEP-22).
--yes (or answering y) confirms without prompting. See
Commands - learn 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 (DEP-32):
mind learn --dry-run skill:review
Interactive TUI (probe)
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 (DEP-40, DEP-41).
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 (DEP-50).
init-source
mind init-source [path] helps a source author prepare a repo for melding. It
scaffolds a mind.toml, reports the intra-source reference graph it detects,
and optionally rewrites bare sibling references into {{ns:}} tokens. It is
the setup counterpart to review (which validates without changing anything).
What it does
Run mind init-source in the repo (or pass a path):
mind init-source # report + scaffold
mind init-source --template # also rewrite bare sibling refs to {{ns:}}
mind init-source path/to/repo
On each run it:
- Discovers items exactly as melding would: convention scanning
(
skills/<n>/SKILL.md,agents/<n>.md,rules/<n>.md,tools/<n>/), then an authoritativemind.tomlif one is present. What it reports is what melding would install. - Scaffolds a
mind.tomlwhen none exists (INIT-3). The scaffold contains a[source]table with adescriptionplaceholder and a commented-outprefix. An existingmind.tomlis never overwritten. - Reports the intra-source dependency graph (INIT-4): for each item, the
siblings it references via existing
{{ns:name}}tokens, and the siblings it mentions in bare prose. Bare-prose mentions are emitted asunguarded-referenceadvisories in the sameadvisory [kind]: messageformat thatreviewuses, and only when a prefix is in effect (see below). - Reports
duplicate-toolingadvisories for any non-markdown helper file that is byte-identical across two or more items (INIT-7), in the same finding format asreview. These are advisory only;--templatedoes not fix them. Extracting a shared tool intotools/<name>/is a structural change made by hand.
--template: rewriting bare references
With --template, init-source rewrites each bare whole-word sibling mention
in item text to its {{ns:name}} token (INIT-5), writes the changed files, and
reports each rewrite.
What is rewritten:
- Bare whole-word occurrences of a sibling item’s name in prose.
What is left alone:
- Text already inside a
{{ns:}}token. - Names inside a fenced code block, an inline code span, a path, or a frontmatter structured field. A keyword or path component is never wrapped.
The rewrite is heuristic: a sibling’s name can be an ordinary English word, so
the result should be reviewed (e.g. with git diff) before committing.
See Namespacing for how {{ns:name}} tokens work and when
they are needed.
Bare-reference advisories
init-source emits unguarded-reference advisories only when an effective
prefix is in force for the repo ([source].prefix in mind.toml) (INIT-9).
Without a prefix, bare sibling references resolve as written at runtime, so
flagging them would be noise. This matches meld and review, which also
suppress this advisory on unprefixed sources.
{{ns:}}-token edges in the dependency graph and the --template rewrite are
unaffected by this gate; only the advisory is gated.
Safety contract
init-source makes no network calls and does not read or write the store or
any agent home (INIT-6). Without --template it is read-only except for
creating an absent mind.toml. With --template it edits item files in the
target repo only.
Broader workflow
init-source sets up the repo; review validates it before publishing. The
full authoring workflow is in Authoring a source.