How We Use Claude Code at Serokell

The Problem Before the Tool

At Serokell, we have an internal wiki hosted in Notion, where we keep our accumulated technical knowledge, guidelines, and conventions. These guides cover an entire project lifecycle: from bootstrapping the repository, choosing a license, writing tests, applying Haskell styles, among others, to committing on Git, opening pull requests, and performing reviews.

Over the years, our engineers were instructed to always keep these in mind and apply them while programming and performing reviews. With experience, they eventually come naturally, though slips can still happen even to experienced engineers. New hires, in particular, often found them puzzling.

Now, with AI agents doing ever more work, we found that not only would they have to know about our internal guidelines to perform their work, but also to effectively collaborate with the engineers. To fill the gap, we ported many of our guidelines as Claude skills, producing a plugin that is now being adopted by the Serokell engineers who use Claude Code.

In this post, we want to talk about the process to produce these skills, and perhaps inspire people outside Serokell to check the plugin out, who may want to adapt them to their own guidelines.

What Claude Code Skills Actually Are

Claude supports a feature called skills, an open format now used across several AI tools; Claude Code’s own take on it is documented here. A skill is a directory with a SKILL.md file describing what to do in plain English, plus some optional supporting files. These support files could be anything: Bash scripts complementing its instructions, other markdown files with gotchas or solutions to problems it might run into, etc.

Each skill may refer to other skills as a dependency. For example, one of the skills we created is bootstrap-repo, acting as the entrypoint for repository creation. This skill currently calls 9 out of the other 14 skills, such as the skills on how to choose a license, configure CI, or create a .gitignore, among others.

Example `bootstrap-repo` skill: --- # SPDX-FileCopyrightText: 2026 Serokell # SPDX-License-Identifier: CC0-1.0 name: bootstrap-repo description: Use when the user asks to create a new Serokell repository, bootstrap a fresh repo from `metatemplates`, fork an external repo into the Serokell org, initialize a new project, or sequence the full new-repo setup (create → customise → license → CI → settings). Triggers on phrases like "create a new Serokell repo", "bootstrap this repo", "set up a fresh repo", "fork this repo into serokell", "initialize new project", "new repo from metatemplates", "start a new project", "first commit on a new repo". requires: - pull-requests - license-choice - reuse-headers - readme - gitignore - haskell-style - setup-ci - repository-settings - changelog ---

Bootstrap a new repository

Skill bundle: this skill delegates to pull-requests, license-choice, reuse-headers, readme, gitignore, haskell-style, setup-ci, repository-settings, and changelog. All of them must be available. Install the serokell-global plugin from serokell/claude-plugins — it ships all sibling skills at plugins/serokell-global/skills/.

Fork or new repo?

  • Temporary fork — fork into the serokell GitHub org so colleagues inherit access. Add a one-paragraph “why this fork exists” both to the README on master and to the GitHub “About” section — an agent should do both for maximum visibility. Stop here. No further steps apply.
  • A normal new repo — follow the sequence below.

The sequence (non-fork repos)

  1. Create the new repo from metatemplates. On GitHub: gh repo create <owner>/<name> --template serokell/metatemplates (use --private or --public for the visibility you need now — a repo that’s private today but may open later still starts --private). GitLab has no template feature — clone metatemplates and copy its contents into a fresh project.
  2. Open a bootstrap PR. Don’t push customisation directly to master — even initialisation goes through review. → pull-requests skill.
  3. Customise inherited files (any order — these are independent):
    • Fill PROJECT.md with this repo’s issue tracker, YouTrack project key, and team lead — several skills read it.
    • License + LICENSES/ + root LICENSE → license-choice skill.
    • SPDX headers on every file → reuse-headers skill.
    • README rewritten to Standard Readme → readme skill.
    • .gitignore for the project’s stack → gitignore skill.
    • .editorconfig: keep and adapt it (or remove it if the team doesn’t use editor integration).
    • Haskell configs in haskell/ (or delete the directory) → haskell-style skill.
    • PR/MR template: always keep it. Keep the directory for your host (.github/ or .gitlab/) and delete the other.
    • CONTRIBUTING.md: keep and adapt it if it carries repo-specific contribution info; otherwise remove it.
    • Agent instruction files: review .github/copilot-instructions.md and plugins/serokell-global/skills/ (or the equivalent skill directory for your plugin); customise or delete what’s irrelevant.
    • Strip meta-comments and placeholders (see Gotchas).
  4. Bootstrap CI → setup-ci skill. Must complete before step 5.
  5. Apply repo settings → repository-settings skill. Branch protection’s status-check requirement needs the CI from step 4.
  6. Optionally set up a changelog → changelog skill. Worth doing now for libraries or anything user-visible.
  7. Merge the bootstrap PR → pull-requests skill.

