Skip to content

Architecture

ai-config has two connected products: declarative Claude plugin reconciliation and Claude-plugin conversion for other coding tools. Both enter through the Click CLI, but mutation authority stays in narrower modules.

flowchart LR
    Config[Versioned YAML] --> Observe[Observe runtime and sources]
    Runtime[Claude and target state] --> Observe
    Observe --> Plan[Immutable sync plan]
    Plan --> Apply[Validated executor]
    Sources[Claude plugin sources] --> Parse[Plugin IR]
    Parse --> Emit[Target emitters]
    Emit --> Plan
    Apply --> Tools[Claude Codex Cursor OpenCode Pi]

Responsibility boundaries

Boundary Owner Contract
Configuration config.py, types.py Parse config version 1 into frozen desired-state records; the only top-level target is Claude.
Claude runtime access Claude adapter List and mutate Claude marketplaces and plugins.
Sync planning sync_pipeline.py Transform desired state, runtime snapshot, and resolved sources into an ordered immutable plan without mutation.
Observation and execution sync_orchestration.py Collect state, validate plan preconditions, execute authorized actions, and report partial progress.
Conversion source safety source_safety.py Traverse and read plugin-root-contained regular files through retained no-follow descriptors; source hashing uses the same boundary.
Conversion orchestration Sync conversion, conversion entry point Resolve source plugins, parse once, select emitters, and retain per-target results.
Skill projection src/ai_config/converters/skill_projection.py Purely materialize immutable shared include records, exact instruction rewrites, and per-copy evidence for every target.
Target semantics Target emitters, target validators Map IR components to each target and report native, transformed, degraded, or unsupported behavior.
Codex lifecycle Codex lifecycle, Codex adapter Own generated package metadata and call Codex's marketplace/plugin lifecycle without writing shared Codex config directly.
Pi ownership pi_ownership.py Reconcile only ledger-proven output and preserve unowned or locally modified files.

Sync flow

Marketplace actions precede dependent Claude plugin actions. Configured local marketplaces remain the strict conversion-source authority after installation; remote and marketplace-less plugins may use safely observed installed sources.

When a fresh remote source does not exist until Claude installs it, the public operation coordinates two bounded immutable plans. It first applies only the exact Claude prerequisite prefix, re-observes once, then applies a conversion-only projection of the second plan. The second plan is blocked if it still requires Claude reconciliation. Executors never replan, and sync never recursively runs until quiet. Each executor rechecks its plan's runtime, source, cache, and ownership preconditions, records completed and failed actions separately, and commits checkpoints only where that plan permits them.

--dry-run renders the initial materialized plan without runtime, cache, ownership, or filesystem mutation, except for a report explicitly requested with convert --report PATH. Deferred remote conversion remains explicit because dry-run cannot inspect a source that installation has not materialized. A real --fresh clears Claude's plugin cache before observation and forces configured conversion; it does not clear target homes or ownership ledgers. A fresh dry-run does not clear anything because its mutation-free contract is stronger than simulating that altered observation. Optional verification performs one final read-only plan only after every apply stage succeeds.

Conversion flow

ClaudePluginParser normalizes the source bundle into PluginIR. Emitters independently transform that IR into target output:

  • Codex receives an ai-config-owned local marketplace and package under .ai-config/codex/, then Codex's CLI owns installation and enablement.
  • Cursor and OpenCode receive path-contained target files. Containment prevents traversal but is not a general ownership ledger.
  • Pi receives project .pi/ or user .pi/agent/ files through ledger-backed reconciliation.
  • targets/<target>/ files are copied last and may override generated bytes at the same target-relative path.
  • Skills declaring x-ai-config-includes receive byte-preserved _shared/<plugin-relative-path> copies through one target-neutral projection; generated SKILL.md strips the declaration and exact declared root references become skill-root-relative paths.

The parser and emit-time source support readers share the same fail-closed containment authority. The IR carries include bytes so emitters never reopen include source paths. Sync's source digest walks the same regular-file universe and additionally hashes metadata for the exact repository mirror CLAUDE.md -> AGENTS.md after proving the sibling target is a no-follow regular file. It never reads through the link; every other symlink and every special file still makes the source unreadable. This source-read boundary does not remove the separate pre-existing check-then-write race at the validated output path; output containment is not an atomic writer or additional ownership proof.

See Sync and Conversion Pipelines for implementation detail and Target Compatibility Baseline for runtime-specific evidence.