MusePi

Configuration Discovery and Resolution

English | 中文 This document describes how the coding-agent resolves configuration today: which roots are scanned, how precedence works, and how resolved config is consumed by settings, skills, hooks, tools, and extensions.

Scope

Primary implementation:

Key integration points:


Resolution flow (visual)

         Generic helper order (`config.ts`)
┌───────────────────────────────────────┐
│ 1) ~/.musepi/agent, ~/.claude, ...       │
│ 2) <cwd>/.musepi, <cwd>/.claude, ...     │
└───────────────────────────────────────┘
                    │
                    ▼
        capability providers enumerate items
 (native provider scans project .musepi before user .musepi;
  other providers have their own loading rules)
                    │
                    ▼
      provider priority sort + capability dedup
                    │
                    ▼
          subsystem-specific consumption
   (settings, skills, hooks, tools, extensions)

1) Config roots and source order

Canonical roots

src/config.ts defines a fixed source priority list:

  1. .musepi (native)
  2. .claude
  3. .codex
  4. .gemini

User-level bases:

Project-level bases:

CONFIG_DIR_NAME is .musepi (packages/utils/src/dirs.ts).

Profiles

A named profile (musepi --profile <name>, the --alias shortcut, or OMP_PROFILE / PI_PROFILE) relocates the OMP user base. When a profile is active, every OMP-native user-level path written here as ~/.musepi/agent/... resolves to ~/.musepi/profiles/<name>/agent/... instead.

The relocation is uniform across the native provider (builtin.ts) and the generic config.ts helpers, so it covers slash commands, rules, prompts, instructions, hooks, tools, extensions, settings, skills, and MCP, plus the top-level SYSTEM.md / RULES.md / AGENTS.md files and runtime state (sessions, blobs, agent.db). A profile sees only its own OMP config, never the default profile’s ~/.musepi/agent.

Keybindings are the one exception: a named profile merges the default profile’s ~/.musepi/agent/keybindings.* under its own ~/.musepi/profiles/<name>/agent/keybindings.*, with the profile file overriding per binding (#4867). Keybindings describe the terminal/keyboard in front of the user, which doesn’t change with the active profile, so user-level remaps keep working in every profile unless the profile explicitly overrides them. The inherited file is read-only for the profile process — legacy-format migration of the default profile’s file only happens when the default profile itself runs.

The other source bases are not profile-scoped and load identically under every profile: the external-tool bases (~/.claude, ~/.codex, ~/.gemini) belong to those tools, and the project-level bases (<cwd>/.musepi, <cwd>/.claude, …) are keyed to the working directory. Throughout this document, read ~/.musepi/agent as shorthand for the active profile’s agent directory.

Important constraint

The generic helpers in src/config.ts do not include .pi in source discovery order.


2) Core discovery helpers (src/config.ts)

getConfigDirs(subpath, options)

Returns ordered entries:

Options:

This API is used for directory-based config lookups (commands, hooks, tools, agents, etc.).

findConfigFile(subpath, options) / findConfigFileWithMeta(...)

Searches for the first existing file across ordered bases, returns first match (path-only or path+metadata).

findAllNearestProjectConfigDirs(subpath, cwd)

Walks parent directories upward and returns the nearest existing directory per source base (.musepi, .claude, .codex, .gemini), then sorts results by source priority.

Use this when project config should be inherited from ancestor directories (monorepo/nested workspace behavior).


3) File config wrapper (ConfigFile<T> in src/config/config-file.ts, re-exported from src/config.ts)

ConfigFile<T> is the schema-validated loader for single config files.

Supported formats:

Behavior:

Legacy migration still supported:


4) Settings resolution model (src/config/settings.ts)

The runtime settings model is layered:

  1. Global settings: ~/.musepi/agent/config.yml
  2. Project settings: discovered via settings capability (settings.json and config.yml from providers)
  3. CLI config overlays: musepi --config <path> / repeated --config files, loaded as config.yml-style YAML for this process only
  4. Runtime overrides: in-memory, non-persistent
  5. Schema defaults: from SETTINGS_SCHEMA

Effective precedence:

defaults <- global <- project <- CLI config overlays <- overrides

Write behavior:

Migration behavior still active

On startup, if config.yml is missing:

  1. Migrate from ~/.musepi/agent/settings.json (renamed to .bak on success)
  2. Merge with legacy DB settings from agent.db
  3. Write merged result to config.yml

Field-level migrations in #migrateRawSettings:


5) Capability/discovery integration

Most non-core config loading flows through the capability registry (src/capability/index.ts + src/discovery/index.ts).

Provider ordering

Providers are sorted by numeric priority (higher first). Example priorities:

Provider precedence (higher wins)

native (.musepi)          priority 100
claude                 priority  80
codex / agents / ...   priority  70
gemini                 priority  60

Dedup semantics

Capabilities define a key(item):

Relevant keys:


6) Native .musepi provider behavior (packages/coding-agent/src/discovery/builtin.ts)

Native provider (id: native) reads native config from:

Directory admission rules

Scope-specific loading

Nearest-project lookup nuance

For SYSTEM.md and AGENTS.md, native provider uses nearest-ancestor project .musepi directory search (walk-up) and still requires the project .musepi dir to be non-empty.

7) How major subsystems consume config

Settings subsystem

Session title prompt override

Create TITLE_SYSTEM.md in the same config locations as SYSTEM.md / APPEND_SYSTEM.md:

# ~/.musepi/agent/TITLE_SYSTEM.md
Generate a session name using lowercase `<type>:<primary-objective>`.

Skills subsystem

Hooks subsystem

Tools subsystem

Extensions subsystem


8) Precedence rules to rely on

Use this mental model:

  1. Source directory ordering from config.ts determines candidate path order.
  2. Capability provider priority determines cross-provider precedence.
  3. Capability key dedup determines collision behavior (first wins for keyed capabilities).
  4. Subsystem-specific merge logic can further change effective precedence (especially settings).

Settings-specific caveat

Settings capability items are not deduplicated; Settings.#loadProjectSettings() deep-merges project items in returned order. Because merge applies later item values over earlier values, effective override behavior depends on provider emission order, not just capability key semantics.


9) Legacy/compatibility behaviors still present

If these compatibility paths are removed in code, update this document immediately; several runtime behaviors still depend on them today.