Appearance
Install a source
What add does with each of its flags, from the everyday command to a scripted install that resolves a name collision. The CLI reference has the one-row summary of every flag; this page has the behavior behind the add and remove rows. What a source must contain is on the memory files page.
bash
npx -y @vivswan/maxims add @Octocat/rules -g --rule --add-hookThe quickstart shows what that command prints and writes. Every flag below changes one part of it.
Where it lands
| flag | destination | notes |
|---|---|---|
-g, --global | the user scope: every project on the machine | on a harness with no global target, warns and skips that harness |
-p, --project | the project scope: the git checkout you are in | the explicit opposite of -g |
| neither | a GitHub source: the project when inside one, else the user scope, as in skills | a local directory source defaults to the user scope (below) |
-o, --out <dir> | an output folder for a rule file no harness owns | neither scope; a relative path resolves against the cwd, not the project root |
--share | with -p: the source also enters the project lock for teammates | without it a project install stays yours; refused with -g or -o |
Two of -g, -p, and -o together is exit 1, "two destinations given". A team folder inside a repo is the case -o exists for. A source already installed at one scope is not moved by a re-add at another: that is exit 1 naming where it is, and remove followed by add is the way to move it.
A local directory source defaults to the user scope so personal text stays out of the repo; the security page owns what can leak.
What gets installed
| flag | selection |
|---|---|
| none | every memory in the source (*) |
-m, --memory <names> | a comma list, repeatable; * means all |
--all | every memory, every harness, no prompt |
-l, --list | a read-only preview of the source; nothing installs |
- A
--memoryname the source lacks is exit 3 with nothing written. --allis shorthand for--memory '*' --agent '*' -y. Onremoveit means every installed source, with-yspelled out, so it is the oneremovethat needs no separate-y.--listprints the "Found N memories" line and every memory name, one per line, then "Run without --list to install". It writes nothing, never touches state, and ignores--rule,--add-hook,-o, and-ywith a warning. Once a source is installed,maxims show <memory>prints one memory in full; check owns it.--listnever folds and prints no descriptions. The whole list of names is the point, in a terminal too. The install plan is the one that shows descriptions and folds in a terminal, to one entry plus... N more;--verboseshows the rest.
Re-running add with a different --memory list replaces the recorded one, shown in the plan first. It never unions.
The output of add @Octocat/rules --list, captured from the built CLI:
text
|
o Source: https://github.com/Octocat/rules.git
o Repository cloned
o Found 4 memories
|
o Available Memories
| commit-small-and-often
| never-retry-without-a-cap
| prefer-timeouts-to-hangs
| tests-before-the-fix
|
o Run without --list to installTwo separate choices
--rule and --add-hook record two different intents, and neither implies the other.
| flag | records | every later sync |
|---|---|---|
--rule | this source's one-liners go to the rule file | regenerates the rule lines; remove always removes them |
--add-hook | the harness's sync hook (and MCP server, where declared) is wanted at this install's scope | keeps the hook and the server registered there |
The hook carries no source and no filter, so the tenth source adds nothing to it. An install from . registers no hook, so an unpushed edit is never clobbered by a refresh.
The first source from an owner
The first time a GitHub or git source is added from an owner (the account on GitHub, the host and first path segment elsewhere) no installed source shares, add prints where it comes from above the plan, once, whether or not it will prompt. --json carries the same facts as provenance, null when the owner is already installed.
text
o First source from github.com/acme
https://github.com/acme/rules.git
commit 1a2b3c4, not pinned (tracks HEAD)
4 memoriesWhich harnesses
-a, --agent <ids> is a comma list, * for all, with ids from the harness matrix. Without it, the detected harnesses are used. To change one harness of an installed source without a refetch, use link or unlink; the verb notes own how they differ from add -a.
Bodies
| flag | where the body lives | for |
|---|---|---|
| none | the canonical home, linked from the destination | every setup where a symlink works |
--copy | a copy at the destination | symlink-hostile setups, as skills --copy |
--link | the store entry is a symlink to the source directory, so edits are live | local sources only; on by default for . |
--link is local-only because a fetched source has no representation for it in state.
Refs
--pin <sha or tag> tracks that ref instead of the default branch, for GitHub and git sources. It is not offered for a local directory. A GitHub URL with a /tree/<ref> segment sets the same pin, as the source forms show.
The ref is part of the source's key, so an installed source is repinned in two steps: remove it, then add it with the new --pin (or without one, to track HEAD again). A re-add with another ref collides with the installed copy on every shared name and is refused with the installed key and that order named.
Two pins of one repository install side by side when their installed names are disjoint; -m and --rename are the ways to keep them apart.
Path scoping
--paths <glob> writes the rule file in the target harness's own path-scoping syntax and is stored per source. A harness with no scoping mechanism warns and skips it.
A scoped rule loads only when matching files are touched, so it trades away "every session". It is opt-in and never a default. A harness may read such a file under a smaller limit than an always-on one (Claude Code: 40,000 characters against 4 MiB), and the writer holds a scoped install to it; the matrix shows it after the always-on budget.
Name collisions and renames
A memory's name is its identity. When an incoming memory's name is already owned by another installed source, add stops instead of skipping it, because a silently skipped rule is a rule the user believes is loaded.
text
index: commit-small-and-often -> owned by @Octocat/rules
incoming: commit-small-and-often from @example-user/dotfiles COLLISION
interactive: rename to commit-small-and-often-dotfiles; recorded in state
non-interactive: exit 6, nothing written, unless --rename names the pair
result: two rule lines, two names, one rename entry applied by every later syncThe last two lines of the output when the prompt cannot be shown, captured from the built CLI with @Octocat/rules already owning the name; above them are the ordinary progress lines of any add:
text
ERROR commit-small-and-often is owned by @Octocat/rules
Tip: --rename commit-small-and-often=<new>--rename commit-small-and-often=commit-small-and-often-dotfiles records the same entry the prompt would, so a scripted add never meets exit 6 for a collision it knows about. It repeats, one pair per colliding memory.
The rename is a local identity only. The stored file keeps its upstream name and body, the mapping lives in state, and an upstream rename of that file retires the mapping with it.
Non-interactive behavior
Detection mirrors skills, which prints "Agent detected - installing non-interactively" when run from inside an agent.
| stdout is a TTY | agent env detected | --yes | behavior |
|---|---|---|---|
| yes | no | no | full prompts: confirm the plan, confirm hook registration |
| yes | no | yes | no prompts, full output |
| yes | yes | either | no prompts, full output, "Agent detected" banner |
| no | either | either | no prompts, plain output, no spinner, no color; --yes implied on add and sync |
| any | any | any, with --quiet | only the lines a session must hear, or nothing; prompts follow the rules below |
Without a prompt, add and sync proceed as if --yes were given, and no other verb does. remove without an explicit -y aborts with exit 1, and --all spells -y out, so it is the one form that passes. The quiet section shows the lines --quiet prints.
disable, enable, link, and unlink take no -y, since they never prompt. A collision without --rename is exit 6 with nothing written.
The output of a non-interactive remove without -y, captured from the built CLI with stdout piped:
text
|
o Memories to remove: @Octocat/rules
ERROR Interactive prompt required but stdin is not a TTY. Nothing was removed. Use -y to run non-interactively.