mind-cli 0.45.0

A manager for agent tooling (skills, agents, rules, tools) that melds arbitrary git repos and links items into your agent directories.
# Install profiles

An install profile is a named set of a source's items, declared by the source
maintainer in `mind.toml` and selected by the consumer at `meld` or `learn`. The
model is cargo's feature flags: profiles compose by including one another, a
`default` profile applies unless the consumer opts out, and naming a profile adds
to `default` rather than replacing it.

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

```sh
mind meld owner/repo                        # installs basic (via default)
mind meld owner/repo --profile full         # default + full
mind meld owner/repo --no-default-profile --profile full
mind learn owner/repo --profile full        # enable full on an already-melded source
mind learn owner/repo --profile             # list the source's profiles
mind recall --profiles owner/repo           # same, with each profile's install state
mind forget owner/repo --profile full       # disable it again
```

## Declaration

- `PROF-1` A source declares profiles in a `[profiles]` table in its root
  `mind.toml`. Each key is a profile name; each value is an array of members.
  `[profiles]` is metadata, like `[source]`: it does not make the `mind.toml`
  authoritative, and it composes with whatever discovery is in effect
  (convention, `[[items]]`, `[discover]`, `--add-root`, a plugin manifest's own
  items, MKT-15). A member can only reach an item the source offers, so a
  profile never widens the source's export control.
- `PROF-2` A profile name matches `[a-z0-9][a-z0-9_-]*` and is at most 64 bytes.
  Any other name, including one carrying `:`, is a `MindToml` error. The syntax
  rule is what makes a profile name safe to print unsanitized and lets PROF-3
  tell a profile member from an item member.
- `PROF-3` A member is one of:
  - an item pattern `kind:name`, where `kind` is an item kind word and `name` is
    a bare name or a glob over bare names (`agent:*`, `skill:pdf-*`),
  - `"*"`, every item the source offers,
  - a profile name (no `:`), which includes that profile's members.

  An item pattern splits at its first `:`, so `command:frontend:build` is the
  `command` named `frontend:build`. Item patterns are in source truth: a namespace in effect for the source is
  applied at install (as DSC-63 does for `install-items`), never written in the
  profile. A member with an unknown kind word, or a profile-name member that is
  not a key of the table, is a `MindToml` error.
- `PROF-4` Profile inclusion is acyclic. A cycle, including a profile that lists
  itself, is a `MindToml` error whose message names the cycle (`full -> basic ->
  full`).
- `PROF-5` A profile's expansion is the union of its item patterns' matches and
  the expansions of the profiles it includes. An exact item pattern (no glob
  metacharacters) naming an item the source does not offer is `BadReference`
  when the profile is expanded, at `meld`, `learn`, `upgrade`, or `curate`; it
  cannot be caught at parse time, since it needs the catalog. A glob pattern
  that matches nothing is not an error, so a source can keep `agent:*` before it
  ships an agent; `review` reports it (PROF-30).
- `PROF-6` Installing a profile installs each item of its expansion through the
  ordinary `learn` path, so the dependency closure (DEP-30/31) and the collision
  checks (CLI-33) apply unchanged. A closure item is not a profile member; it is
  installed on behalf of the member that pulled it in and carries that member's
  install reason (PROF-20).
- `PROF-7` The `default` key names the profile that is enabled when the consumer
  names none and does not opt out (PROF-10). Its value follows PROF-3. A source
  whose `[profiles]` has no `default` keeps the CLI-23 install-all offer, and its
  profiles are opt-in only (a flag, or the PROF-13 picker).
- `PROF-8` Profiles are read from the root `mind.toml` only. A scoped item
  `mind.toml` (HOOK-131) declaring `[profiles]` is a `MindToml` error, and a
  plugin or marketplace manifest declares none. An item-link instance
  (spec/item-link.md) is a single item, so `--profile`, `--no-default-profile`,
  and `--all-profiles` on an item link are a usage error.

## Selection at meld and learn

- `PROF-9` `meld <repo> --profile <NAME>` enables the named profile in addition
  to `default`. The flag is repeatable and accepts a comma-separated list. The
  CLI-23 install offer then covers the expansion of the enabled set instead of
  the whole source, with the CLI-23 gate unchanged: one preview and one prompt on
  a TTY, `--yes` installs without prompting, a non-TTY run without `--yes`
  installs nothing and prints how to install later.
