# Allium → Pi.dev Port Plan ## Goal Port allium (https://github.com/juxt/allium) to pi.dev, keeping upstream files by reference where possible. Test with the turn-limit extension (https://gitea.apps.sustainabledelivery.com/QWAN/monotonic-pi-extensions). ## Directory layout ``` pi-allium/ ← this repo ├── allium-main/ ← git clone of juxt/allium (submodule or checkout) ├── turn-limit/ ← checkout of monotonic-pi-extensions/packages/pi-turn-limit ├── .pi/ │ └── skills/ │ ├── allium/ ← root skill (entry point, routing table) │ │ ├── SKILL.md ← adapted frontmatter + symlink or include of upstream content │ │ └── references/ ← symlinks to allium-main/references/* │ ├── elicit/ │ │ ├── SKILL.md │ │ └── references/ ← symlinks to allium-main/skills/elicit/references/* │ ├── distill/ │ │ ├── SKILL.md │ │ └── references/ ← symlinks to allium-main/skills/distill/references/* │ └── propagate/ │ ├── SKILL.md │ └── references/ ← (upstream has none currently) ├── allium-port-plan.md ← this file └── learning-goal.md ← goal card ``` ## Key differences: Claude Code plugin vs Pi skill | Aspect | Claude Code | Pi.dev | |--------|-------------|--------| | Skill location | `.claude-plugin/` + `skills/` | `.pi/skills/{name}/SKILL.md` | | Frontmatter | `name`, `description`, `version`, `auto_trigger` | `name`, `description`, `disable-model-invocation`, `license`, `metadata` | | References dir | `resources/` or `references/` | `references/` | | Agents | `.claude/agents/{name}.md` with `model:`, `tools:` | No native agent support — needs extension or workaround | | Hooks | `.claude/settings.json` → PostToolUse hooks | Not directly supported — needs extension | | Rules | `.claude/rules/*.md` with `globs:` | No equivalent — content goes into SKILL.md or references | | Auto-trigger | `auto_trigger: file_patterns, keywords` | `disable-model-invocation: true/false` | | Invocation | `/allium`, `/allium:elicit` | `/skill:allium`, `/skill:elicit` | ## Phases ### Phase 1: Setup and smoke test _Goal: get the root `/skill:allium` working in pi with a local model._ - [x] Clone allium upstream into `allium-main/` ``` cd ~/research/pi-allium git clone https://github.com/juxt/allium.git allium-main ``` - [x] Create `.pi/skills/allium/SKILL.md` with pi-compatible frontmatter - Source: `allium-main/skills/allium/SKILL.md` - Change: add `disable-model-invocation: true`, `license: MIT`, `metadata` block - Change: update routing table to use `/skill:elicit`, `/skill:distill`, `/skill:propagate` instead of Claude skill/agent references - Change: update reference paths from `../../references/` to `references/` - Keep: the full language reference body - [x] Symlink references: `ln -s ../../../allium-main/references .pi/skills/allium/references` - [x] Smoke test: `pi -p` in this directory, invoke `/skill:allium`, verify it loads and responds with the routing table - [x] Verify references load: ask allium about language syntax, confirm it can read `references/language-reference.md` ### Phase 2: Port elicit, distill, propagate skills _Goal: all three sub-skills work via `/skill:elicit` etc._ - [x] Create `.pi/skills/elicit/SKILL.md` — adapt frontmatter from `allium-main/skills/elicit/SKILL.md` - [x] Symlink references: individual symlinks in `.pi/skills/elicit/references/` (language-reference.md + library-spec-signals.md) - [x] Smoke test `/skill:elicit` — start a mini elicitation session, verify it follows the methodology - [x] Create `.pi/skills/distill/SKILL.md` — adapt frontmatter from `allium-main/skills/distill/SKILL.md` - [x] Symlink references: individual symlinks in `.pi/skills/distill/references/` (language-reference.md + worked-examples.md) - [x] Smoke test `/skill:distill` - [x] Create `.pi/skills/propagate/SKILL.md` — adapt frontmatter from `allium-main/skills/propagate/SKILL.md` - [x] Smoke test `/skill:propagate` ### Phase 3: Test with turn-limit extension (TDD) _Goal: use distill → propagate on real code, verify allium produces useful output._ - [x] Checkout turn-limit into this workspace ``` git clone https://gitea.apps.sustainabledelivery.com/QWAN/monotonic-pi-extensions.git turn-limit-repo ln -s turn-limit-repo/packages/pi-turn-limit turn-limit ``` - [x] Run `/skill:distill` against `turn-limit/` — extract a `.allium` spec from existing code - [x] Review the generated spec: does it capture turn-limit constraints, enable/disable, UI separation? - Fixed: section order (entities before config), `session.max_turns` → `config.max_turns`, missing closing brace - [x] Run `/skill:propagate` against the generated spec — generate test suggestions - Generated 30 test obligations: 10 unit (P1-P5, C1-C5), 20 integration (E, R, S, W, I, C6-C8) - Identified discrepancy: `checkTurnLimit` uses `>` but handler uses `===` - [x] Write at least one test based on propagate output (TDD red step) - Wrote 21 tests: P1-P5 (pure fn), C1-C8 (config+command), E1-E3 (entity state), R1-R5 (rule) - All pass — green from the start since code already exists - [x] Implement to make the test pass (green step) - Only change needed: exported `getMaxTurns` for testability - [x] Run `/skill:elicit` to explore the "disable turn limit" feature requirement - Elicited 3 design decisions: (1) unlimited = no boundary check, (2) hard reset on re-enable, (3) config value not entity state - [x] Generate spec + tests for the new feature via propagate - Updated spec with `max_turns: Integer | unlimited` and `LimitReEnabled` rule - Propagate generated 13 test obligations; wrote 8 covering config, command, rule, and integration - TDD cycle complete: red (8 failing) → implemented unlimited mode → green (29/29 pass) ### Phase 4: Fold in allium rules content _Goal: pi agent knows allium syntax rules when editing .allium files._ - [x] Symlink `allium-main/.claude/rules/allium.md` into `allium-main/references/allium-rules.md` (accessible via `.pi/skills/allium/references/`) - [x] Add "Syntax rules" section to elicit, distill, and propagate skills - Instructs model to read `../../allium/references/allium-rules.md` before writing .allium files - [ ] Test: create/edit a `.allium` file via pi, verify the model follows naming conventions and avoids anti-patterns ### Phase 5: Agent support (tend & weed) _Goal: find a way to support tend/weed agent functionality in pi._ Tend and weed are Claude Code agents with `model: opus` and specific tool permissions (Read, Glob, Grep, Edit, Write, Bash). Pi doesn't have native agent support. - [x] **Option A: Skills as agents** — ported tend.md and weed.md as skills with `disable-model-invocation: true` - Created `.pi/skills/tend/SKILL.md` and `.pi/skills/weed/SKILL.md` - Symlinked references (language-reference.md + allium-rules.md) - Added verification: run `allium check` after writing .allium files - Trade-off: no enforced model (runs on default, not opus), no tool restrictions, no auto-trigger - [ ] **Option B: Pi extension (TypeScript)** — deferred, would give more control but more work - [ ] **Option C: npm agents package** — not investigated - [ ] Test: use tend to modify a `.allium` spec, verify it follows allium conventions - [ ] Test: use weed to check spec-code alignment on the turn-limit extension ### Phase 6: Hook support (allium-check validation) _Goal: .allium files get validated on write._ The Claude Code version runs `allium-check.mjs` as a PostToolUse hook on Edit/Write. - [x] Added "Verification" section to elicit, distill, tend, and weed skills - Instructs model to run `allium check ` after writing .allium files - Uses the globally installed `allium` CLI (`/opt/homebrew/bin/allium`) - [ ] Investigate pi.dev hook/extension support for automatic post-write validation (deferred) - [ ] Test: write a `.allium` file with deliberate errors, verify validation catches them ### Phase 7: Documentation and upstream _Goal: publishable result._ - [x] Add README.md with attribution (MIT license from JUXT) - [x] Add LICENSE - [x] Document the symlink-based approach for staying in sync with upstream - [x] Document known limitations vs Claude Code version - [x] Write blog post draft (separate file) - [ ] Share with allium team and pi.dev community ## Key URLs - Allium upstream: https://github.com/juxt/allium - Allium docs: https://juxt.github.io/allium/ - Allium installation: https://juxt.github.io/allium/installation - Turn-limit extension: https://gitea.apps.sustainabledelivery.com/QWAN/monotonic-pi-extensions - Pi.dev: https://pi.dev - Local allium cache (current): `~/.claude/plugins/cache/juxt-plugins/allium/f6fb08dd301f/` ## Notes for the agent on the other machine - Pi is invoked with `pi -p` to load project skills from `.pi/skills/` - Default model: `qwen3.5:35b-a3b` via Ollama on `192.168.0.1:11434` - Skills are invoked with `/skill:name` (requires `enableSkillCommands: true` in pi settings if not already set) - Symlinks must resolve on the target machine — clone allium-main in the same relative position - The `.allium` language version is 3 (check `allium-main/VERSION`)