Skip to content

Commit 092a453

Browse files
authored
refactor: hoist installation-editing from 'soliplex-concierge' (#1)
... fixing its #79 bugs as the code was ported. - 'sections' (new module) maps out sematncis of section types in a Soliplex installation configl - 'installer' (new module) provides mechanisms and a grammar for performing edits (adding entries) respecting those semantics. - 'rooms.install_room' and 'rooms.install_room_from' now take a required 'parent_path', and ensure that adding a room is done cleanly, without e.g. hiding existing rooms or surfacing rooms which had been excluded. See: soliplex/soliplex-concierge#79
1 parent 0e423e0 commit 092a453

10 files changed

Lines changed: 1436 additions & 198 deletions

File tree

docs/reference/api.md

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,60 @@ There is no re-exporting package `__init__` — **client code imports the
99
submodule and uses its members by dotted name** (e.g. `from soliplex_plumber
1010
import rooms` then `rooms.install_room(...)`).
1111

12-
## `rooms`add a room to a Soliplex stack
12+
## `sections`the installation.yaml section catalog
1313

14-
Generic, template-agnostic, stdlib-only logic for wiring a room into a generated
15-
stack. The shared core behind both the `soliplex-template` skill's `add_room.py`
16-
and the `soliplex-concierge` installer; it edits `installation.yaml` line-based so
17-
comments and layout are preserved.
14+
Declarative facts about each editable `installation.yaml` section, mirroring the
15+
defaults the backend applies in `soliplex.config.installation` (the source of
16+
truth). The editors consult it to decide an absent section's behavior.
17+
18+
| Member | Purpose |
19+
| --- | --- |
20+
| `Family` (`DISCOVERY` / `COLLECTION` / `WHITELIST`) | how a section behaves when an entry is added |
21+
| `Section(key, family, parent=None, discovery_default=None, kind_field=None)` | one section's declarative facts |
22+
| `ROOM_PATHS` / `ENVIRONMENT` / `SECRETS` / `META_TOOL_CONFIGS` / `SKILL_CONFIGS` | the catalog entries |
23+
| `COMPOSE_FILE` / `ENVIRONMENT_DIR` / `INSTALLATION_FILE` / `ROOMS_DIR` / `STACK_MARKERS` | stack-structure constants (re-exported by `rooms`) |
24+
25+
The families: **discovery** lists default to a directory (`room_paths`
26+
`["./rooms"]`), so an entry beneath the default is already *covered* when the
27+
section is absent; **collection** sections default to empty, so an absent section
28+
is *created* to add an entry; **whitelist** (`skill_configs`) is permissive per
29+
`kind` — empty/absent for a kind enables every discovered skill of it.
30+
31+
## `installation` — edit a stack's `installation.yaml`
32+
33+
Generic, comment-preserving, idempotent, stdlib-only line editors. Each is a
34+
pure `text -> (new_text, TargetAction)` function. Scanning is **section-scoped**
35+
and **comment-skipping**.
36+
37+
| Member | Purpose |
38+
| --- | --- |
39+
| `add_list_entry(text, *, section, block, probe)` | add `block` under a top-level collection `section:`; create it if absent |
40+
| `add_nested_list_entry(text, *, parent, section, item, probe)` | add `item` under nested `parent.section:`; create parent/child if absent |
41+
| `add_environment(text, var_name)` | add `var_name` to `environment:` |
42+
| `add_secret(text, secret_name, *, env_var_name=None)` | add an `env_var`-sourced secret to `secrets:` |
43+
| `add_meta_tool_config(text, class_path)` | register `class_path` under nested `meta.tool_configs:` |
44+
| `add_skill_config(text, skill_name, *, kind="filesystem", confirm=False)` | whitelist a skill; `COVERED` when the kind is permissive; raises `WhitelistActive` when a kind's whitelist is active (unless `confirm`) |
45+
| `resolve_stack(stack_dir, markers, error)` | resolve a stack root requiring every marker; `error(stack, marker)` builds the raised exception |
46+
| `WhitelistActive(kind, entries)` | the one "stop and confirm" abort — a `skill_configs` kind already has an explicit whitelist |
47+
| `section_span` / `is_item` / `append_section` | low-level scoped, comment-aware scan helpers (shared with `rooms`) |
48+
| `TargetAction(StrEnum)` (`ADDED` / `UNCHANGED` / `COVERED`) | the action a helper reports |
49+
50+
## `rooms` — install a room into a Soliplex stack
51+
52+
Writes a room under an explicit `parent_path` (relative to the installation
53+
config) and wires its `room_paths` entry line-based. Append-only: installing a
54+
room never disables another (the `./rooms` default is materialized before any
55+
non-default parent is added).
1856

1957
| Member | Purpose |
2058
| --- | --- |
2159
| `validate_room_id(room_id)` | enforce the room-id / path-segment rule (`ROOM_ID_RE`); raises `AddRoomError` |
2260
| `resolve_project(project_dir)` | resolve + verify the stack root (has `COMPOSE_FILE` and `INSTALLATION_FILE`) |
2361
| `resolve_package_name(project, override)` | the stack's own package (inferred from `src/<pkg>/tools.py`) or `DEFAULT_PACKAGE_NAME` |
24-
| `add_room_path(text, room_id) -> (text, action)` | ensure `room_paths` loads the room; action is `ADDED` / `UNCHANGED` / `COVERED` |
25-
| `install_room(project, room_id, *, config_text, prompt_text=None, force=False, dry_run=False)` | write the room dir + config (+ optional prompt) and apply the `room_paths` edit |
26-
| `RoomInstalled(config_path, path_action)` | the `install_room` outcome (alias `RoomInstall` kept for back-compat) |
27-
| `AddRoomError` | user-facing error with message-factory classmethods |
28-
| `ADDED` / `UNCHANGED` / `COVERED` / `ROOMS_PARENT_ENTRY` | the `room_paths` action constants and the `./rooms` auto-discovery entry |
62+
| `room_parent_candidates(project)` | the `room_paths` container entries a caller can offer as a `parent_path` (or `["./rooms"]` when absent) |
63+
| `install_room(project, room_id, *, config_text, prompt_text=None, parent_path, force=False, dry_run=False)` | write the room dir from a rendered `config_text` (+ optional prompt) under `parent_path` and wire `room_paths` |
64+
| `install_room_from(project, room_id, src_dir, *, parent_path, force=False, dry_run=False)` | the same, but *copy* the `src_dir` template tree (multi-file); the caller patches the copied files afterward |
65+
| `RoomInstalled(config_path, path_action)` | the install outcome (alias `RoomInstall` kept for back-compat) |
66+
| `AddRoomError` | user-facing error with message-factory classmethods (incl. `room_exists`, `parent_is_room`) |
67+
| `ADDED` / `UNCHANGED` / `COVERED` | the `installation.TargetAction` members, re-exported |
68+
| `ROOMS_PARENT_ENTRY` | the `./rooms` default-discovery container |

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ build-backend = "hatchling.build"
1212

1313
[project]
1414
name = "soliplex-plumber"
15-
version = "0.1"
15+
version = "0.2dev0"
1616
description = "Read and modify the configuration of an existing Soliplex stack."
1717
readme = "README.md"
1818
license = "MIT"

0 commit comments

Comments
 (0)