Skip to content

Architecture

ai-rizz is a single-file POSIX shell CLI that reconciles rule repository entries into Cursor's active configuration directories.

The mental model:

  1. Commands mutate manifests.
  2. Sync rebuilds generated Cursor directories from those manifests.
  3. The source repository cache is implementation detail, not user-facing state.

The rest of this page describes the cross-cutting flow. Specifics — manifest schema, cache paths, mode-specific output directories, conflict-resolution rules — live on their own pages and are linked inline.

System Shape

graph TD
    classDef cli fill:#1e88e5,fill-opacity:0.16,stroke:#1e88e5,stroke-width:1.5px;
    classDef state fill:#8e24aa,fill-opacity:0.16,stroke:#8e24aa,stroke-width:1.5px;
    classDef cache fill:#f57c00,fill-opacity:0.16,stroke:#f57c00,stroke-width:1.5px;
    classDef output fill:#2e7d32,fill-opacity:0.16,stroke:#2e7d32,stroke-width:1.5px;

    User["Developer"] --> CLI["ai-rizz CLI"]:::cli
    CLI -->|"reads & writes"| Manifests["Mode Manifests"]:::state
    CLI -->|"clones & pulls"| Cache["Source Repository Cache"]:::cache
    Manifests -->|"desired state"| Sync["sync_all_modes()"]:::cli
    Cache -->|"source files"| Sync
    Sync -->|"writes"| Output["Cursor Output Tree"]:::output
Hold "Alt" / "Option" to enable pan & zoom
  • Mode Manifests are the durable, user-facing state. Schema and file locations: Manifest Files.
  • Source Repository Cache is a local clone that backs all reads. Layout: Rules Cache.
  • Cursor Output Tree is regenerated by sync and should be treated as deploy output. Per-mode destinations: Rule Modes.

Command Flow

Most commands follow the same shape: validate state, pick a mode, mutate a manifest, then reconcile generated files.

sequenceDiagram
    participant U as Developer
    participant C as Command Handler
    participant M as Manifest File
    participant G as Source Cache
    participant S as Sync Reconciler
    participant O as Cursor Output

    U->>C: ai-rizz add rule foo --local
    C->>C: validate initialization and manifest integrity
    C->>C: select or lazily initialize mode
    C->>G: clone or pull source repository
    C->>M: add desired entry
    C->>S: sync_all_modes()
    S->>S: resolve conflicts
    S->>O: clear managed output for active modes
    S->>G: copy manifest entries from cache
    S-->>U: success message
Hold "Alt" / "Option" to enable pan & zoom

add and remove are intentionally manifest-first. They do not surgically edit deployed files; sync makes deployed output match the current manifests. list syncs the relevant cache and compares its contents against active manifests, reporting status via mode glyphs rather than inspecting generated files.

Sync Reconciliation

sync_all_modes() reconciles every initialized mode. For each one, sync_manifest_to_directory() clears managed output and replays manifest entries through copy_entry_to_target(), which routes by entity type.

graph TD
    classDef action fill:#1e88e5,fill-opacity:0.16,stroke:#1e88e5,stroke-width:1.5px;
    classDef decision fill:#f57c00,fill-opacity:0.16,stroke:#f57c00,stroke-width:1.5px;
    classDef output fill:#2e7d32,fill-opacity:0.16,stroke:#2e7d32,stroke-width:1.5px;

    Start["sync_all_modes()"]:::action --> Conflicts["resolve_conflicts()"]:::action
    Conflicts --> Mode["For each active mode"]:::action
    Mode --> Clear["Clear managed output"]:::action
    Clear --> Entry["For each manifest entry"]:::action
    Entry --> Kind{"Entity type?"}:::decision

    Kind -->|"*.mdc file"| Rule["Rules target"]:::output
    Kind -->|"*.md file"| Command["Commands target"]:::output
    Kind -->|"directory with SKILL.md"| Skill["Skills target"]:::output
    Kind -->|"ruleset directory"| Ruleset["Walk children"]:::action

    Ruleset --> Kind
Hold "Alt" / "Option" to enable pan & zoom

Two cross-cutting properties of this flow are easy to miss:

  • Commit wins on conflict. When the same deployed filename would land from two modes, sync keeps the committed copy and drops the local one. User-facing rules: Constraints.
  • Symlinks are followed but sandboxed. Targets must resolve inside the source cache; out-of-repository symlinks are skipped. Detail: Rulesets §Symlink Security.

Development Boundaries

The most important implementation boundary is between desired state (manifests) and generated state (Cursor output). When changing behavior, look first at the manifest mutation path and the sync path, not at ad hoc edits in generated directories.

When adding a new entity type or changing deployment behavior, expect to touch all of these areas:

  • Detection and routing helpers.
  • list display logic.
  • add and remove manifest behavior.
  • copy_entry_to_target() deployment behavior.
  • Conflict resolution and sync cleanup if filenames can overlap across modes.
  • Unit or integration tests, depending on whether the behavior is pure helper logic or filesystem/git behavior. See Testing.