Skip to main content

spaex — reproducible coding harnesses for any repo and development environment

Status: 5.0.0 (external skills remain standard skill content but are delegated to upstream installers through molecule hooks; see Spec 018). Portmanteau of spec and haex. See docs/adr/0011-rename-to-spaex.md for the rename decision and specs/014-rename-to-spaex/ for the full spec.

What it is

spaex composes a coding harness for a single repo out of reusable pieces (MCPs, constitutions, slash commands, dev-environment files, and external skill references, collectively "molecules"). You declare which molecules you want in .spaex/manifest.json. All spaex-owned project state lives under .spaex/; spaex install writes participating runtime files deterministically, pinned by SHA. Two consecutive spaex install runs on unchanged inputs produce byte-identical output.

What you can do today

  • spaex add <source-url> <molecule-ids...>: adopt one or more molecules from a publisher repo into .spaex/manifest.json and install them in one invocation.
  • spaex remove <molecule-ids...>: retract one or more molecules from .spaex/manifest.json and re-run install (files that only the retracted molecule contributed are deleted).
  • spaex install: publish adopted molecules atomically into their participating roots. Writes .spaex/install.lock.
  • spaex constitution show: print the effective spaex constitution to stdout, assembled from adopted molecules per install.lock.

Molecule install-hooks

A molecule may declare an optional install_hook in its manifest.json. spaex install invokes it as a normal subprocess (arbitrary code, consumer-user permissions, full environment inheritance) after any required atom materialization and before publishing the install.lock generation; hook-only molecules run without atom materialization. Per-molecule on_failure: "abort" | "warn" selects between transaction rollback and continue-with-hook_status-recorded. Consumers can opt out for a single invocation via spaex install --no-install-hooks (or spaex add --no-install-hooks). See docs/install-hooks.md for the full contract: declaration schema, execution semantics, the four failure kinds, idempotency, non-reversibility, and the trust model.

Atom-category conventions

The v4 molecule-manifest schema treats atoms{} as an open Dict[str, List[str]] map. Publishers pick category names by convention. Common categories today: behavior, slash_commands, agents, mcps. The legacy constitution category remains readable for older molecules but is not the canonical way to contribute policy.

Environment-config files (flake.nix, Dockerfile, devcontainer.json, .envrc, shell.nix, etc.) can be declared under any category name a publisher chooses. The retired skill and skills categories are the one exception: a skill may still live in the publisher repository, but is declared as an external_skills reference and installed by the molecule's install_hook. Spec 014 makes no other naming commitment here; multi-environment vocabulary (dev/staging/prod), consumer-side selection, and orchestration verbs are the scope of Spec 015 (planned; see docs/plans/2026-09-07-slot-015-multi-environment-placeholder.md).

Activating an adopted Nix devShell

A molecule that delivers flake.nix/.envrc under an environment-config category (e.g. com.github.haexmas.atoms.nix-devshell-base) only writes those files; it does not — and should not — provision Nix or direnv themselves, since that needs interactive root access spaex should not attempt unattended. Three preconditions are easy to miss on a fresh machine after spaex install:

  1. Flakes must be enabled. A default Nix install has nix-command and flakes behind the experimental-features flag — without it, nix develop/direnv's use flake fail with experimental Nix feature 'nix-command' is disabled. Enable it per-user, no root needed:

    mkdir -p ~/.config/nix
    echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
    
  2. direnv itself must be installed (Nix does not bring it in): sudo pacman -S direnv, sudo apt install direnv, brew install direnv, or nix profile install nixpkgs#direnv.

  3. direnv must be hooked into your shell, then open a new shell:

    # bash (~/.bashrc) / zsh (~/.zshrc)
    eval "$(direnv hook bash)"   # or: zsh
    
    # fish (~/.config/fish/config.fish)
    direnv hook fish | source
    

With all three in place, cd into the consumer repo and run direnv allow once; direnv builds the devShell and loads its tools into PATH automatically on every subsequent cd. Without direnv, nix develop (with flakes enabled per step 1) drops into an equivalent shell manually.

Install

Once published to PyPI (upcoming with the v5.0.0 tag):

pipx install spaex

From a local checkout (development):

git clone https://github.com/haexmas/spaex.git
cd spaex
pip install -e '.[dev]'

Requires Python 3.14.x and Git 2.30+ on $PATH. spaex includes uv for isolated, pinned CLI provisioning; jsonschema and pyyaml are the other runtime dependencies.

External skills

Molecules may keep standard SKILL.md directories in the publisher repository, including haexmas/atoms. They are not copied by spaex's atom materializer. The current 5.0.0 implementation uses opaque external_skills references and molecule hooks. The Spec 018 design update proposes structured source metadata and explicit spaex skills install / spaex skills configure commands with consumer-selected installer, agent, and scope. Those commands and the skill_installation policy are not implemented yet; the update is documentation-only.

After spaex install completes, .spaex/install.lock is present and byte-identical across two consecutive runs.

The spaex vocabulary at a glance

  • Compound (.spaex/manifest.json.compounds[]): a (source, revision) pair with a list of adopted molecules[]. The consumer's allowlist.
  • Molecule: a published, reverse-DNS-identified bundle that a publisher declares in its root manifest.json under molecules{}.
  • Atom: a single delivered file, grouped under a category key in a molecule's manifest.json atoms{} map.

This vocabulary (compounds -> molecules -> atoms) is the clean-cut spaex format. There is no migration command or legacy manifest fallback.

Multi-device delegation

Not part of spaex. That is a separate project: holzi (Nostr + iroh + MCP agent plane, single-user first). spaex is deliberately scoped to one repo, one device.

Environment variable

spaex honors $SPAEX_STATE for the per-invocation publisher-clone state directory. Unset falls back to ~/.local/share/spaex/.

Documentation

  • Every spec under specs/ is authoritative for the mechanism it introduces.
  • Design plans under docs/plans/ capture pre-spec requirements.
  • Architecture Decision Records under docs/adr/ record decisions that reshape the system.
  • The constitution at .specify/memory/constitution.md is the non-negotiable invariant set every spec, plan, and implementation MUST respect.

Release files for spaex 5.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for spaex 5.1.0
File Size Uploaded
spaex-5.1.0.tar.gz 146.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for spaex 5.1.0
File Interpreter ABI Platform
spaex-5.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 328.0 kB

Release files / spaex-5.1.0.tar.gz

Download URL spaex-5.1.0.tar.gz
Size 146.8 kB
Tags Source
SHA-256 checksum
How to use checksums
b6201bd0c868d0ece96dbff808fa59a972f5b70f5bab91b631f698ca61197368
BLAKE2b-256 checksum
How to use checksums
b8ac7f8706ffbe8d0faad01e747ac3ed83f9fa057f156bbe6feb62031454f106
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release files / spaex-5.1.0-py3-none-any.whl

Download URL spaex-5.1.0-py3-none-any.whl
Size 181.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d287687bfc938b9e68ecd307281c6e1b4196e3db75b402c0ae8335b559149ffe
BLAKE2b-256 checksum
How to use checksums
fb131a17421860a0f5ea59c439a28b1d8776b62cbaa8522a1d7fa29231a792a2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release history Release notifications | RSS feed

5.2.2

2 release files

5.2.0

2 release files

This release

5.1.0 This release

2 release files

5.0.0

2 release files

4.3.0

2 release files

4.2.0

2 release files

4.1.0

2 release files

4.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page