- `PROF-10` `--no-default-profile` removes `default` from the enabled set. With
  no `--profile`, nothing is offered for install: the source is registered with
  no profile enabled, and `meld` names `mind learn <source> --profile <name>` as
  the way to install later.
- `PROF-11` `--all-profiles` enables every declared profile. An item in no
  profile is still not installed by it; `learn --all` (CLI-36) remains the
  whole-source form. `--profile` alongside `--all-profiles` is accepted and
  redundant, and `--all-profiles` enables `default` even alongside
  `--no-default-profile`.
- `PROF-12` When the source declares `default`, a plain `meld` (no profile flag)
  offers the `default` expansion instead of every item. The preview names the
  enabled profiles, lists the other declared profiles, and names `--profile` /
  `--all-profiles`, so the consumer sees what is not being installed.
- `PROF-13` An interactive first `meld` with no profile flag, no `--learn`, and
  no `--yes`, of a source that declares profiles but no `default`, asks which
  profiles to enable before the install preview. Typed names are the enabled
  set, the same as `--profile <names>`, and the install offer covers their
  expansion (PROF-9). An empty answer keeps the CLI-23 install-all offer
  (PROF-7). An undeclared name is `ProfileNotFound`, not a re-prompt. A source
  that declares `default` is never asked: its plain `meld` goes straight to the
  PROF-12 preview.
- `PROF-14` `--profile` values are checked against PROF-2's syntax before the
  clone, so a malformed name (`InvalidProfileName`) does not leave a registered
  source behind. A well-formed name the source does not declare is
  `ProfileNotFound`, raised after the clone, whose message lists the declared
  profiles or says the source declares none. As with an unmatched `--learn`
  pattern (CLI-236), the source stays melded and nothing from it is installed.
- `PROF-15` `--profile` and `--all-profiles` compose with `meld --learn`
  (CLI-236): the install batch is the union of the profile expansion and the
  `--learn` matches, under one preview and one prompt. Both conflict with
  `--register-only`.
- `PROF-16` A re-meld (CLI-12) honors the profile flags, adding to the recorded
  set (PROF-19). On a profile-tracked source the selection is the recorded set
  (names still declared) plus the flags; `default` is not re-added unless it is
  recorded or named, so an earlier `--no-default-profile` holds. A re-meld never
  disables a profile; that is PROF-25.
- `PROF-17` `learn <source> --profile <NAME>` installs the named profile's
  expansion for an already-melded source and adds it to the recorded set.
  `<source>` is a source selector as in CLI-36. `learn <source> --all-profiles`
  does the same for every declared profile. `learn` enables only what it names:
  it never adds `default`, and `--no-default-profile` on `learn` changes nothing
  (it prints a note). `--all` conflicts with `--profile` and `--all-profiles`.
  The gate is `learn`'s, including the dependency-closure confirmation and
  `--dry-run`.
- `PROF-18` Under `--json`, the `meld` and `learn` result objects carry a
  `profiles` array of the enabled profile names. `pending_items` (CLI-156)
  counts the union of the expansion and its closures, each item once.

## Recording

- `PROF-19` A source installed through a profile is *profile-tracked*: the
  registry entry records the enabled profile names (`default` by name, so a
  later change to its members is tracked like any other) and a snapshot of the
  bare `kind:name` keys the enabled set expanded to when last applied. A source
  with neither field, including every source registered before this feature and
  one installed by the CLI-23 install-all offer, is not profile-tracked and none
  of PROF-21..24 applies to it. Both fields are optional additions to
  `sources.json` and need no schema bump (STO-50).

  The enabled set is recorded only when the batch is applied (`--yes`, a
  confirmed prompt, or nothing left to install, in which case it is recorded
  without a prompt). A declined or non-TTY `meld` records nothing and leaves the
  source untracked. Only keys that actually installed enter the snapshot, so a
  member that failed, or that managed policy skipped, is offered again later.
