Maintainer guide
Adding, editing, and rendering TVBO skills
This page is for people contributing to the TVBO codebase: how the skill system is laid out internally, how to add a new skill, and how to keep the adapter files in sync.
If you’re a user of TVBO, you want Installing & tuning instead.
Repository layout
tvbo/ # repo root
├── AGENTS.md # cross-tool entry point (mostly hand-written)
├── skills/ # canonical maintainer skills (NOT in wheel)
│ ├── git/SKILL.md
│ ├── writing-code/SKILL.md
│ ├── grill-me/SKILL.md
│ ├── linkml-schema/SKILL.md
│ ├── codegen-templates/SKILL.md
│ └── tests-and-backends/SKILL.md
├── .claude/skills/ # rendered Claude Code skills (committed)
├── .github/instructions/ # rendered Copilot instructions (committed)
└── tvbo/
├── cli/skills.py # CLI subcommands (sync / install / uninstall)
└── skills/
├── __init__.py # public API of the skill module
├── _render.py # canonical parser + adapter renderers
└── canonical/ # canonical USER skills (ships in wheel)
├── overview/SKILL.md
├── writing-models/SKILL.md
├── running-simulations/SKILL.md
└── platform/SKILL.md
The pipeline
canonical SKILL.md
│
│ parse_skill() → Skill dataclass
│
├──► render_claude_code() ─► .claude/skills/<name>/SKILL.md
├──► render_copilot() ─► .github/instructions/<name>.instructions.md
├──► render_cursor() ─► .cursor/rules/<name>.mdc
├──► render_agents_md() ─► AGENTS.md skills-index region
└──► render_prompt() ─► stdout (raw API)
All renderers live in tvbo/skills/_render.py. They’re pure functions of the Skill dataclass — easy to test and easy to extend.
Adding a new skill
1. Pick the audience
| Audience | Canonical location | Shipped in wheel? |
|---|---|---|
maintainer |
skills/<name>/SKILL.md |
No — repo-only |
user |
tvbo/skills/canonical/<name>/SKILL.md |
Yes — pip install tvbo ships it |
both |
tvbo/skills/canonical/<name>/SKILL.md |
Yes |
Rule of thumb: if a user who only ever calls pip install tvbo (no clone) could benefit, it’s a user skill. If it’s about the TVBO source tree itself, it’s maintainer.
2. Write the canonical SKILL.md
---
name: my-new-skill
description: One sentence — what it teaches and when it should fire.
metadata:
audience: user # or maintainer / both
applies_to:
- "**/*.py"
tags: [my-tag]
requires_extras: []
---
# My new skill
The body in plain markdown. This is exactly what gets included in every
adapter format. Keep it tight — Claude Code loads this whole body once
the skill fires.Guidelines:
- Body should be self-contained. The assistant won’t have access to the rest of the repo unless you point it there.
- Capture non-obvious rules. Things an LLM would predictably get wrong without this skill. Generic style advice is in
writing-codealready. - Cite file paths and line ranges. They survive better than vague references like “the codegen module”.
3. Sync
tvbo skills syncThis:
- Parses every canonical
SKILL.mdfrom both roots. - Writes
.claude/skills/<name>/SKILL.mdfor every skill. - Writes
.github/instructions/<name>.instructions.mdfor every maintainer-tagged skill. - Regenerates the skills-index region of
AGENTS.md.
Output:
synced 11 skills → 19 files
4. Verify
tvbo skills sync --check # CI guard — fails if any adapter file would changeIf you see drift output, something downstream of canonical was hand-edited. Re-sync to fix.
Editing an existing skill
Always edit the canonical source under skills/<name>/SKILL.md or tvbo/skills/canonical/<name>/SKILL.md. Then tvbo skills sync.
Do not edit the adapter outputs:
.claude/skills/<name>/SKILL.md.github/instructions/<name>.instructions.md
Sync will overwrite them. Edits there are silently lost.
Releasing the wheel
tvbo/skills/canonical/** is bundled by the standard tool.hatch.build.targets.wheel config — no extra force-include needed. Sanity check before a release:
hatch build
unzip -l dist/tvbo-*.whl | grep skills/canonicalYou should see all four user SKILL.md files inside the wheel.
Versioning the marker
User-installed skills carry tvbo-version: X.Y.Z in their frontmatter (stamped at install time, not at build). This is purely informational today — uninstall doesn’t filter by version; it removes any file with managed-by: tvbo.
When we eventually need a migration path (e.g. a renamed skill), we can add version comparison to install. For now the marker just helps debug “where did this skill on my machine come from?”.
Adding a new render target
Want to support Aider, Continue.dev, OpenCode, Codex CLI, or some new tool?
- Add a
render_<target>(skill, dest_dir, ...)function intvbo/skills/_render.py. Use the existing adapters as templates. - Add a value to the
Targetenum intvbo/cli/skills.py. - Wire it into
_render_target()and_target_path()switch cases. - Update the Installing & tuning doc.
Each adapter is ~10 lines of code. The hardest part is reading the target tool’s docs to figure out its exact frontmatter convention.
Why this architecture?
Earlier the repo had three places to edit every rule: skills/git.md, .claude/skills/git/SKILL.md, and .github/instructions/git.instructions.md. Same content, three formats, three sources of drift. The canonical-source + renderer design replaces that with one source, N renderings:
- Editing is one file.
- Sync runs in milliseconds.
- New tools require a new renderer, not a new copy of every skill.
- Build-time rendering for maintainer-side (so a fresh clone has working Claude/Cursor/Copilot configs without running anything), install-time rendering for user-side (so the wheel doesn’t have to ship pre-rendered per-target trees that bloat with every new adapter).
See also: Skill reference · AGENTS.md