OxagenDocs
CLI

Importing agent artifacts

Convert Claude Code, Codex, Cursor, and legacy Oxagen agents, skills, and commands into canonical Oxagen TOML — scanned locations, dry runs, conflicts, capability mapping, and receipts.

Oxagen agents, skills, and slash commands are TOML only. oxagen import artifacts converts artifacts authored for Claude Code, Codex, Cursor, or legacy Markdown-era Oxagen into canonical Oxagen TOML.

The importer is the only component that reads Markdown or YAML frontmatter. The runtime never does.

What it guarantees

  • Your source files are never modified, moved, or deleted. Discovery and parsing are read-only. Your .claude/, .codex/, and .cursor/ trees keep working with those tools.
  • Imported artifacts are Oxagen-owned and independent. The result is a regular TOML file — and for skills, a regular directory of regular files. No symlink back to the source survives, so editing the source later cannot silently change Oxagen behavior.
  • Capability mapping is exact and versioned. Ambiguous tools are never guessed.
  • Activation is staged and atomic. A half-written artifact is never visible to a loader.

Scanned locations

Scope is --scope workspace (default, scans the working directory) or --scope user (scans $HOME).

PlatformAgentsSkills (bundles)Commands
claude.claude/agents/.claude/skills/<name>/.claude/commands/
codex.codex/agents/, .agents/agents/.codex/skills/<name>/, .agents/skills/<name>/.codex/commands/, .agents/commands/
cursor.cursor/agents/.cursor/skills/<name>/.cursor/commands/
oxagen-legacy.oxagen/agents/.oxagen/skills/<name>/.oxagen/commands/

Under --scope user these resolve against $HOME, except oxagen-legacy, which resolves to ~/.config/oxagen/.

Agents and commands are flat .md or .mdc files. Skills are directories with a SKILL.md or skill.md manifest at the root. .agents/skills is shared between tools, so candidates are de-duplicated by real path and content hash.

Narrow the scan with --from, or point somewhere else entirely with --source (which requires exactly one --from):

oxagen import artifacts --from claude --from cursor
oxagen import artifacts --from claude --source ./vendor/claude-pack

Always dry-run first

--dry-run does the full job — discovery, parsing, mapping, validation — and writes nothing at all, not even a receipt.

oxagen import artifacts --dry-run
Would process 3 artifact(s):
✓ claude agent code-reviewer: imported
! claude agent release-captain: imported (needs review; non-executable)
– cursor skill testing: skipped

Add --json for the machine-readable receipt — the right form for CI, and for diffing two runs.

Conflicts and per-item decisions

A conflict is an existing artifact at the destination path.

DecisionBehavior
skipLeave the existing artifact untouched. Default.
replaceAtomically replace it with the imported artifact.
renameImport alongside as <name>-imported-2.

Non-interactive runs default to skip — nothing is overwritten unless you ask. Set a run-wide default with --conflict, or decide per item:

oxagen import artifacts --conflict rename
oxagen import artifacts --choice code-reviewer=replace --choice testing=skip

Anything not named in a --choice falls back to skip, so a partial decision set can never overwrite an artifact you forgot to mention.

In the REPL, /import prompts for each conflicting item in turn. CLI and REPL share one engine — there is no second importer with different behavior.

Capability mapping

Foreign tool names map to canonical Oxagen capability slugs through a data-only, versioned registry. The current mapping version is 2026-07-21.1, recorded on every receipt item so a past import stays explainable.

Mapping is exact, and may be one-to-many — Claude's Read covers what Oxagen models as four slugs:

Source (claude)Oxagen capabilities
Readread_file, list_dir, glob, grep
Writewrite_file
Editedit_file
Bashbash
WebFetchfetch_web
WebSearchsearch_web

Cursor maps Terminal and Shell to bash. Codex uses Oxagen-shaped snake_case names that map identity-wise when the name exists in the live catalog. No wildcard ever broadens a grant: a source tool with no registry entry grants nothing.

Unresolved capabilities

A tool is unresolved when it has no exact registry entry (unknown_mapping), when its mapped target is not in the live catalog (target_unavailable), or when it is an mcp__<server>__<tool> name with no configured mapping (ambiguous_mcp). MCP tools resolve only by installed server identity plus tool identity — a server you have not configured is ambiguous, never guessed.

Unresolved names are preserved verbatim in the agent's unresolved_tools array and the agent is marked needs_review. A needs_review agent is written to disk, is fully inspectable, and will not run until a human moves each entry into tools with the correct slug or deletes it.

What is preserved, normalized, and dropped

Preserved — names, descriptions, bodies, declared skill references, every unresolved tool name verbatim, and every regular file in a skill bundle (copied recursively into the Oxagen-owned bundle).

Normalized — names become kebab-case; model identifiers collapse to Oxagen's portable tiers (haiku/mini/fastfast, opus/maxpowerful, sonnet/balancedbalanced); a missing description becomes Imported agent/Imported skill/Imported command.

Dropped — any frontmatter key with no field in the canonical schema, and foreign symlinks inside a skill bundle (deliberately not carried across, so the imported bundle has no external dependency).

Every loss and normalization is recorded as a diagnostic on the receipt.

.oxagen/ paths are often symlinks into a foreign tool's directory, left from pre-TOML workflows. Import treats such a destination as an ordinary conflict. Choosing replace swaps the symlink for a regular, Oxagen-owned file or directory — the symlink's target is not written to, not deleted, and not followed for the write.

Receipts

Every non-dry-run import that processed at least one artifact writes a receipt:

  • Workspace scope: .oxagen/import-receipts/<timestamp>.json
  • User scope: ~/.config/oxagen/import-receipts/<timestamp>.json

Receipts are machine-local, mode 0600, and never uploaded. Home directories are normalized to ~, and no absolute machine path is ever written into an artifact.

Each item records provenance (sourcePath, sourceHash), result (destinationPath, artifactHash), the mapping version in force, the conflict decision, the review state, the outcome, and every diagnostic. Skips and failures are recorded too — a skipped conflict is a recorded outcome, not a silent no-op.

OutcomeMeaning
importedWritten (or, in a dry run, would be written).
unchangedAn identical artifact is already at the destination. Nothing written, no conflict prompt.
skippedA real conflict was left alone, by default or by decision.
rejectedThe source could not be converted — invalid frontmatter, a reference escaping its bundle, or a failed write.

unchanged is what makes reruns readable: a second import over an already-converted tree reports every item as unchanged rather than burying a genuine conflict among identical "skipped" lines.

rejected items are isolated — a corrupt or hostile source is recorded with a diagnostic and the run continues. It can neither be activated nor hide its valid siblings, and its source file is left untouched.

Exit codes

CodeMeaning
0Run completed. Items may still be skipped or needs_review.
2Invalid invocation — unknown platform, bad scope or conflict mode, malformed --choice, or --source without exactly one --from.
# 1. See exactly what would happen.
oxagen import artifacts --dry-run --json > import-plan.json

# 2. Import the unambiguous artifacts; conflicts skip by default.
oxagen import artifacts

# 3. Resolve conflicts deliberately, one at a time.
oxagen import artifacts --choice code-reviewer=replace

# 4. Find agents still awaiting a capability decision.
rg -l 'unresolved_tools = \[".+"\]' .oxagen/agents

Re-running is safe: with the default skip, a second run over an already-imported tree changes nothing.

On this page