Skip to main content

spaex — reproducible coding harnesses for any repo and development environment

Status: 5.0.0 (external skills remain standard skill content, declared as structured external_skills metadata and installed through an explicit consumer-selected adapter, not a molecule hook; 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.
  • spaex status: summarize the repository's active composition — every pinned/installed molecule, what its atoms materialized into, the composed constitution, and any drift between the manifest, the install lock, and the constitution. Read-only.
  • spaex trace <path>: print which molecule(s) wrote a given file, or every recorded file under a directory. Read-only.
  • spaex skills install: activate the consumer's skill_installation policy for pending external_skills references, prompting once (interactively) if none is persisted yet, then running the selected adapter. spaex install never does this on its own.
  • spaex skills configure: change the persisted skill_installation policy (mode, adapter, scope, agents) without installing anything or removing already-installed skills.

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 a structured external_skills reference and installed only through an explicit, consumer-selected adapter (spaex skills install), never a molecule 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. A molecule declares them as structured external_skills metadata: a repository, a full 40-character revision SHA, and a repository-relative skill path (co-located with the molecule or elsewhere). No install_hook is required or used for skill installation.

"external_skills": [
  {
    "repository": "https://github.com/haexmas/atoms",
    "revision": "0123456789abcdef0123456789abcdef01234567",
    "path": "skills/example-skill"
  }
]

Normal spaex install never installs or materializes a referenced skill; its structured declaration remains in the pinned molecule manifest as metadata, reported as pending. Installation is a separate, explicit, consumer-selected operation: run spaex skills install to choose a mode, adapter, scope, and agents (persisted under skill_installation in .spaex/manifest.json) and invoke the adapter, or spaex skills configure to change that policy without installing. See the Spec 018 design and its consumer policy contract for the full command and policy semantics.

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.2.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.2.0
File Size Uploaded
spaex-5.2.0.tar.gz 161.0 kB Details

Built distribution (wheel)

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

Total release size: 359.6 kB

Release files / spaex-5.2.0.tar.gz

Download URL spaex-5.2.0.tar.gz
Size 161.0 kB
Tags Source
SHA-256 checksum
How to use checksums
0ce0cee3275e2c0546409e2f79ee6405b07af43c2f14f7ad6e2a278a5fc25176
BLAKE2b-256 checksum
How to use checksums
d0dc175bff388f0d914f996d4f771d3e0badaeae7a114a7adc694f4d01820a66
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 24, 2026.

Transparency log

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

Download URL spaex-5.2.0-py3-none-any.whl
Size 198.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5b957d2b61bc1b6395988be7580d13a4e19789479571081ac84ce284496a9863
BLAKE2b-256 checksum
How to use checksums
31c8170cade786d3bcb96017fd7c37995f44c7e568521c82bdb55c700e92d62d
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 24, 2026.

Transparency log

Release history Release notifications | RSS feed

5.2.2

2 release files

This release

5.2.0 This release

2 release files

5.1.0

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