Skip to content

CLI — API reference

State paths

claude_dir

claude_dir() -> Path

Return the user's Claude Code config directory (~/.claude).

data_dir

data_dir() -> Path

Return the mait-code data directory.

Delegates to :func:mait_code.config.data_dir, the single source of truth for this resolution (honours $MAIT_CODE_DATA_DIR; otherwise ~/.claude/mait-code-data).

install_record_path

install_record_path() -> Path

Return the path to the install record JSON.

mait_code_config_dir

mait_code_config_dir() -> Path

Return the directory holding mait-code's configuration.

Houses the settings file. $XDG_CONFIG_HOME/mait-code by default; honours $XDG_CONFIG_HOME overrides via :func:xdg_config_home.

mait_code_log_dir

mait_code_log_dir() -> Path

Return the directory holding mait-code's log files.

$XDG_STATE_HOME/mait-code by default; honours $XDG_STATE_HOME overrides via :func:xdg_state_home.

mait_code_state_dir

mait_code_state_dir() -> Path

Return the directory holding mait-code's persistent data.

Houses the install record. $XDG_DATA_HOME/mait-code by default; honours $XDG_DATA_HOME overrides via :func:xdg_data_home.

settings_path

settings_path() -> Path

Return the path to the mait-code settings file (TOML).

xdg_config_home

xdg_config_home() -> Path

Return the XDG config home directory.

Honours $XDG_CONFIG_HOME when set to a non-empty value; otherwise falls back to ~/.config per the XDG Base Directory Spec.

xdg_data_home

xdg_data_home() -> Path

Return the XDG data home directory.

Honours $XDG_DATA_HOME when set to a non-empty value; otherwise falls back to ~/.local/share per the XDG Base Directory Spec.

xdg_state_home

xdg_state_home() -> Path

Return the XDG state home directory.

Honours $XDG_STATE_HOME when set to a non-empty value; otherwise falls back to ~/.local/state per the XDG Base Directory Spec.

Install record

SCHEMA_VERSION module-attribute

SCHEMA_VERSION = 2

InstallRecord dataclass

InstallRecord(
    source_dir: str,
    first_installed_at: str,
    updated_at: str,
    schema_version: int = SCHEMA_VERSION,
)

Persisted state about a mait-code install.

ATTRIBUTE DESCRIPTION
source_dir

Absolute path to the cloned source tree.

TYPE: str

first_installed_at

ISO 8601 UTC timestamp of the first install. Frozen at install time and preserved across every update.

TYPE: str

updated_at

ISO 8601 UTC timestamp of the most recent install / update. Refreshed on every update.

TYPE: str

schema_version

Format version of this record. See :data:SCHEMA_VERSION.

TYPE: int

new classmethod

new(
    *,
    source_dir: Path | str,
    first_installed_at: str | None = None,
) -> InstallRecord

Construct a record stamped with the current UTC time.

PARAMETER DESCRIPTION
source_dir

The cloned source tree.

TYPE: Path | str

first_installed_at

Preserve an earlier first-install timestamp (passed by update to keep the original date across reinstalls). When None (a fresh install), it defaults to now, matching updated_at.

TYPE: str | None DEFAULT: None

RecordError

Bases: Exception

Raised when the install record is missing, malformed, or from a schema version this binary doesn't understand.

read_record

read_record(*, path: Path | None = None) -> InstallRecord

Read the install record from disk.

PARAMETER DESCRIPTION
path

Override source path (defaults to :func:~mait_code.cli._paths.install_record_path).

TYPE: Path | None DEFAULT: None

RETURNS DESCRIPTION
InstallRecord

The deserialised :class:InstallRecord.

RAISES DESCRIPTION
RecordError

If the file is missing, malformed JSON, missing a required field, or has a schema_version this binary doesn't understand.

write_record

write_record(
    record: InstallRecord, *, path: Path | None = None
) -> Path

Persist record to disk, creating parent directories as needed.

PARAMETER DESCRIPTION
record

The :class:InstallRecord to write.

TYPE: InstallRecord

path

Override target path (defaults to :func:~mait_code.cli._paths.install_record_path).

TYPE: Path | None DEFAULT: None

RETURNS DESCRIPTION
Path

The path the record was written to.

Install flow

EMBEDDING_PROVIDERS module-attribute

EMBEDDING_PROVIDERS = ('local', 'bedrock')

The valid values for --embedding-provider.

