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-code already.
  • Cite file paths and line ranges. They survive better than vague references like “the codegen module”.

3. Sync

tvbo skills sync

This:

  • Parses every canonical SKILL.md from both roots.
  • Writes .claude/skills/<name>/SKILL.md for every skill.
  • Writes .github/instructions/<name>.instructions.md for 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 change

If 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/canonical

You 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?

  1. Add a render_<target>(skill, dest_dir, ...) function in tvbo/skills/_render.py. Use the existing adapters as templates.
  2. Add a value to the Target enum in tvbo/cli/skills.py.
  3. Wire it into _render_target() and _target_path() switch cases.
  4. 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