┌──────────────────────────────────────────────────────────────┐
│▐▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▐│
│▐ ▌│
│▐ ██████╗██████╗ ██████╗ ▌│
│▐ ██╔════╝██╔══██╗██╔════╝ ▌│
│▐ ██║ ██████╔╝███████╗ ▌│
│▐ ██║ ██╔═══╝ ╚════██║ ▌│
│▐ ╚██████╗██║ ██████║ ▌│
│▐ ╚═════╝╚═╝ ╚═════╝ ▌│
│▐ ▌│
│▐▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▐│
│ │
│ Version 0.7.0 · Cudane Python Subsystem │
│ │
└──────────────────────────────────────────────────────────────┘
cps is the shared Python subsystem of the Cudane ecosystem. One pyo3-backed engine for plugins, themes and TUIs that every Cudane component embeds — Cesar, Context, Outsider, MCX and Leon — so a single Python skill works everywhere with the exact same contract.
Contents
The four user-space components of Cudane each grew their own copy of the same Python
machinery — src/python/{mod,plugin,theme,tui}.rs — and the copies drifted apart
(payloads, message prefixes, descriptor paths). cps is the extraction: one crate,
one contract, one engine.
A component embeds cps in four lines:
use cps::{Options, PythonConfig, PythonEngine};
cps::configure(Options::new("context"));
let engine = PythonEngine::new(&PythonConfig::default());That's it. The interpreter is booted once, a venv is activated if configured, and the
configured theme, TUI and plugins are loaded. The same code powers csr, ctx, ous,
mcx — and, as a host-side companion, Leon's boot tooling.
- One contract.
PythonConfighas the identical shape everywhere, so a[python]section means the same thing in every component's config. - Unlimited ideas, one surface. A theme or plugin written once works in Cesar's service UI, Context's shell, Outsider's build engine, MCX's package manager and Leon's boot experience.
- No drift. The five divergent copies are replaced by one crate pinned to a
Cargo.lock; every component builds with--locked. - Safe by design. The interpreter is only touched when
enabled = true, every load iscatch_unwind-wrapped, andfallback_on_error = trueby default — a broken theme can never take down a host component.
┌──────────────────────────────────────────────────────────────┐
│ cps crate │
│ │
│ PythonConfig config.rs the shared contract │
│ PythonEngine engine.rs Once boot + venv + load │
│ ThemeEngine theme.rs render_prompt / run │
│ PluginManager plugin.rs hook discovery + registry │
│ TuiEngine tui.rs run() full-screen apps │
│ Reporter lib.rs host-branded messages │
│ Options lib.rs desc dirs + reporter │
└──────────────────────────────────────────────────────────────┘
│ ▲
│ configure(Options) │ list/by_name/apply
▼ │
┌─────────────────────────┐ ┌──────────────────────────────┐
│ Host component │ │ Descriptor files │
│ (csr/ctx/ous/mcx/lbt) │ │ ~/.config/<brand>/{t,p}.desc│
└─────────────────────────┘ └──────────────────────────────┘
Two layers live in one crate:
- The engine (
PythonEngine,ThemeEngine::load,PluginManager::load_all,TuiEngine::load) — imports real Python modules and calls them. This is what a running component uses. - The registries (
ThemeEngine::list,PluginManager::by_alias,TuiEngine::run, and theregister/register_desc/unregisterfamily) — a static, in-memory index populated fromt.desc/p.desc, used by the component CLIs (cps theme list,csr plugin run, …).
A theme is a single .py module that may define (all optional):
| Function | Signature | Purpose |
|---|---|---|
render_prompt |
render_prompt(**context) -> dict | str |
returns {lines_above, input_prefix, right_prompt, colors, ...} |
render_right_prompt |
render_right_prompt(**context) -> str |
right-aligned suffix |
render_command_summary |
render_command_summary(**context) -> str |
one-line summary after a command |
run |
run() -> bool |
full-screen mode (called when tui_mode is on) |
Context keys passed to every render function include at least cwd, user, host,
exit_code, and brand; each component may add more.
A plugin is a .py module whose top-level callables become event hooks
(on_startup, on_shutdown, on_command, …). Hooks receive **context.
A TUI is a .py module exposing run().
Full authoring details: docs/PYTHON.md.
t.desc registers themes and TUIs; p.desc registers plugins. Both are searched, in
order, across the desc_dirs configured by the host component (defaults:
~/.config/<brand>, /etc/<brand>, ., plus the current directory). The first file
that exists and parses wins.
# t.desc
[theme.cps]
name = "cps"
path = "~/CPS/themes/cps.py"
description = "Default cps theme — cyan accent, host-aware prompt"
[tui.installer]
name = "Installer"
path = "~/CPS/tuis/installer.py"
description = "Full-screen package installer TUI"# p.desc
[plugin.example]
name = "Example"
path = "~/CPS/examples/example_plugin.py"
aliases = { hi = "echo 'hi from cps example plugin'" }register/register_desc/unregister write to the in-memory registry; persistent
installations are made by writing a descriptor file (or by the component's own CLI,
e.g. csr plugin install).
[python]
enabled = true
theme = "~/CPS/themes/cps.py"
tui = ""
plugins = ["~/CPS/examples/example_plugin.py"]
fallback_on_error = true
venv_path = "~/venvs/cudane"
tui_mode = false| Key | Type | Default | Meaning |
|---|---|---|---|
enabled |
bool | false |
master switch — false never touches the interpreter |
theme |
str | "" |
theme module path |
tui |
str | "" |
TUI module path |
plugins |
list | [] |
plugin module paths |
fallback_on_error |
bool | true |
degrade to native on any Python failure |
venv_path |
str | "" |
optional venv whose site-packages is activated |
tui_mode |
bool | false |
invoke the theme's run() after startup |
The cps CLI reads its config from ~/.config/cps/config.toml, /etc/cps/config.toml
or ./cps.toml (first match wins), matching how every component loads its own.
| Component | Brand | Dep | Seam |
|---|---|---|---|
| Cesar | cesar |
cps = { path = "../CPS" } |
csr plugin/theme/tui commands |
| Context | context |
cps = { path = "../CPS" } |
prompt rendering + on_startup |
| Outsider | ous |
cps = { path = "../CPS" } |
build-aware prompt + theme registry |
| MCX | mcx |
cps = { path = "../CPS" } |
package-aware prompt + plugin aliases |
| Leon | lbt |
cps = { path = "../CPS" } |
boot companion: splash/theme authoring + BootInfo preview |
Each component:
- depends on cps via
path = "../CPS"and drops its ownpyo3dep; - calls
cps::configure(Options::new("<brand>"))once at startup (optionally with aReporteradapter to keep its own message style); - replaces
crate::python::…imports withcps::…and deletes itssrc/python/.
cps theme list | apply <name> | register <name> <path> | unregister <name>
cps plugin list | run <alias> … | register <name> <path> | unregister <name>
cps tui list | apply <name> | register <name> <path> | unregister <name>
cps engine [--config <path>] # boot the engine and render a sample prompt
The CLI is both a self-contained demo of the subsystem and the template for the Leon boot companion.
Requires the Cudane musl toolchain (clang targeting x86_64|aarch64-unknown-linux-musl)
and a Rust toolchain with pyo3 (Python 3 headers available).
make # or: ninja | meson setup build && ninja -C build
make clippy # cargo clippy --all-targets -- -D warnings
make test # cargo test --lockedAll build paths pass --locked and target the musl sysroot, exactly like the rest of
the ecosystem. cross.txt is regenerated with ./gen-cross.sh.
make install DESTDIR=/mnt/rootfs # installs /system/bin/cps + share/cps dataInstalls the cps binary to ${PREFIX}/bin/cps and its themes, descriptors and
sample config to ${PREFIX}/share/cps.
cargo test --lockedThe suite covers the pure-Rust behaviour (config parsing, ~ expansion, the static
registries, the disabled-engine path) and runs without a live interpreter. Engine
tests that touch Python live alongside the component suites.
src/lib.rs Reporter trait, Options, configure, module wiring
src/config.rs PythonConfig — the shared contract
src/engine.rs PythonEngine — Once boot + venv + load
src/paths.rs ~ expansion, desc candidates, venv activation
src/theme.rs ThemeEngine, ThemeResult, theme registry
src/plugin.rs PluginManager, PluginEntry, plugin registry
src/tui.rs TuiEngine, TuiEntry, tui registry
src/bin/cps.rs reference CLI (theme/plugin/tui/engine)
themes/ cps.py, minimal.py
examples/ example_plugin.py
tests/ integration tests (no interpreter needed)
docs/ authoring guide
pyo30.25 — Python bindingsserde/toml0.8 — descriptor + config parsingclap4 — the reference CLI
cps lives at ~/CPS. It is versioned with the ecosystem (0.7.0) and follows the
Cudane build conventions. Keep the engine feature-complete but dependency-free of any
one component: host-specific behaviour belongs behind Reporter / Options, never
hardcoded.
MIT License · Copyright (c) 2026 Mapuse