InstallSummary

InstallSummary(
    *,
    record: InstallRecord,
    claude_md: SymlinkResult,
    skills: SymlinkResult,
    agents: SymlinkResult,
    templates_copied: list[str],
    templates_upgraded: list[str],
    memory_md_created: bool,
    settings_path: Path,
)

What :func:install produces — used by the CLI to render output.

Update flow

UpdateSummary

UpdateSummary(
    *,
    record: InstallRecord,
    fetched: bool,
    landed_on: str,
    reinstalled: bool,
    installed_version: str | None,
    claude_md: SymlinkResult,
    skills: SymlinkResult,
    agents: SymlinkResult,
    templates_copied: list[str],
    templates_upgraded: list[str],
    settings_path: Path,
)

What :func:update produces — used by the CLI for output.

Uninstall flow

UninstallSummary

UninstallSummary(
    *,
    had_record: bool,
    claude_md_removed: bool,
    skills_removed: list[Path],
    agents_removed: list[Path],
    settings_cleaned: bool,
    uv_tool_uninstalled: bool,
    data_dir_removed: bool,
    warnings: list[str],
)

Outcome of an uninstall — used by the CLI for output.

Status & doctor

Check dataclass

Check(
    name: str,
    level: Level,
    message: str,
    fix_hint: str | None = None,
)

A single diagnostic outcome.

DoctorReport dataclass

DoctorReport(checks: list[Check], fixes_applied: list[str])

All diagnostic outcomes for one doctor run.

Status dataclass

Status(
    record_present: bool = False,
    source_dir: str | None = None,
    version: str | None = None,
    embedding_provider: str | None = None,
    first_installed_at: str | None = None,
    updated_at: str | None = None,
    record_error: str | None = None,
    claude_md_path: str | None = None,
    claude_md_target: str | None = None,
    claude_md_is_symlink: bool = False,
    skills_linked: int = 0,
    skills_total: int = 0,
    agents_linked: int = 0,
    agents_total: int = 0,
    hooks_registered: list[str] = list(),
    data_dir_path: str | None = None,
    data_dir_size_bytes: int = 0,
    has_soul_document: bool = False,
    has_user_context: bool = False,
    has_memory_md: bool = False,
    binary_path: str | None = None,
)

Snapshot of the current mait-code install state.

SymlinkResult dataclass

SymlinkResult(
    created: list[Path] = list(),
    already_linked: list[Path] = list(),
    updated: list[Path] = list(),
    backed_up: list[Path] = list(),
)

Outcome summary returned by the symlink helpers.

ATTRIBUTE DESCRIPTION
created

Links that did not exist before this call.

TYPE: list[Path]

already_linked

Links that already pointed at the correct target.

TYPE: list[Path]

updated

Links that existed but pointed somewhere stale, now repointed.

TYPE: list[Path]

backed_up

Existing non-symlink files renamed to <name>.backup.

TYPE: list[Path]

remove_agent_symlinks(
    source_dir: Path, claude_dir: Path
) -> list[Path]

Remove every agent symlink under <claude_dir>/agents/ that points into source_dir. Returns the list of paths removed.

remove_claude_md_symlink(
    source_dir: Path, claude_dir: Path
) -> bool

Remove the CLAUDE.md symlink if it points into source_dir.

Restores CLAUDE.md.backup to CLAUDE.md if the backup exists.

RETURNS DESCRIPTION
bool

True if the symlink was removed; False if nothing to do.

remove_skill_symlinks(
    source_dir: Path, claude_dir: Path
) -> list[Path]

Remove every skill symlink under <claude_dir>/skills/ that points into source_dir. Returns the list of paths removed.

symlink_agents(
    source_dir: Path, claude_dir: Path
) -> SymlinkResult

Symlink every agents/<file> into <claude_dir>/agents/.

No-op if source_dir/agents is missing. .gitkeep placeholders are ignored. Agents are individual files, not directories.

symlink_claude_md(
    source_dir: Path, claude_dir: Path
) -> SymlinkResult

Symlink config/CLAUDE.md to <claude_dir>/CLAUDE.md.