Bootstrap-specific gotchas

  • The template README is not Standard-Readme-compliant. Don’t mirror its shape; rewrite from scratch to spec.
  • The template .gitignore is intentionally minimal. Replace it.
  • The root LICENSE must exist regardless of where your real license metadata lives — GitHub and GitLab key off it.
  • Placeholders are scattered across files: mypackage, KEK, Patak, LicenseRef-ReplaceMe. Search-and-replace them all before merging.
  • Strip every [//]: # (...) meta-comment from inherited files — they exist to guide the human creator, not to ship. Verify removal with:
    git grep '\[\/\/\]:' -- ':!.claude/' ':!.github/pull_request_template.md'
    
    (The exclusions avoid false positives from skill files and the PR template, which contain these patterns intentionally.)

The skill starts out with REUSE headers and metadata: name, description, and other required skills. Then the next session instructs the agent to delegate skills and install the Claude plugin.

In the next sections, the meat of the skill comes into play, where it goes through the entire sequence of steps to bootstrap the repo and produce a pull request. Some gotchas are documented to steer the agent into the correct direction around some common mistakes.

The end result should be the configured repository plus a pull request adding the infrastructure files in accordance to our guidelines, ready to be reviewed.

From metatemplates to a Real Plugin

Our serokell/metatemplates template repository contains templates for various kinds of projects we want to create. A plugin, in Claude Code terms, is a bundle of skills that installs once, from a marketplace repository, and stays in sync everywhere it’s used, no copying files by hand into every new repo. Our skills weren’t always a plugin, though: they started out living directly in metatemplates under .claude/skills/, meant to be copied into every new repository created from the template.

That’s exactly the kind of thing that drifts, and we didn’t have to wait long to see it happen. branch-lint, a public repository created to test these skills, was forked from metatemplates early on and got a full local copy of the skills baked in at creation. Shortly after, a more urgent task pulled me away, and I only came back to it a month later. Around that time, metatemplates replaced the local copies with the actual serokell-global plugin from serokell/claude-plugins, and branch-lint’s copy had already started drifting from the source of truth. We had to migrate it separately, by hand, the next day. env-check, created a little later, never had the problem: it was bootstrapped straight from the plugin, with no static copy to fall behind in the first place.

The 15 Skills

We ended up with 15 skills, covering a repository’s whole lifecycle. Rather than walking through all of them, here’s the shape in four groups.

Bootstrap, Settings, and CI

  • bootstrap-repo is the sequencer: one PR that takes a new repo from metatemplates through licensing, infrastructure files, CI, and repo settings, then merges.
  • repository-settings uses the GitHub API to require signed commits and PR review, disable rebase merging, and set the standard access roles (admin for Operations, write for developers, read for everyone else).
  • setup-ci wires up GitHub Actions or GitLab CI, preferring the Nix-based templates, and feeds the resulting status checks into branch protection. Every pipeline runs reuse lint, xrefcheck, and shellcheck, plus Haddock and hlint for Haskell projects.
  • nix-binary-cache checks whether the cache is already configured and, if not, walks through requesting read-only credentials from Operations and adding the substituter, so Nix fetches pre-built derivations instead of rebuilding GHC from scratch.

Infrastructure Files

  • readme writes a Standard-Readme-compliant README: the required sections in spec order, a promo blurb when the repo is public, and a separate README per component in a multi-package repo.
  • reuse-headers adds the two required SPDX lines to every new source file (or registers it in REUSE.toml when a file can’t carry a header) and keeps reuse lint passing.
  • gitignore scopes the .gitignore to what the project’s build tools actually produce, leaving editor and OS files for the user’s own global ignore instead.
  • license-choice defaults new Serokell-owned projects to MPL-2.0, switching to GPL or AGPL for specific commercial situations, or to a LicenseRef-Proprietary license for closed-source work.

Quality

  • haskell-style runs stylish-haskell and hlint with the repo’s shared configs, enables -Weverything with a curated disable list, and requires a Haddock comment on every exported name.
  • code-testing requires a regression test for every bug fix, an end-to-end test that actually executes the binary for any CLI tool, and a test for each edge case even when it passes trivially.
  • changelog adds one bullet per user-visible change, tagged with its PR or issue number, under Keep a Changelog categories like Added, Changed, and Fixed.

Shipping and Tracking

  • committing-work has the agent sign every commit using GPG keys and use the “Problem:/Solution:” format for commit messages.
  • pull-requests requires every PR template checkbox to be ticked, even the irrelevant ones (to show they were actually considered), and only allows merge commits, never squash or rebase.
  • code-review picks a verdict (approve, request changes, or comment), and when the PR under review is the agent’s own, hands the review to a separate agent instance with no memory of why the changes were made, so it can’t rubber-stamp itself.
  • youtrack-issues writes the required Bug/Task structure (title, repro steps, and acceptance criteria) and moves the issue through its states (Open → In Progress → Review → Done) as the work happens, keeping the assignee field current along the way.

A Concrete Taste

To test our skills, I’ve bootstrapped two simple demo repos: branch-lint and env-check.

I’m not the kind of person who enjoys having AI produce code (or vibecode) and art. Haskell lives in a warm spot in my heart, and I like to get my hands dirty writing code. Writing functional code is legitimately fun for me, and having the AI do it is like watching the other kids having fun in the playground while you’re grounded in your room. Having said that, these were tools with just 149 LOC and 65 LOC, respectively. They were created just to test and demonstrate these skills and, given what they were for, I had no choice other than having the agent do it this time.

branch-lint

The first bootstrap created a Haskell CLI application meant to validate a branch name against Serokell guidelines. It was created as a public repo using GitHub issues, and it has served its purpose as a test project and was archived shortly after.

When I started this repository, our Claude skills were not yet a plugin, but rather a collection of 14 skills files in metatemplates that got cloned into this repository. The 15th skill, nix-binary-cache, wasn’t part of this original copy. It only arrived after the repo moved to the plugin.

I asked it to load the skills and bootstrap the repository. Claude filled PROJECT.md, wrote the README, created a .gitignore tailored for Haskell and Nix, picked MPL-2.0 for the license, migrated to REUSE.toml, removed GitLab-specific files, and added the Haskell tooling configuration files. The agent also produced the code and wrote tests for it.

All of this landed as one pull request (#3), with the bootstrap changes themselves in a single commit (66fca65). The agent then spawned a subagent that performed a review with a pair of fresh eyes, which caught some defects. I also reviewed it myself and found some things, which Claude fixed.

Claude also configured the repository settings: branch protection, access, topics, and description. I didn’t just trust the agent’s claim on this and asked it to perform a GET request to GitHub, where Claude and I could see that the settings were applied. I also confirmed on GitHub itself. All the agent did matched what the skill said it applied, and no manual clicking through GitHub’s settings UI was required.

env-check

The second bootstrap created another basic Haskell CLI application that reads a TOML file that lists which environment variables must be present. If any variable was not set, it would quit with non-zero error code and list each violation. An example TOML might look like the following:

[vars]
DATABASE_URL = true
PORT = false

This project was initially private, using YouTrack for issues rather than GitHub issues, but made public before its archival. Like branch-lint, this repository was archived shortly after its conception.

The biggest difference was that the issue tracker was YouTrack. I created a YouTrack project and had Claude fill an issue to bootstrap the repo according to the project description.

By the time I bootstrapped this repository, our Claude skills were already consolidated into a plugin. The overall process was similar to branch-lint’s, except there was no local copy to fall out of sync, so no separate migration commit was needed later. Bootstrapping just meant installing the plugin and running the skill.

Where This Is Going

About a week after the creation of our Claude plugins repository, I presented a small seminar to Serokell’s engineers, where I briefly described these skills, some interactions between Claude and me, as well as some shortcomings I’ve faced. At the end, I motivated the team to install the plugin with auto-updates enabled, test it with real projects, and create PRs for any problems they encounter.

In some sense, the plugin is still experimental. Even though it was demonstrated to work on smaller projects, I still hope it will be useful in bigger contexts and can evolve from here. We are working to make it happen. With this, my hope is to also motivate external contributors to give our plugin a try, and perhaps inspire them to fork it to match their workflows, contributing back to what could benefit us collectively.

There are some skills that involve internal workflows, namely youtrack-issues, repository-settings, and nix-binary-cache, that should be adapted accordingly. youtrack-issues is tied to our internal issues.serokell.io, and repository-settings names Serokell’s specific team names. nix-binary-cache describes the flow around Serokell’s own Nix cache and its AWS credentials, which are not made public.

Even though the code files in this repository are licensed under the MPL-2.0, the skills themselves are licensed under CC0-1.0.

Our work on these skills does not only affect Claude. We also created a small corresponding instructions file for GitHub Copilot, which, although not as in-depth as the skills for Claude, should still prove useful.

These skills are still code, although written in plain English, and are interpreted by the agent. They are still prone to bugs, drift, edge cases, and code duplication. That’s a topic for another post.

For now, though, the plugin is public. If any of this looks useful, serokell/claude-plugins is right there to clone, read, or steal ideas from.

Banner that links to Serokell Shop. You can buy stylish FP T-shirts there!
More from Serokell
AI implementation in Medical FieldAI implementation in Medical Field
What is AI alignment thumbnailWhat is AI alignment thumbnail
The Real Limits of AI Agents in 2025The Real Limits of AI Agents in 2025