spaex 4.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spaex-4.0.0/PKG-INFO +111 -0
- spaex-4.0.0/README.md +96 -0
- spaex-4.0.0/pyproject.toml +57 -0
- spaex-4.0.0/setup.cfg +4 -0
- spaex-4.0.0/src/spaex/__init__.py +0 -0
- spaex-4.0.0/src/spaex/__main__.py +10 -0
- spaex-4.0.0/src/spaex/cli/__init__.py +0 -0
- spaex-4.0.0/src/spaex/cli/add.py +423 -0
- spaex-4.0.0/src/spaex/cli/constitution.py +53 -0
- spaex-4.0.0/src/spaex/cli/diagnostics.py +42 -0
- spaex-4.0.0/src/spaex/cli/install.py +234 -0
- spaex-4.0.0/src/spaex/cli/main.py +159 -0
- spaex-4.0.0/src/spaex/cli/migrate.py +256 -0
- spaex-4.0.0/src/spaex/cli/remove.py +156 -0
- spaex-4.0.0/src/spaex/constitution/__init__.py +0 -0
- spaex-4.0.0/src/spaex/constitution/publish.py +236 -0
- spaex-4.0.0/src/spaex/constitution/resolve.py +171 -0
- spaex-4.0.0/src/spaex/constitution/safety.py +104 -0
- spaex-4.0.0/src/spaex/constitution/show.py +67 -0
- spaex-4.0.0/src/spaex/git/__init__.py +0 -0
- spaex-4.0.0/src/spaex/git/publisher_fetch.py +183 -0
- spaex-4.0.0/src/spaex/git/remote.py +23 -0
- spaex-4.0.0/src/spaex/git/revparse.py +32 -0
- spaex-4.0.0/src/spaex/git/show.py +33 -0
- spaex-4.0.0/src/spaex/install/__init__.py +1 -0
- spaex-4.0.0/src/spaex/install/delta.py +1 -0
- spaex-4.0.0/src/spaex/install/errors.py +14 -0
- spaex-4.0.0/src/spaex/install/generation.py +39 -0
- spaex-4.0.0/src/spaex/install/inflight.py +125 -0
- spaex-4.0.0/src/spaex/install/lock.py +96 -0
- spaex-4.0.0/src/spaex/install/manifest_lock.py +222 -0
- spaex-4.0.0/src/spaex/install/overlay.py +1 -0
- spaex-4.0.0/src/spaex/install/write_and_reinstall.py +79 -0
- spaex-4.0.0/src/spaex/io/__init__.py +0 -0
- spaex-4.0.0/src/spaex/io/atomic.py +109 -0
- spaex-4.0.0/src/spaex/io/json_deterministic.py +45 -0
- spaex-4.0.0/src/spaex/io/state.py +110 -0
- spaex-4.0.0/src/spaex/io/transaction.py +221 -0
- spaex-4.0.0/src/spaex/io/writer_lock.py +295 -0
- spaex-4.0.0/src/spaex/migrate/__init__.py +0 -0
- spaex-4.0.0/src/spaex/migrate/detect.py +34 -0
- spaex-4.0.0/src/spaex/migrate/registry.py +62 -0
- spaex-4.0.0/src/spaex/migrate/sidecar.py +38 -0
- spaex-4.0.0/src/spaex/migrate/transform.py +420 -0
- spaex-4.0.0/src/spaex/migrate/v2_to_v3.py +329 -0
- spaex-4.0.0/src/spaex/migrate/v3_to_v4.py +242 -0
- spaex-4.0.0/src/spaex/migrate/walker.py +138 -0
- spaex-4.0.0/src/spaex/model/__init__.py +0 -0
- spaex-4.0.0/src/spaex/model/_immutable.py +23 -0
- spaex-4.0.0/src/spaex/model/consumer_manifest.py +164 -0
- spaex-4.0.0/src/spaex/model/install_lock.py +191 -0
- spaex-4.0.0/src/spaex/model/molecule_id.py +38 -0
- spaex-4.0.0/src/spaex/model/molecule_manifest.py +75 -0
- spaex-4.0.0/src/spaex/model/publisher_manifest.py +57 -0
- spaex-4.0.0/src/spaex/model/repo_relative_path.py +38 -0
- spaex-4.0.0/src/spaex/model/source_url.py +84 -0
- spaex-4.0.0/src/spaex/model/version_constraint.py +42 -0
- spaex-4.0.0/src/spaex/schema/__init__.py +0 -0
- spaex-4.0.0/src/spaex/schema/data/consumer-manifest.v4.schema.json +106 -0
- spaex-4.0.0/src/spaex/schema/data/install-lock.v4.schema.json +57 -0
- spaex-4.0.0/src/spaex/schema/data/molecule-manifest.v4.schema.json +58 -0
- spaex-4.0.0/src/spaex/schema/data/publisher-manifest.v4.schema.json +59 -0
- spaex-4.0.0/src/spaex/schema/loader.py +25 -0
- spaex-4.0.0/src/spaex/schema/validator.py +140 -0
- spaex-4.0.0/src/spaex/util/__init__.py +0 -0
- spaex-4.0.0/src/spaex/util/errors.py +328 -0
- spaex-4.0.0/src/spaex/util/exit_codes.py +52 -0
- spaex-4.0.0/src/spaex.egg-info/PKG-INFO +111 -0
- spaex-4.0.0/src/spaex.egg-info/SOURCES.txt +71 -0
- spaex-4.0.0/src/spaex.egg-info/dependency_links.txt +1 -0
- spaex-4.0.0/src/spaex.egg-info/entry_points.txt +2 -0
- spaex-4.0.0/src/spaex.egg-info/requires.txt +7 -0
- spaex-4.0.0/src/spaex.egg-info/top_level.txt +1 -0
spaex-4.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: spaex
|
|
3
|
+
Version: 4.0.0
|
|
4
|
+
Summary: spaex 4.0: reproducible coding harnesses for any repo. Compose skills, MCPs, constitutions, slash commands, and dev-environment files into a single project; add/remove/install/migrate/constitution show CLI.
|
|
5
|
+
Author: spaex contributors
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: jsonschema>=4.18
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
12
|
+
Requires-Dist: pytest-subprocess>=1.5; extra == "dev"
|
|
13
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
14
|
+
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
15
|
+
|
|
16
|
+
# spaex — reproducible coding harnesses for any repo and development environment
|
|
17
|
+
|
|
18
|
+
**Status**: `4.0.0.dev0` (v4 vocabulary landed as part of Spec 014, first PyPI release upcoming). Portmanteau of `spec` and `haex`. See [docs/adr/0011-rename-to-spaex.md](docs/adr/0011-rename-to-spaex.md) for the rename decision and [specs/014-rename-to-spaex/](specs/014-rename-to-spaex/) for the full spec.
|
|
19
|
+
|
|
20
|
+
## What it is
|
|
21
|
+
|
|
22
|
+
spaex composes a coding harness for a single repo out of reusable pieces (skills, MCPs, constitutions, slash commands, dev-environment files, collectively "molecules"). You declare which molecules you want in `.spaex.json`. `spaex install` writes them into `.claude/`, `.codex/`, `.spaex/`, and any other participating roots deterministically, pinned by SHA. Two consecutive `spaex install` runs on unchanged inputs produce byte-identical output.
|
|
23
|
+
|
|
24
|
+
## What you can do today
|
|
25
|
+
|
|
26
|
+
- `spaex add <source-url> <molecule-ids...>`: adopt one or more molecules from a publisher repo into `.spaex.json` and install them in one invocation.
|
|
27
|
+
- `spaex remove <molecule-ids...>`: retract one or more molecules from `.spaex.json` and re-run install (files that only the retracted molecule contributed are deleted).
|
|
28
|
+
- `spaex install`: publish adopted molecules atomically into their participating roots. Writes `.spaex/install.lock`.
|
|
29
|
+
- `spaex migrate`: read v1/v2/v3 legacy manifests (`.haex-hive.json`, `manifest.json`) and emit v4 `.migrated` sidecar proposals with adoption instructions.
|
|
30
|
+
- `spaex constitution show`: print the effective constitution to stdout, assembled from adopted molecules per `install.lock`.
|
|
31
|
+
|
|
32
|
+
## Atom-category conventions
|
|
33
|
+
|
|
34
|
+
The v4 molecule-manifest schema treats `atoms{}` as an open `Dict[str, List[str]]` map. Publishers pick category names by convention. Common categories today: `constitution`, `slash_commands`, `agents`, `mcps`.
|
|
35
|
+
|
|
36
|
+
**Environment-config files** (`flake.nix`, `Dockerfile`, `devcontainer.json`, `.envrc`, `shell.nix`, etc.) can be declared under any category name a publisher chooses. Spec 014 makes no 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](docs/plans/2026-09-07-slot-015-multi-environment-placeholder.md)).
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
**Once published to PyPI (upcoming with the `v4.0.0` tag):**
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pipx install spaex
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**From a local checkout (development):**
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
git clone https://github.com/haexmas/haex-hive.git
|
|
50
|
+
cd haex-hive
|
|
51
|
+
pip install -e '.[dev]'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Requires Python 3.10+ and Git 2.30+ on `$PATH`. Only runtime dependency is `jsonschema`.
|
|
55
|
+
|
|
56
|
+
## Migrating from haex-hive v3
|
|
57
|
+
|
|
58
|
+
If your project has a `.haex-hive.json` with `haex_hive_version: "3"`:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pipx install spaex
|
|
62
|
+
cd /path/to/your/project
|
|
63
|
+
spaex migrate
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`spaex migrate` walks the repo and writes `.migrated` siblings for every v3 (or v1/v2) manifest it finds:
|
|
67
|
+
|
|
68
|
+
- `.haex-hive.json` (v1/v2/v3) → `.spaex.json.migrated`
|
|
69
|
+
- publisher-root `manifest.json` → `manifest.json.migrated`
|
|
70
|
+
- per-molecule `manifest.json` → `manifest.json.migrated`
|
|
71
|
+
|
|
72
|
+
Review the printed diffs. When satisfied, adopt each proposal:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# consumer
|
|
76
|
+
mv .spaex.json.migrated .spaex.json
|
|
77
|
+
rm .haex-hive.json
|
|
78
|
+
|
|
79
|
+
# publisher-root and each molecule
|
|
80
|
+
mv path/to/manifest.json.migrated path/to/manifest.json
|
|
81
|
+
|
|
82
|
+
# runtime output (safe to delete; regenerated by install)
|
|
83
|
+
rm -rf .haex-hive/
|
|
84
|
+
|
|
85
|
+
spaex install
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
After `spaex install` completes, `.spaex/install.lock` is present and byte-identical across two consecutive runs.
|
|
89
|
+
|
|
90
|
+
## The v4 vocabulary at a glance
|
|
91
|
+
|
|
92
|
+
- **Compound** (`.spaex.json.compounds[]`): a `(source, revision)` pair with a list of adopted `molecules[]`. The consumer's allowlist.
|
|
93
|
+
- **Molecule**: a published, reverse-DNS-identified bundle that a publisher declares in its root `manifest.json` under `molecules{}`.
|
|
94
|
+
- **Atom**: a single delivered file, grouped under a category key in a molecule's `manifest.json` `atoms{}` map.
|
|
95
|
+
|
|
96
|
+
This vocabulary (compounds -> molecules -> atoms) was introduced by Spec 013 and is unchanged in v4. The v4 delta is limited to two field-name renames: `haex_hive_version` -> `spaex_version` (value `"3"` -> `"4"`) and `haex_hive_min_version` -> `spaex_min_version`.
|
|
97
|
+
|
|
98
|
+
## Multi-device delegation
|
|
99
|
+
|
|
100
|
+
Not part of spaex. That is a separate project: [holzi](https://github.com/haexmas/holzi) (Nostr + iroh + MCP agent plane, single-user first). spaex is deliberately scoped to one repo, one device.
|
|
101
|
+
|
|
102
|
+
## Environment variable
|
|
103
|
+
|
|
104
|
+
`spaex` honors `$SPAEX_STATE` for the per-invocation state directory (publisher clones, migration proposals). Unset falls back to `~/.local/share/spaex/`.
|
|
105
|
+
|
|
106
|
+
## Documentation
|
|
107
|
+
|
|
108
|
+
- Every spec under [specs/](specs/) is authoritative for the mechanism it introduces.
|
|
109
|
+
- Design plans under [docs/plans/](docs/plans/) capture pre-spec requirements.
|
|
110
|
+
- Architecture Decision Records under [docs/adr/](docs/adr/) record decisions that reshape the system.
|
|
111
|
+
- The constitution at [.specify/memory/constitution.md](.specify/memory/constitution.md) is the non-negotiable invariant set every spec, plan, and implementation MUST respect.
|
spaex-4.0.0/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# spaex — reproducible coding harnesses for any repo and development environment
|
|
2
|
+
|
|
3
|
+
**Status**: `4.0.0.dev0` (v4 vocabulary landed as part of Spec 014, first PyPI release upcoming). Portmanteau of `spec` and `haex`. See [docs/adr/0011-rename-to-spaex.md](docs/adr/0011-rename-to-spaex.md) for the rename decision and [specs/014-rename-to-spaex/](specs/014-rename-to-spaex/) for the full spec.
|
|
4
|
+
|
|
5
|
+
## What it is
|
|
6
|
+
|
|
7
|
+
spaex composes a coding harness for a single repo out of reusable pieces (skills, MCPs, constitutions, slash commands, dev-environment files, collectively "molecules"). You declare which molecules you want in `.spaex.json`. `spaex install` writes them into `.claude/`, `.codex/`, `.spaex/`, and any other participating roots deterministically, pinned by SHA. Two consecutive `spaex install` runs on unchanged inputs produce byte-identical output.
|
|
8
|
+
|
|
9
|
+
## What you can do today
|
|
10
|
+
|
|
11
|
+
- `spaex add <source-url> <molecule-ids...>`: adopt one or more molecules from a publisher repo into `.spaex.json` and install them in one invocation.
|
|
12
|
+
- `spaex remove <molecule-ids...>`: retract one or more molecules from `.spaex.json` and re-run install (files that only the retracted molecule contributed are deleted).
|
|
13
|
+
- `spaex install`: publish adopted molecules atomically into their participating roots. Writes `.spaex/install.lock`.
|
|
14
|
+
- `spaex migrate`: read v1/v2/v3 legacy manifests (`.haex-hive.json`, `manifest.json`) and emit v4 `.migrated` sidecar proposals with adoption instructions.
|
|
15
|
+
- `spaex constitution show`: print the effective constitution to stdout, assembled from adopted molecules per `install.lock`.
|
|
16
|
+
|
|
17
|
+
## Atom-category conventions
|
|
18
|
+
|
|
19
|
+
The v4 molecule-manifest schema treats `atoms{}` as an open `Dict[str, List[str]]` map. Publishers pick category names by convention. Common categories today: `constitution`, `slash_commands`, `agents`, `mcps`.
|
|
20
|
+
|
|
21
|
+
**Environment-config files** (`flake.nix`, `Dockerfile`, `devcontainer.json`, `.envrc`, `shell.nix`, etc.) can be declared under any category name a publisher chooses. Spec 014 makes no 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](docs/plans/2026-09-07-slot-015-multi-environment-placeholder.md)).
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
**Once published to PyPI (upcoming with the `v4.0.0` tag):**
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pipx install spaex
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**From a local checkout (development):**
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git clone https://github.com/haexmas/haex-hive.git
|
|
35
|
+
cd haex-hive
|
|
36
|
+
pip install -e '.[dev]'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Requires Python 3.10+ and Git 2.30+ on `$PATH`. Only runtime dependency is `jsonschema`.
|
|
40
|
+
|
|
41
|
+
## Migrating from haex-hive v3
|
|
42
|
+
|
|
43
|
+
If your project has a `.haex-hive.json` with `haex_hive_version: "3"`:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pipx install spaex
|
|
47
|
+
cd /path/to/your/project
|
|
48
|
+
spaex migrate
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`spaex migrate` walks the repo and writes `.migrated` siblings for every v3 (or v1/v2) manifest it finds:
|
|
52
|
+
|
|
53
|
+
- `.haex-hive.json` (v1/v2/v3) → `.spaex.json.migrated`
|
|
54
|
+
- publisher-root `manifest.json` → `manifest.json.migrated`
|
|
55
|
+
- per-molecule `manifest.json` → `manifest.json.migrated`
|
|
56
|
+
|
|
57
|
+
Review the printed diffs. When satisfied, adopt each proposal:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# consumer
|
|
61
|
+
mv .spaex.json.migrated .spaex.json
|
|
62
|
+
rm .haex-hive.json
|
|
63
|
+
|
|
64
|
+
# publisher-root and each molecule
|
|
65
|
+
mv path/to/manifest.json.migrated path/to/manifest.json
|
|
66
|
+
|
|
67
|
+
# runtime output (safe to delete; regenerated by install)
|
|
68
|
+
rm -rf .haex-hive/
|
|
69
|
+
|
|
70
|
+
spaex install
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
After `spaex install` completes, `.spaex/install.lock` is present and byte-identical across two consecutive runs.
|
|
74
|
+
|
|
75
|
+
## The v4 vocabulary at a glance
|
|
76
|
+
|
|
77
|
+
- **Compound** (`.spaex.json.compounds[]`): a `(source, revision)` pair with a list of adopted `molecules[]`. The consumer's allowlist.
|
|
78
|
+
- **Molecule**: a published, reverse-DNS-identified bundle that a publisher declares in its root `manifest.json` under `molecules{}`.
|
|
79
|
+
- **Atom**: a single delivered file, grouped under a category key in a molecule's `manifest.json` `atoms{}` map.
|
|
80
|
+
|
|
81
|
+
This vocabulary (compounds -> molecules -> atoms) was introduced by Spec 013 and is unchanged in v4. The v4 delta is limited to two field-name renames: `haex_hive_version` -> `spaex_version` (value `"3"` -> `"4"`) and `haex_hive_min_version` -> `spaex_min_version`.
|
|
82
|
+
|
|
83
|
+
## Multi-device delegation
|
|
84
|
+
|
|
85
|
+
Not part of spaex. That is a separate project: [holzi](https://github.com/haexmas/holzi) (Nostr + iroh + MCP agent plane, single-user first). spaex is deliberately scoped to one repo, one device.
|
|
86
|
+
|
|
87
|
+
## Environment variable
|
|
88
|
+
|
|
89
|
+
`spaex` honors `$SPAEX_STATE` for the per-invocation state directory (publisher clones, migration proposals). Unset falls back to `~/.local/share/spaex/`.
|
|
90
|
+
|
|
91
|
+
## Documentation
|
|
92
|
+
|
|
93
|
+
- Every spec under [specs/](specs/) is authoritative for the mechanism it introduces.
|
|
94
|
+
- Design plans under [docs/plans/](docs/plans/) capture pre-spec requirements.
|
|
95
|
+
- Architecture Decision Records under [docs/adr/](docs/adr/) record decisions that reshape the system.
|
|
96
|
+
- The constitution at [.specify/memory/constitution.md](.specify/memory/constitution.md) is the non-negotiable invariant set every spec, plan, and implementation MUST respect.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "spaex"
|
|
7
|
+
version = "4.0.0"
|
|
8
|
+
description = "spaex 4.0: reproducible coding harnesses for any repo. Compose skills, MCPs, constitutions, slash commands, and dev-environment files into a single project; add/remove/install/migrate/constitution show CLI."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "Apache-2.0" }
|
|
12
|
+
authors = [{ name = "spaex contributors" }]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"jsonschema>=4.18",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
spaex = "spaex.cli.main:main"
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
dev = [
|
|
22
|
+
"pytest>=7.4",
|
|
23
|
+
"pytest-subprocess>=1.5",
|
|
24
|
+
"ruff>=0.5",
|
|
25
|
+
"mypy>=1.8",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[tool.setuptools]
|
|
29
|
+
package-dir = { "" = "src" }
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.package-data]
|
|
35
|
+
spaex = ["schema/data/*.json"]
|
|
36
|
+
|
|
37
|
+
[tool.ruff]
|
|
38
|
+
line-length = 100
|
|
39
|
+
target-version = "py310"
|
|
40
|
+
|
|
41
|
+
[tool.ruff.lint]
|
|
42
|
+
select = ["E", "F", "W", "I", "UP", "B", "SIM"]
|
|
43
|
+
ignore = []
|
|
44
|
+
|
|
45
|
+
[tool.mypy]
|
|
46
|
+
python_version = "3.10"
|
|
47
|
+
strict = true
|
|
48
|
+
packages = ["spaex"]
|
|
49
|
+
mypy_path = "src"
|
|
50
|
+
|
|
51
|
+
[tool.pytest.ini_options]
|
|
52
|
+
testpaths = ["tests"]
|
|
53
|
+
pythonpath = ["src"]
|
|
54
|
+
markers = [
|
|
55
|
+
"slow: opt-in benchmarks and heavy end-to-end scenarios",
|
|
56
|
+
]
|
|
57
|
+
addopts = "-m 'not slow'"
|
spaex-4.0.0/setup.cfg
ADDED
|
File without changes
|
|
File without changes
|