If a non-symlink CLAUDE.md already exists at the target, it is renamed to CLAUDE.md.backup first (preserving the user's pre-existing config).

PARAMETER DESCRIPTION
source_dir

Absolute path to the cloned source tree.

TYPE: Path

claude_dir

The Claude Code config dir (typically ~/.claude).

TYPE: Path

RETURNS DESCRIPTION
A

class:SymlinkResult summarising what was done.

TYPE: SymlinkResult

symlink_skills(
    source_dir: Path, claude_dir: Path
) -> SymlinkResult

Symlink every skills/<name>/ directory into <claude_dir>/skills/.

No-op if source_dir/skills is missing. .gitkeep placeholders are ignored.

Settings

merge_settings

merge_settings(
    source: dict[str, Any],
    dest: dict[str, Any],
    *,
    user_settings: dict[str, str],
) -> dict[str, Any]

Merge mait-code's settings into a user's existing settings.

Returns a new dict; does not mutate either input. The merge replaces mait-code-owned hook entries and MCP servers with the source's versions, then propagates all user settings as MAIT_CODE_* environment variables. Other user keys are preserved verbatim.

PARAMETER DESCRIPTION
source

The config/settings.json shipped in the source tree.

TYPE: dict[str, Any]

dest

The user's existing ~/.claude/settings.json content (or {} if the file didn't exist).

TYPE: dict[str, Any]

user_settings

Flat {key: value} from the settings file.

TYPE: dict[str, str]

RETURNS DESCRIPTION
dict[str, Any]

The merged settings dict ready to write back.

read_settings_file

read_settings_file(path: Path) -> dict[str, Any]

Read a settings.json. Returns {} if missing or unparseable.

unmerge_settings

unmerge_settings(
    settings: dict[str, Any],
) -> dict[str, Any]

Strip mait-code-owned entries from a settings dict.

Hook entries are identified by any inner command starting with :data:MAIT_CODE_HOOK_PREFIX. MCP servers are removed by name from :data:MAIT_CODE_MCP_SERVERS. The MAIT_CODE_EMBEDDING_PROVIDER env entry is removed. Empty top-level sections are dropped.

PARAMETER DESCRIPTION
settings

The current on-disk settings dict.

TYPE: dict[str, Any]

RETURNS DESCRIPTION
dict[str, Any]

A new dict with mait-code's footprint removed.

write_settings_file

write_settings_file(
    path: Path, settings: dict[str, Any]
) -> None

Write settings to path atomically with a trailing newline.

Uses a tempfile + os.replace so a crash mid-write can't leave half a JSON file on disk.

Editable settings

ApplyOutcome dataclass

ApplyOutcome(
    key: str,
    old_value: str,
    new_value: str,
    followup: str | None,
    followup_done: bool,
    warnings: list[str] = list(),
)

The result of a successful :func:apply_setting call.

ATTRIBUTE DESCRIPTION
key

The setting that was changed.

TYPE: str

old_value

Its resolved value before the change.

TYPE: str

new_value

The value written to the settings file.

TYPE: str

followup

The required follow-up — "reindex", "move-data" or None when the change applies on the next invocation.

TYPE: str | None

followup_done

True when the follow-up was actually carried out.

TYPE: bool

warnings

Human-readable warnings (e.g. a shell export still shadows the change).

TYPE: list[str]

apply_setting

apply_setting(
    key: str,
    value: str,
    *,
    reindex: bool | None = None,
    move_data: bool | None = None,
    confirm: Callable[[str], bool] | None = None,
) -> ApplyOutcome

Validate, persist and enforce a single setting change.

PARAMETER DESCRIPTION
key

Kebab-case setting key (e.g. log-level).

TYPE: str

value

The new value, as a string.

TYPE: str

reindex

For a migration key, whether to re-embed now. None (and no confirm) means undecided — an error, since the caller must choose explicitly.

TYPE: bool | None DEFAULT: None

move_data

For data-dir, whether to relocate the data directory.

TYPE: bool | None DEFAULT: None

confirm

A yes/no prompt used by the interactive editor to decide a follow-up when the corresponding flag is None.

TYPE: Callable[[str], bool] | None DEFAULT: None

RETURNS DESCRIPTION
An

class:ApplyOutcome describing what changed and what ran.

TYPE: ApplyOutcome

RAISES DESCRIPTION
SettingError

The key is unknown, derived (read-only), a single scoring weight, or the value fails validation; or a required follow-up decision was not supplied.

Entry point

app module-attribute

app = Typer(
    name="mait-code",
    help="mait-code install-lifecycle CLI.",
    pretty_exceptions_enable=False,
    add_completion=False,
)

main

main() -> None

Console-script entry point declared in pyproject.toml.