CLI Reference
All commands are provided by the aiman binary.
Output format is unstable before version 1.0. Human-readable command output is not a stable API. For automation, wait for the upcoming
--jsonflag. All format changes are markedbreaking:in the changelog.
Global flags
| Flag | Description |
|---|---|
--help | Show help for a command |
--version | Print the binary version |
aiman init
aiman init <repo-url> [--path <dir>]Connects an inventory repository.
Arguments:
<repo-url>— URL of the inventory Git repository.
Flags:
--path <dir>— local path for cloning or registering the repository.
Behavior:
- clones or registers the inventory repository;
- creates the base structure for an empty repository;
- creates a local
aim.local.yaml; - writes the active repository to the global config;
- classifies existing files as adoptable, existing AIM, or conflicting.
aiman switch
aiman switch <path>Switches the active inventory repository without cloning.
Use this if you have multiple local inventory repositories or need to run commands from a different directory.
switch unconditionally clears the active pin (aiman apply --loadout <name> --pin, see Pinning the active loadout) — even if the new repository has a loadout with the same name. If you need the pin after switching, set it again: aiman apply --loadout <name> --pin.
aiman apply
aiman apply [--dry-run] [--loadout <name> [--pin]] [--default] [--unpin]Applies the current local inventory to AI environments without any Git operations.
Flags:
--dry-run— show the sha256 delta between the local inventory and the installed AI environments without writing files.--loadout <name>— apply a named loadout declaratively: environments are brought to exactly its set, within the AIM-managed namespace. Seeaiman apply --loadout.--pin— requires--loadout <name>; after a successful apply, saves that loadout as the active pin so thataiman syncapplies it instead of the full inventory going forward. See Pinning the active loadout.--default— apply the full inventory (same as a plainapplywith no flags) and clear the active pin, if one is set.--unpin— clear the active pin without applying anything. Does not read the inventory or touch AI environments; must be used on its own, not combined with--loadout,--pin, or--default. With--dry-runit only shows what would be cleared.
apply does not commit, does not push or fetch, and does not update published_hash or synced_hash.
Without --loadout, the command is additive: it installs and updates inventory items (A/M), but never removes anything from environments — even if the repository has a loadouts/ directory.
Example output — apply
On success, apply prints a result line. If any AI environment has changes, a change delta block follows — indented by two spaces, with A (new) and M (modified) markers:
applied: 21 skills, 1 MCP server → 3 environments
A skills/refactor-helper.md (new in all environments)
M skills/commit-message.md (updated in claude-code, cursor)The counters on the result line (21 skills, 1 MCP server) represent the operation volume: how many items were applied in total. The block below is the change delta: what was actually added or overwritten. These are different numbers — 21 skills means 21 skills total, not 21 changes.
If nothing changed in any environment, only the result line is printed:
applied: 21 skills, 1 MCP server → 3 environmentsExample output — apply --dry-run
Inventory matches the installed AI environments:
[dry-run] nothing to apply — environments match local inventoryChanges detected:
[dry-run] would apply 2 changes to 2 environments (claude-code, cursor):
A skills/new-skill.md (new in all environments)
M skills/existing.md (differs in cursor)
[dry-run] MCP context7 → claude-code, cursorFor MCP servers, apply --dry-run shows only the target AI environments and does not compute a delta — AIM does not compare the installed MCP configuration against the inventory descriptor. See Known limitation: MCP config delta.
If the change list exceeds 20 lines, it is truncated:
A skills/skill-1.md
A skills/skill-2.md
… and 15 moreKnown limitation: MCP config delta
For skills, AIM computes a delta: it compares the inventory content against the installed file in each AI environment and marks changes with A/M.
For MCP servers, delta is not yet computed. AI environments write MCP entries into shared config files (~/.claude.json, mcp.json, config.toml). Comparing the installed fragment against the inventory descriptor requires parsing and normalizing those configs — this is deferred. As a result, MCP entries in the output show only the target environments, without an A/M marker.
The absence of a marker for an MCP server does not mean it is unchanged. It means AIM does not check its state in the environments. For skills, that claim is verified by the delta; for MCP servers, it is not.
aiman apply --loadout <name>
aiman apply --loadout <name> [--dry-run]Applies a named loadout — a subset of the inventory described in loadouts/<name>.yaml — declaratively: each AI environment is brought to exactly the loadout's set, within the AIM-managed namespace.
The AIM-managed namespace is everything that exists in the inventory as skills/<name> or mcp/<name>. Every item falls into one of three branches:
| Item | Action |
|---|---|
in the loadout and allowed in this environment by targets (loadout-level and item-level) | install / update |
| in the inventory, but not in this environment's desired set, and present there | remove from the environment |
| not in the inventory (created by hand or by another tool) | leave untouched |
This holds for both skills and MCP servers: the desired set is computed per environment, and each item's item-level targets narrows it symmetrically for both types.
Removing items outside the loadout is the default behavior, not an opt-in flag. The namespace boundary is the guarantee: AIM removes only what it considers its own. Files outside the inventory are never touched.
Name resolution. <name> is resolved in this order: the exact filename loadouts/<name>.yaml, then the normalized name (spaces → hyphens, lowercase), then the loadout whose name field matches the request after normalization. That is why aiman apply --loadout "Documentation Work" finds loadouts/documentation-work.yaml.
targets. The loadout-level targets restricts which environments the loadout may be applied to: environments outside the list are not part of the plan and are not touched. Each item (skill or MCP server) is additionally restricted by its item-level targets — an item is applied to an environment only where both restrictions overlap. For an MCP Item, item-level targets is required; for a Skill Item it is optional — an absent or empty item-level list does not narrow the intersection, so the item passes through into any environment allowed at the loadout level.
The two levels behave differently. An environment cut off by the loadout-level targets drops out of the plan entirely. An environment that the loadout allows but that an item does not list in its item-level targets stays in the plan — and that skill or MCP server will be removed from it if installed there, because it is not part of the desired set for that environment. This rule applies only on this declarative path (apply --loadout, pinned sync); regular apply/sync never removes anything. See the Inventory Format Reference for details.
Example output — apply --loadout
The result line names the loadout, and the counters represent the operation volume: how many loadout items were applied, not how many changes occurred. The delta block uses three markers: A (installed), M (updated), D (removed from the environment):
applied loadout "Documentation Work": 2 skills, 1 MCP server → 1 environment
A skills/create-spec.md (new in all environments)
D skills/review-code.md (removed from all environments)
M skills/wpage.md (updated in all environments)
A mcp/context7.yaml (new in all environments)Here the loadout consists of two skills and one MCP server (hence 2 skills, 1 MCP server), while the block has four lines: three skill changes, including the removal of review-code — an inventory skill outside the loadout — plus the installation of the MCP server.
The block lines are sorted: skills by name first, then MCP servers by name. The all environments note means "all environments in the plan" — that is, all detected environments allowed by the loadout's targets. If an action affects only some of them, their names are listed instead, separated by commas.
The D category exists only on the loadout path. A plain apply remains additive.
If the environments already match the loadout, only the result line is printed, without the block.
If a loadout item references an inventory item that no longer exists or is currently invalid, the item is skipped with a warning — it is neither installed nor removed:
warning: loadout "dev": no valid inventory item for skill:ghost (skipped)Example output — apply --loadout --dry-run
--dry-run shows the full A/M/D plan without writing to environments or configs:
[dry-run] would apply loadout "Documentation Work" — 4 changes to 1 environment (claude-code):
A skills/create-spec.md (new in all environments)
D skills/review-code.md (would remove from all environments)
M skills/wpage.md (differs in all environments)
A mcp/context7.yaml (new in all environments)Environments already match the loadout:
[dry-run] nothing to apply — environments match loadout "Documentation Work"Always run --dry-run before applying a loadout for the first time: the D lines show exactly what will be removed (see limitations).
Errors — apply --loadout
Loadout not found (loadouts/ contains neither a matching file nor a matching name field):
hint: available loadouts: dev, docs
error: loadout "ghost" not found in loadouts/The hint with the list is printed when there are between one and five valid loadouts: with none there is nothing to suggest, and with more the list is not useful. The hint lists name field values, not filenames.
Loadout found but invalid — apply stops at the first error:
error: loadout "empty": items: cannot be emptyTo get the full list of errors across all loadout files at once, run aiman push.
Pinning the active loadout: --pin, --default, --unpin
aiman apply --loadout <name> --pin
aiman apply --default
aiman apply --unpinA pin is persisted state, not a one-off action: it lives in the global user config (~/.config/aim/config.yaml, field loadout — see the Config Reference) and survives CLI restarts and inventory updates. While a pin is set, aiman sync applies that loadout instead of the full inventory — so you can pull updates from remote while staying in a narrow set. For the difference between a one-off apply --loadout <name> and a pin, see Concepts; for the usage scenario, see Workflows.
aiman apply --loadout documentation-work --pin # apply it and save it as the active pin
aiman apply --default # go back to the full inventory, clear the pin
aiman apply --unpin # clear the pin without applying anythingThe pin is only saved on a successful apply. With --dry-run, the plan for --loadout <name> --pin is printed as usual, but the pin is not saved: dry-run has no side effects, either in environments or in the config. The rule covers all three flags — --default --dry-run and --unpin --dry-run also just show what would happen and leave the pin in place:
[dry-run] would unpin loadout "documentation-work" — sync would go back to applying the full inventoryWhen nothing is pinned:
[dry-run] nothing to unpin — no loadout is pinned--default is the only way to return to the full inventory and clear the pin in a single action. The literal string "Default" is never written as the pin's value: the absence of a pin and Default mode are indistinguishable at the config level.
apply flag conflicts
These errors are checked before the inventory is read and before any changes are made on disk:
| Combination | Error |
|---|---|
--pin without --loadout | error: --pin requires --loadout <name> |
--default + --loadout | error: --default and --loadout are mutually exclusive |
--default + --pin | error: --pin is redundant with --default |
--unpin combined with --loadout, --pin, or --default | error: --unpin does not apply — combine only alone |
aiman push
aiman push [--dry-run]Validates the inventory, creates a commit, and pushes changes to the remote.
Flags:
--dry-run— show the publish plan without committing or pushing.
push is blocked if the remote is ahead of the local state or if the Git state is unsafe.
Frontmatter validation: if a frontmatter field (other than the file body) contains an error, push prints a warning: to stderr and continues. Errors in the file body are blocking: push prints an error: and aborts.
loadouts/ validation: if the repository has a loadouts/ directory, push validates every loadouts/*.yaml — both the format (name is required, items must not be empty, references must use the skill:/mcp: prefixes) and referential integrity: every skill:<name> must exist as skills/<name>.md or skills/<name>/SKILL.md, and every mcp:<name> as mcp/<name>.yaml. All errors found are printed at once and publishing is blocked:
error: loadout "Documentation Work" references unknown skill "missing-skill"
hint: check loadouts/documentation-work.yaml → itemsA missing description and a filename that does not match the normalized name are warnings; they do not block publishing. A repository without loadouts/ is validated exactly as before. The validation also runs in push --dry-run.
After validation, push publishes loadouts/ together with skills/, mcp/, aim.yaml, and .gitignore — no separate git add is needed.
Example output — push
On success, push prints a result line with the published commit hash. If the inventory had changes, a delta block follows — showing which files were published (A for new, M for modified):
published: 96e091b · 19 skills, 1 MCP server
M skills/commit-message.md
A mcp/jira.yaml
A loadouts/documentation-work.yamlIf there were no changes, only the result line is printed.
Example output — push --dry-run
Working tree is clean:
[dry-run] nothing to publish — working tree is cleanChanges ready to publish:
[dry-run] would publish managed changes:
M skills/review-code.md
A mcp/jira.yaml
A loadouts/documentation-work.yaml
validated inventory: 5 skills, 2 MCP serversThe delta block is built from skills/, mcp/, loadouts/, aim.yaml, and .gitignore. The validated inventory line counts inventory items only — skills and MCP servers.
aiman sync
aiman sync [--dry-run] [--force]Fetches the published state from the remote and applies it to the local AI environments.
Flags:
--dry-run— show the plan without writing to the repository or AI environments;--force— remove untracked files conflicting with remote and apply the published state. Files without conflicts are always preserved.
sync does not perform merges. If the history has diverged, AIM stops and asks you to restore Git manually.
Untracked files in skills/, mcp/, and loadouts/ do not block sync unless they conflict by name with files from remote. On conflict, sync stops and lists the problematic files.
Behavior with an active pin
If a loadout is pinned in the global config (aiman apply --loadout <name> --pin, see Pinning the active loadout), the sync transport itself does not change: fetching and applying the published state to the working tree work exactly as usual. What changes is the install step into AI environments: instead of additively installing the full inventory (A/M, no D), sync declaratively brings environments to exactly the pinned loadout's set — the same way as aiman apply --loadout <name>, including removing items outside the set (D). Without an active pin, the sync behavior described elsewhere in this section is unchanged.
The pin is checked after the working tree is updated to the current published state, so a rename or deletion of the loadout that happened in that very update is detected rather than missed due to stale data. If the pin points to a loadout that no longer exists:
error: pinned loadout "documentation-work" not found in inventoryThis error blocks the synced_hash update: AI environments stay in the state applied by the previous sync run. The error is distinct from loadout "X" not found in loadouts/, which apply --loadout returns — the wording differs deliberately, so the source of the error is easy to tell apart in logs and in tooling. See the Errors Reference.
In pinned mode, applying loadout "X" (pinned) is always printed before the result line, followed by the apply delta block (A/M/D) in normal output, not only with --dry-run. This is a separate block from the git-transport delta (what changed in the published inventory relative to the previous state) — the apply delta and the transport delta are never mixed into a single block:
applying loadout "documentation-work" (pinned)
synced: 67451fc · 2 skills, 1 MCP server → 1 environment
A skills/create-spec.md (new in all environments)
D skills/review-code.md (removed from all environments)
M skills/wpage.md (updated in all environments)
A mcp/context7.yaml (new in all environments)--force applies only to the git transport: it removes untracked files in the inventory repository itself that conflict with remote. It is unrelated to the D lines from the pinned apply, which describe items removed from AI environments. If both happen in the same run, both reports are printed separately.
In pinned mode, sync --dry-run shows the same A/M/D plan as apply --loadout <name> --dry-run, as a separate second block after the regular git delta (or after nothing to sync, if there is nothing to apply from remote):
[dry-run] nothing to sync — environments up to date with origin/main
[dry-run] would sync loadout "documentation-work" — 3 changes to 1 environment (claude-code):
A skills/create-spec.md (new in all environments)
D skills/review-code.md (would remove from all environments)
M skills/wpage.md (differs in all environments)If environments already match the pinned loadout:
[dry-run] nothing to sync — environments match loadout "documentation-work"Example output — sync without a pin
Without an active pin, sync works as before: it additively installs the full inventory without removing anything from environments. The delta block below describes changes to the inventory itself (what arrived from remote), not actions taken in environments.
On success, sync prints a result line with the hash of the applied state. If changes arrived from remote, a delta block follows — showing what changed in the inventory (A for new, M for modified, D for deleted):
synced: 67451fc · 21 skills, 1 MCP server → 3 environments
M skills/commit-message.md
A skills/refactor-helper.mdAs with apply, the counters on the result line represent the operation volume (total applied); the block below is the delta of incoming changes. 21 skills means 21 skills total, not 21 changes.
If the local state already matched origin/main and nothing came in from remote, only the result line is printed:
synced: 67451fc · 21 skills, 1 MCP server → 3 environmentsWith --force, AIM removes conflicting untracked files before applying and reports what was deleted. This report always appears before the result line:
discarded untracked files (--force):
skills/draft.md
mcp/local-only.yaml
synced: 67451fc · 21 skills, 1 MCP server → 3 environmentsThe deletion report cannot be suppressed: --force deletes files permanently, and AIM never does this silently.
Example output — sync --dry-run without a pin
Nothing to apply (local state matches origin/main):
[dry-run] nothing to sync — environments up to date with origin/mainChanges to apply:
[dry-run] would sync 2 changes from origin/main → 3 environments:
M skills/commit-message.md
A skills/refactor-helper.mdaiman status
aiman statusShows the state of the active inventory repository: position relative to origin/main, state of AI environments, and the list of changes in skills/, mcp/, and loadouts/ that have not yet been published.
aiman status fetches from remote. Before calculating the position, the command runs a Git fetch — it answers the question "what in my inventory has changed relative to the published state," and that question inherently includes the remote. Without a fetch, the position would be based on a stale local ref and could be wrong (showing up-to-date when the remote has already moved on).
This is a deliberate difference from git status. git status never touches the network because its question is only about the local working tree. aiman status has a different scope, so the short fetch delay is expected behavior, not a bug.
TODO: add a spinner during fetch
The fetch runs with a hard timeout. If the remote is unreachable, the command does not hang: the Position: field is set to unknown (remote unreachable), a warning: cannot reach remote repository is printed to stderr, and the exit code remains 0.
Position: possible values: up-to-date with origin/main | N commits ahead of origin/main | N commits behind of origin/main | diverged from origin/main (N ahead, M behind) | unknown (remote unreachable).
Environments: possible values: applied (synced <hash>) | unknown | needs sync (N commits not applied).
The Pinned loadout: field shows the active pin (aiman apply --loadout <name> --pin, see Pinning the active loadout) or none if no pin is set. This is local state from the global config — it is always printed, even if the remote is unreachable, and does not require network access.
If the change list exceeds 20 lines, it is truncated with … and N more.
Example output
Repository is in sync, inventory is applied, no pin set:
Repository: git@github.com:you/aim-loadout.git
Position: up-to-date with origin/main
Environments: applied (synced a1b2c3d)
Pinned loadout: none
Working tree matches origin/main · nothing to publishAn active pin is set:
Repository: git@github.com:you/aim-loadout.git
Position: up-to-date with origin/main
Environments: applied (synced a1b2c3d)
Pinned loadout: documentation-work
Working tree matches origin/main · nothing to publishUnpublished changes present:
Repository: git@github.com:you/aim-loadout.git
Position: 2 commits ahead of origin/main
Environments: needs sync (2 commits not applied)
Pinned loadout: none
Changes not yet published (origin/main → working tree):
A skills/new-skill.md
M skills/existing.md
run aiman push to publish
run aiman sync to applyRemote unreachable (no network or no access to the repository) — Pinned loadout: is shown as usual, since it is local state:
Repository: git@github.com:you/aim-loadout.git
Position: unknown (remote unreachable)
Environments: applied (synced a1b2c3d)
Pinned loadout: none
Working tree matches origin/main · nothing to publishaiman doctor
aiman doctorDiagnoses the local installation:
- active repository;
- detected AI environments;
- adapter paths;
- validity of the skills in
skills/of the active inventory repository; - required MCP env variables;
- remote availability and sync state.
The === AI Environments === section answers a single question: was the environment's base directory found. The === Skills === section counts valid and invalid skills in skills/ of the active inventory repository — it does not scan the AI environments and does not show what is installed in them. This holds for every environment: doctor never inspects the set of skills installed in an AI environment.
Example output
Everything is healthy, all environments found:
Active Repo: /home/user/.aim/aim-loadout (from /home/user/.config/aim/config.yaml)
=== AI Environments ===
✓ claude-code /home/user/.claude found
✓ cursor /home/user/.cursor found
✗ codex /home/user/.codex not found
=== Skills ===
Found: 4 valid, 0 invalid
=== Sync State ===
synced_hash: a1b2c3d
published_hash: a1b2c3d
remote HEAD: a1b2c3d
status: up-to-date
=== MCP Environment Variables ===
✓ context7 › UPSTASH_REDIS_REST_URL — set
=== Issues ===
• codex: not installed or not found at /home/user/.codexIssues present — AI environment not found, required variable not set:
Active Repo: /home/user/.aim/aim-loadout (from /home/user/.config/aim/config.yaml)
=== AI Environments ===
✓ claude-code /home/user/.claude found
✗ cursor /home/user/.cursor not found
✗ codex /home/user/.codex not found
=== Skills ===
Found: 2 valid, 1 invalid
=== Sync State ===
synced_hash: not set
published_hash: not set
remote HEAD: a1b2c3d
status: not yet synced
=== MCP Environment Variables ===
✗ context7 › UPSTASH_REDIS_REST_URL — missing (required)
=== Issues ===
• cursor: not installed or not found at /home/user/.cursor
• codex: not installed or not found at /home/user/.codex
• my-skill.md: invalid: missing description
• context7 › UPSTASH_REDIS_REST_URL — missing (required)aiman add
aiman add skill <file|dir|-> [--name <string>] [--overwrite]
aiman add mcp <file|-> [--name <string>] [--overwrite]Adds an inventory item (skill or MCP server) to the local repository from a file, a directory, or stdin.
aiman add skill
aiman add skill <file|dir|->
aiman add skill ~/Downloads/my-skill.md
aiman add skill -Reads a skill from a file, a directory, or stdin (-), validates the frontmatter, determines the name, and writes the result to the active inventory repository.
Arguments:
<file|dir|->— path to the skill file, path to a folder skill (or itsSKILL.md), or-to read from stdin.
Flags:
--name <string>— override the skill name; if not set, the name is taken from the file's frontmatter (for a flat skill) or from the directory name (for a folder skill).--overwrite— overwrite an existing skill on content conflict.
Behavior:
- a single file (not named
SKILL.md) or stdin — written as a flatskills/<name>.md; - a path to a directory, or a path to a
SKILL.mdfile inside it — recognized as a folder skill and copied in full toskills/<name>/(SKILL.mdplus all reference files alongside it). Both path forms produce the same result; - if the directory does not contain
SKILL.md— returns a clear error instead of a system-level "is a directory" message; - if
skills/<name>.md(flat) orskills/<name>/SKILL.md(folder) already exists with the same content — the command succeeds with no changes; for a folder skill, onlySKILL.mdcontent is compared, reference files are not. If only the reference files changed, the command reportsalready identicaland does not copy them —--overwritedoes not help here, so copy the changed files by hand; - file permissions are not preserved during the copy: every file is written as
0644, and the executable bit on scripts inside a folder skill is lost; - if the destination already exists with different content and
--overwriteis not set — returns an error with a hint to use--overwrite.
Add from a file:
aiman add skill ~/Downloads/create-spec.mdAdd from stdin:
cat ~/Downloads/create-spec.md | aiman add skill -Add a folder skill (both forms are equivalent):
aiman add skill ~/Downloads/get-team-tasks
aiman add skill ~/Downloads/get-team-tasks/SKILL.mdOutput on successful add:
added: skill create-specOutput when the skill is already in the inventory with identical content (no-op):
up to date: skill create-spec · already identicalaiman add mcp
aiman add mcp <file|->
aiman add mcp jira.yaml
aiman add mcp -Reads an MCP server description from a YAML file or stdin (-), applies env-strip, and writes the result to the active inventory repository.
Arguments:
<file|->— path to the MCP server YAML file, or-to read from stdin.
Flags:
--name <string>— override the server name; if not set, the name is taken from the file'snamefield.--overwrite— overwrite an existing MCP server on content conflict.
Behavior:
- if
mcp/<name>.yamlalready exists with the same content — the command succeeds with no changes; - if
mcp/<name>.yamlalready exists with different content and--overwriteis not set — returns an error with a hint to use--overwrite.
Env-strip: env fields with a populated value are not saved in mcp/<name>.yaml. Instead, the actual values are written to aim.local.yaml under the key mcp_env: {<name>.<VAR>: value}. The inventory stores only descriptors: name, description, required, example. Since aim.local.yaml is excluded from Git, secrets never enter the repository.
Example input file:
name: jira
description: Jira MCP server
command: npx
args: [-y, mcp-jira]
targets: [claude-code]
env:
- name: JIRA_API_KEY
description: Jira API key
required: true
value: "secret123"After the command:
mcp/jira.yamlcontains the descriptor without thevaluefield;aim.local.yamlcontainsmcp_env: {jira.JIRA_API_KEY: "secret123"}.
Add from a file:
aiman add mcp jira.yamlAdd from stdin:
cat jira.yaml | aiman add mcp -Override the server name:
aiman add mcp jira.yaml --name jira-workOverwrite on conflict:
aiman add mcp jira.yaml --overwriteOutput on successful add:
added: mcp jiraOutput when the MCP server is already in the inventory with identical content (no-op):
up to date: mcp jira · already identicalaiman import
aiman import skill <name> --from <env> [--print] [--overwrite]
aiman import mcp <name> --from <env> [--print] [--overwrite] [--targets all]Imports an inventory item (skill or MCP server) from an installed AI environment into the local repository.
Requirements shared by both subcommands:
An active inventory repository is required. If none is selected (neither
aiman initnoraiman switchhas been run), the command fails before anything is written to disk — it never writes to the current directory:texterror: no active inventory repository; run 'aiman init' firstThe requirement also applies with the
--printflag.The item name is validated before the write path is built. An empty name,
.,.., names containing/or\, absolute paths, control characters, and names longer than 255 characters are rejected:texterror: invalid item name "../evil": must not contain "/" or "\"
aiman import skill
aiman import skill <name> --from <env>
aiman import skill create-spec --from claude-code
aiman import skill create-spec --from claude-code --printReads a skill by name from the specified AI environment and writes it into the active inventory repository.
AI environments store skills in folder format — <name>/SKILL.md alongside its resource files (references/, scripts). Such a skill is transferred in full: SKILL.md and every resource file end up in skills/<name>/. A flat <name>.md file, if the environment has one, is written as skills/<name>.md.
Arguments:
<name>— name of the skill to import.
Flags:
--from <env>— import source (required). Accepted values:claude-code,cursor,codex.--print— print the skill content to stdout without writing to disk. For a folder skill, onlySKILL.mdis printed; resource files are not.--overwrite— overwrite an existing skill on content conflict.--targets— accepted for symmetry withaiman import mcp, but ignored for skills: a skill'stargetscome only from its frontmatter, see Concepts.
Skill sources by AI environment:
| AI environment | Identifier | Skills path |
|---|---|---|
| Claude Code | claude-code | ~/.claude/skills/<name>/SKILL.md and flat ~/.claude/skills/*.md |
| Codex CLI | codex | ~/.codex/skills/<name>/SKILL.md |
| Cursor | cursor | ~/.cursor/skills/<name>/SKILL.md |
Behavior:
- if the skill is not found in the specified environment — returns an error;
- if the skill already exists in the inventory with the same content — succeeds with no changes;
- if the skill already exists in the inventory with different content and
--overwriteis not set — returns an error with a hint to use--overwrite; - a content match is determined by
SKILL.md; resource files are not taken into account. If only the resource files changed in the environment whileSKILL.mdstayed the same, the command reportsalready identicaland does not carry the changes over —--overwritedoes not help here either, so copy the changed files by hand.
Limitation: file permissions are not preserved — every file is written as 0644, both when transferring into the inventory and when installing into an AI environment later. The executable bit on scripts inside a folder skill is lost; see Troubleshooting for how to restore it.
Import from Claude Code:
aiman import skill create-spec --from claude-codeOutput on successful import:
imported: skill create-spec · from claude-codeOutput when the skill is already in the inventory with identical content (no-op):
up to date: skill create-spec · already identicalPreview without writing:
aiman import skill create-spec --from claude-code --printaiman import mcp
aiman import mcp <name> --from <env> [--print] [--overwrite] [--targets all]
aiman import mcp context7 --from claude-code
aiman import mcp jira --from cursor --print
aiman import mcp context7 --from claude-code --targets allScans the live configuration of the specified AI environment, locates MCP servers, applies env-strip, and writes the descriptor to the active inventory repository.
Arguments:
<name>— name of the MCP server to import.
Flags:
--from <env>— import source (required). Accepted values:claude-code,cursor,codex.--print— print the YAML descriptor to stdout without writing files. The conflict check runs before printing: ifmcp/<name>.yamlalready exists with different content, the command fails and prints nothing — add--overwriteto see the descriptor anyway (the file is still not overwritten).--overwrite— overwrite the existing file if content differs.--targets all— set all three adapters (claude-code,cursor,codex) as target environments in the descriptor. By default, only the source environment is used.
Behavior:
Env-strip: real env variable values are not saved in
mcp/<name>.yaml. Instead, they are written toaim.local.yamlunder the keymcp_env: {<name>.<VAR>: value}. Only descriptors —name,required— go into the inventory. Sinceaim.local.yamlis excluded from Git, secrets never enter the repository.Deterministic env order: env variable descriptors are written sorted by
name. Re-importing an unchanged source produces byte-identical YAML, soalready identicaltriggers reliably and Git sees no diffs caused by reordered lines.stdio transport only: a server declared with
"type": "http","type": "sse", or a bare"url"without acommandis not imported. The command fails with an error naming the server and the reason, and writes nothing to disk:texterror: MCP server "remote-tool": unsupported transport "http" (only stdio servers can be imported)Other servers in the same config are imported as usual.
Deduplication: if the same server name appears multiple times with the same command and arguments, the first entry is used.
Ambiguity (AmbiguousError): if the same server name appears with different commands or arguments, the command fails — you must explicitly specify the source via
--from.Server not found: if no server with the given name exists in the environment config, the command fails with
MCP server "<name>" not found in <env>. A server with an unsupported transport does not fall into this category — it produces the error naming the reason (see above) rather thannot found.Unknown environment: if the
--fromvalue is not recognized, the command fails withunknown environment: X; available: claude-code, cursor, codex.if
mcp/<name>.yamlalready exists with the same content — succeeds with no changes;if
mcp/<name>.yamlalready exists with different content and--overwriteis not set — returns an error with a hint to use--overwrite.
MCP configuration sources by AI environment:
| AI environment | Identifier | Configuration file |
|---|---|---|
| Claude Code | claude-code | ~/.claude.json (key mcpServers) |
| Cursor | cursor | ~/.cursor/mcp.json (key mcpServers) |
| Codex CLI | codex | ~/.codex/config.toml (section mcp_servers) |
For Claude Code, ~/.claude/settings.json is read as well: a server is taken from there only if no entry with that name exists in ~/.claude.json.
Import from Claude Code:
aiman import mcp context7 --from claude-codeOutput on successful import:
imported: mcp context7 · from claude-codeIf the MCP server had env variables with populated values, they are written to aim.local.yaml:
imported: mcp context7 · from claude-code · secrets stored in aim.local.yamlOutput when the MCP server is already in the inventory with identical content (no-op):
up to date: mcp context7 · already identicalPreview the YAML descriptor without writing:
aiman import mcp context7 --from claude-code --printImport with all adapters set as targets:
aiman import mcp context7 --from claude-code --targets allOverwrite if the file already exists:
aiman import mcp context7 --from claude-code --overwriteCommands outside the current public scope
aiman list may be present in the codebase as a historical command, but its full public contract is deferred until a complete inventory view is implemented.