Files
reliquary/docs/workpad/designs/2026-05-11-17-gitea-skill-port.md
T
g4borg 1b5a7638d7 🧠 Add brain-dump docs for skill adaptation
Add exploratory and design docs for porting dirigent skills to
reliquary, including:
- Explore port options for the gitea skill (in-plugin vs vendored)
- Document activation mechanics across Claude Code and cross-tools
- Include an orchestration comparison between dirigent and reliquary
  approaches
- Add research on glob negation and flag-file activation patterns
- Create uv-detection goals and skill-engines cross-tool reports
- Provide a research plan for future porting work and risk notes
2026-05-11 23:01:30 +02:00

145 lines
8.1 KiB
Markdown

# Porting the `gitea` skill + scripts into reliquary
Hands-on design for moving dirigent's `gitea` skill (which is a thin wrapper around `scripts/gitea/*.py` in the dirigent repo) into a reliquary plugin where the scripts and the skill ship as one unit.
## Current state in dirigent
```
dirigent/
├── .claude/skills/gitea/SKILL.md # 123 LOC — thin wrapper, points at scripts
└── scripts/gitea/
├── CLAUDE.md # agent reference for scripts (~127 LOC)
├── README.md # human setup guide (~178 LOC)
├── __init__.py
├── client.py # 186 LOC — GiteaClient REST wrapper
├── close_ticket.py # 39 LOC
├── config.py # 68 LOC — env loading
├── create_ticket.py # 115 LOC
├── fetch_ticket.py # 113 LOC
├── list_tickets.py # 56 LOC
├── models.py # 66 LOC — Pydantic models
├── post_comment.py # 69 LOC
├── setup_labels.py # 43 LOC
└── start_ticket.py # 163 LOC
```
Total: ~919 LOC of Python + ~430 LOC of docs.
The skill itself does almost nothing — it tells the agent to *read* `scripts/gitea/CLAUDE.md` and *run* `uv run python -m scripts.gitea.<module>`. Everything load-bearing lives in the dirigent project root: the venv, `uv.lock`, `pyproject.toml`, and `.env`.
## The fundamental tension
The dirigent skill assumes:
1. **The Python scripts live in the project being worked on**, importable as `scripts.gitea.*` from project root.
2. **The project has uv set up and a `.env` with `GITEA_*` vars.**
3. **The user is in dirigent itself** — the skill says `dirigent workpad`, references `docs/workpad/tickets/`, and the labels list (🐛 bug, ✨ feature, 📋 workpad…) is curated for dirigent.
To turn this into a **reusable plugin skill**, none of those assumptions hold. The scripts must live with the plugin, work from anywhere, and adapt to any project's workpad layout.
## Two viable port shapes
### Option A — Scripts ship inside the plugin (recommended)
```
plugins/gitea/
├── .claude-plugin/plugin.json
├── README.md
└── skills/gitea/
├── SKILL.md
└── scripts/
├── __init__.py
├── client.py
├── config.py
├── models.py
├── fetch_ticket.py
├── create_ticket.py
├── post_comment.py
├── start_ticket.py
├── list_tickets.py
├── close_ticket.py
└── setup_labels.py
```
Invocation pattern changes:
- Skill expands `${CLAUDE_PLUGIN_ROOT}` to locate scripts.
- Each call becomes `uv run --no-project --with httpx --with pydantic --with python-dotenv python ${CLAUDE_PLUGIN_ROOT}/skills/gitea/scripts/fetch_ticket.py 42` — or, better, an inline-deps PEP 723 header at the top of each script so they're self-installing.
- Env vars come from `.env` in the *current working directory* (the project the user is in), not the plugin dir. `python-dotenv` already supports this.
- Workpad path comes from the `workpad` skill's resolution rules (env var → just recipe → `/docs/workpad`), not hardcoded.
**Pros:**
- Plugin is fully self-contained — install it once, use it in any repo.
- Versioned together with the skill markdown.
- No requirement that the host project has uv configured.
**Cons:**
- `uv run --no-project --with httpx ...` is slow on first run (env build). Mitigation: PEP 723 headers + `uv` cache make second-run startup fast.
- The scripts must be reworked to take a workpad root as an arg (or env var) rather than hardcoding `docs/workpad/`.
### Option B — Scripts vendored into each project (rejected)
Keep the dirigent shape: ship a "scaffolder" that copies scripts into the user's repo. This is what `superpowers`-style skills sometimes do.
**Why reject:** updates don't propagate, every project carries duplicate code, and the scripts become "the user's code now" which makes version skew permanent.
## Concrete port checklist
If we go with Option A:
### Phase 1 — copy + decouple from dirigent
1. Copy `scripts/gitea/` contents → `plugins/gitea/skills/gitea/scripts/`.
2. Drop `__init__.py` if scripts are run as files; keep it if we want `python -m`.
3. Add PEP 723 inline-dep blocks to each CLI entrypoint (`fetch_ticket.py`, `create_ticket.py`, etc.):
```python
# /// script
# requires-python = ">=3.11"
# dependencies = ["httpx", "pydantic>=2", "python-dotenv"]
# ///
```
This makes each script runnable in isolation via `uv run script.py` without a project context.
4. Rewrite imports: `from scripts.gitea.client import ...` → `from client import ...` (relative within the scripts/ dir). Or run as `python -m` with `PYTHONPATH` set.
### Phase 2 — parameterize project-specific behavior
5. Replace hardcoded `docs/workpad/tickets/` with a CLI flag `--workpad <path>` defaulting to env var `WORKPAD_FOLDER`, defaulting to `./docs/workpad`. Honor the `workpad` skill's resolution order.
6. Move the hardcoded label-emoji list out of `setup_labels.py` into a JSON/YAML file shipped with the plugin (`labels.default.json`) so users can override per-project.
7. Audit any other dirigent-isms: references to "dirigent" in error messages, the README mentioning dirigent's workpad, etc.
### Phase 3 — rewrite the skill markdown
8. `Step 0: Read Current Script Capabilities` currently points at `scripts/gitea/CLAUDE.md`. Replace with **inline** capability summary in the SKILL itself — the skill *is* the reference now; the scripts are an implementation detail.
9. Replace every `uv run python -m scripts.gitea.X` example with `uv run ${CLAUDE_PLUGIN_ROOT}/skills/gitea/scripts/X.py`.
10. Connectivity check stays — just relocate the inline `python -c` snippet's imports to whatever the new module layout is.
11. Error-recovery table stays as-is. It's good.
### Phase 4 — documentation
12. README at plugin root: install instructions + Gitea token setup. Lift most of `scripts/gitea/README.md`, drop the dirigent-specific bits.
13. The skill markdown should be self-contained — don't make the agent open a second file unless absolutely necessary.
## Things to NOT do in the port
- **Don't keep both `CLAUDE.md` and `SKILL.md` for the scripts.** That split made sense in dirigent because the scripts had a life of their own outside the skill. Inside a plugin they don't. One file: `SKILL.md`.
- **Don't ship Pydantic v2 + httpx as hard plugin dependencies installed in the user's project venv.** Use PEP 723 inline deps so uv builds an isolated env per invocation.
- **Don't hardcode `closes #N` / `fixes #N` keywords** — those are Gitea behavior, not script behavior. They should stay in the skill markdown as guidance, not in code.
- **Don't copy `setup_labels.py`'s default label set verbatim.** It's tuned for one user's taste. Externalize.
## Open question — sibling skill in a "mini plugin"
The user mentioned wanting **another skill in a custom mini plugin** that builds on this. That's not designed yet — likely candidates given the workpad ecosystem:
- A `gitea-workpad-flow` skill that ties together: "fetch a ticket → write plan in workpad → post plan back as comment → branch via `start_ticket`". This is workflow glue, not API access.
- A `gitea-bulk` skill for grooming: relabel many issues, close stale ones, etc.
- A `gitea-pr` skill if/when Gitea's PR API gets used (the current scripts only touch issues).
These should be a separate plugin so the core `gitea` plugin stays purely a thin API wrapper. The workflow-glue skill *depends on* the API skill but is opinionated about flow — exactly the kind of thing that should be swappable.
## Estimated work
| Phase | Effort | Risk |
|---|---|---|
| Copy + PEP 723 conversion | ~1h | Low — mechanical |
| Parameterize workpad path + labels | ~1-2h | Medium — touches 4-5 files |
| Skill markdown rewrite | ~30min | Low |
| README + plugin manifest | ~20min | Low |
| Manual end-to-end test (real Gitea + new project) | ~1h | This is where the bugs are |
Total: **half a day of focused work** for a complete port. The `start_ticket` flow is the most likely to surface issues because it touches git state, the workpad, and the API in one call.