- `PROF-20` A manifest entry installed by a profile (as a member or as a closure
  item, PROF-6) records the profile names it was installed for. A `learn` of the
  item by an exact (non-glob, non-`--all`) ref marks it explicit, keeping the
  profile names, and so does enabling a profile over an item that was already
  installed by hand. The reasons survive a content upgrade and a rename. PROF-25
  reads these reasons to decide what disabling a profile removes.

## Tracking

PROF-21..24 run in a source-scoped upgrade pass: plain `upgrade`, `upgrade
--source <filter>` (CLI-244), `sync [source] --upgrade`, and `curate`'s upgrade
change (CUR-6). They do not run for `upgrade <item>` (with or without
`--source`) or the TUI's confirmed-key apply, since an addition is never an item
the user named.

- `PROF-21` `upgrade` re-expands each profile-tracked source's enabled set
  against the current catalog. An item in the new expansion and not in the
  snapshot is pending: the upgrade report lists it as an addition naming its
  profile, and applying the upgrade installs it under `upgrade`'s gate. An
  addition the consumer declines stays out of the snapshot and is offered again
  next time. `--skip-new` and `--ignore-new` apply to additions (LIFE-55).
- `PROF-22` An item in the snapshot that is no longer in the expansion is
  reported once as having left its profile and removed from the snapshot. It is
  never uninstalled by `upgrade`; the report names `mind forget <key>`
  (shell-quoted, CLI-225) when the item was installed only for profiles. The
  departure is persisted even when the upgrade itself is declined.
- `PROF-23` An item the consumer `forget`s stays in the snapshot, so `upgrade`
  does not offer it again while it remains a member. Only a member the
  maintainer adds after the snapshot was taken is offered. This differs on
  purpose from a curator's `install` (CUR-4), which re-proposes any declared
  item that is not installed.
- `PROF-24` An enabled profile the source no longer declares is removed from the
  recorded set with a warning naming it, and its former members are reported
  per PROF-22.

## Disabling

- `PROF-25` `forget <source> --profile <NAME>` removes NAME from the recorded set
  and forgets each installed item whose recorded reasons (PROF-20) are only
  profiles that are no longer enabled and that is not marked explicit. An item
  another installed item depends on is kept and named (DEP-60), and keeps its
  disabled reason so it is not later mistaken for a hand-installed item. An
  explicit item that loses all its profile reasons stays installed as explicit.
  The preview and confirmation are `forget`'s glob gate (CLI-42), which prompts
  only when more than one item would be removed. The snapshot is recomputed from
  the remaining enabled set; if that expansion fails (a maintainer's broken
  table), `forget` warns, keeps the old snapshot, and still disables the
  profile. Disabling the last enabled profile clears both recorded fields.
  Disabling a profile that is not enabled changes nothing. Disabling `default`
  is `--profile default`.

## Curators and dump

- `PROF-26` A `[discover].sources` entry may set `profiles = ["<name>", ...]` and
  `default-profile = false` as the install directive for the nested source,
  applied by the install flow exactly as PROF-9 and PROF-10 apply the flags.
  `profiles` with `install = true` is a `MindToml` error (as DSC-64 is for
  `install-items`); `profiles` with `install-items` installs the union. An
  `install = true` entry whose nested source declares `default` offers the
  `default` expansion, the same as a plain `meld` of it (PROF-12). An entry with
  `install-items` and no `profiles` keeps the DSC-62 path even when the nested
  source declares `default`. A profile name the nested source does not declare
  is `BadReference` at meld.
- `PROF-27` `curate`'s `install` change (CUR-4) covers an entry's profiles: it
  proposes the members missing from the source's snapshot (PROF-21, PROF-23),
  together with any of the entry's `install-items` that are not installed, as
  one change, and applying it records the entry's profiles on the source. An
  entry whose members are all installed but whose profiles are not all recorded
  proposes a change that only records them. A newly listed entry routed to
  profiles installs through them at registration (CUR-3). An entry whose
  profile selection is invalid (an undeclared name, or an exact member naming
  no item) is skipped with reason `profile_selection_invalid` (CUR-15) rather
  than failing the run.
- `PROF-28` `dump` emits each profile-tracked source's enabled profiles as
  `profiles = [...]`, with `default-profile = false` when `default` is not
  enabled, plus `install-items` (DSC-62) for the source's installed items that
  are marked explicit and outside the recorded snapshot. A source with only
  `default` enabled emits `profiles = []`. `dump` reads the recorded state only
  and never re-expands against the catalog. `--whole-sources` (DUMP-3) is
  unchanged and emits no profile keys.

## Display and review

- `PROF-29` `probe --no-tui` and `recall --sources` list each source's declared
  profiles and mark the enabled ones; a source with no profiles adds nothing.
  `--json` carries `profiles` as an object of profile name to its declared
  members, plus an `enabled_profiles` array: on each source object for `recall
  --sources` (where `profiles` therefore means the declared table, not the
  recorded enabled list), and on each item row for `probe`, whose rows of a
  source without profiles carry neither key. `recall <item>` names the profiles
  an installed item was installed for. The `probe` TUI shows the same and
  toggles profiles from the source details dialog (TUI-78).
- `PROF-30` `review` reports PROF-2..4 violations (`invalid-profile`) and an
  exact member naming an item the source does not offer (`bad-profile-member`,
  PROF-5) as hard findings, and a glob or `*` member matching nothing
  (`empty-profile-member`) or a profile whose expansion is empty
  (`empty-profile`) as advisories.

## Discovering profiles after meld

- `PROF-31` `recall --profiles [<source>]` lists the declared profiles of each
  melded source that declares any, or of the one source named by a CLI-36
  selector. Each profile shows its name, marked `(enabled)` when it is in the
  recorded set, its members as declared, and `<n>/<m> installed`, where `<m>`
  is the size of its expansion against the current catalog and `<n>` how many
  of those items are installed from this source. A profile whose expansion
  fails (PROF-5) shows the error in place of the count. Each profile that is not
  enabled also shows its `mind learn <source> --profile <name>` command (the
  source shell-quoted, CLI-225). With no selector and no source declaring
  profiles, it says so; a selected source that declares none is reported the
  same way. An unknown or ambiguous selector fails as for `learn <source>
  --profile` (PROF-17). `--profiles` conflicts with `--sources`, `--tree`, an
  item argument, `--kind`, and `--source`.
- `PROF-32` Under `--json`, `recall --profiles` emits the CLI-167 envelope
  around an array with one object per listed source: `source`, and `profiles`,
  an array of objects carrying `name`, `enabled`, `members` (sanitized), and
  either `items` (the expansion's effective keys), `installed`, and `total`, or
  `error` when the expansion fails.
- `PROF-33` `learn <source> --profile` with no value, and no named profile and
  no `--all-profiles`, prints the PROF-31 listing for that source and installs
  nothing; under `--json` it emits PROF-32's document. An empty `--profile`
  value given alongside named profiles is ignored.
- `PROF-34` The profile header that `meld`, a re-meld, and `learn <source>
  --profile` print before installing (PROF-12) names the declared profiles that
  are not enabled together with `mind recall --profiles <source>` and `mind
  learn <source> --profile <name>`, so the line is still actionable after the
  meld finishes, including when nothing was installed (PROF-10).
- `PROF-35` A profile install says what it is doing before it resolves: outside
  `--json`, `meld`, a re-meld, `learn <source> --profile`, and `curate` print
  `resolving profile(s) <names> of <source> ...` first, naming the enabled set
  the install resolves. Resolution scans the catalog once
  for all members, and the members install in one `learn` pass over their
  combined closure, so the time grows with the number of members rather than
  with its square.
- `PROF-36` The PROF-31 listing prints one block per profile: a heading line
  with the name and the installed count, the counts aligned across the
  source's headings, then indented `members:`, `error:`, and `enable:` lines.
  The members and the error wrap at the output width under a hanging indent,
  breaking only between members or words; the `enable:` command stays on one
  line so it copies whole. The width is `COLUMNS` when set, else the terminal's
  width when stdout is one, else 80, and never below 40.

## Out of scope

- Per-profile hooks or profile-conditional item content. A profile selects
  items; it does not change them. A hook can read the enabled profiles from its
  environment and branch on them itself (HOOK-140).