From yoe
Generates complete Starlark .star unit files for Yoe embedded builds from source URLs or descriptions. Consults Alpine, Yocto, and Buildroot packaging before writing from-source units.
How this skill is triggered — by the user, by Claude, or both
Slash command
/yoe:new-unitThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Generate a complete Starlark `.star` unit from an upstream source URL or a
Generate a complete Starlark .star unit from an upstream source URL or a
natural language description. The output is a ready-to-build unit that follows
existing conventions in the project's modules.
If the thing you need is a tool or library Alpine already ships — a cross
toolchain, a niche build tool, a runtime you don't want to maintain — do not
write a from-source unit. module-alpine exposes all of Alpine main and
community as lazily-materialized feeds (alpine.main, alpine.community).
Pull the package by name and route it with prefer_modules in PROJECT.star; no
per-package file is generated. See the pulling-alpine-packages skill for the
workflow. Write a from-source unit only when Alpine doesn't ship the package,
ships the wrong version, or the build is the product (kernel, bootloader,
busybox, base-files, project libraries you will patch via yoe dev).
If the user provides a URL (GitHub repo, tarball, etc.), use it directly. If the user provides a description ("I need an MQTT broker"), research appropriate upstream projects and suggest one, confirming with the user before proceeding.
Before writing the unit from scratch, check how other distributions package the same software. These are valuable references for configure flags, dependencies, patches, and known pitfalls:
https://gitlab.alpinelinux.org/alpine/aports for the package. Alpine is
closest to Yoe's packaging model (apk, musl/glibc, minimal). Pay attention to
makedepends, depends, and configure flags.https://layers.openembedded.org or the OE-Core module. Yocto units often
have well-tested configure flags and patch sets for embedded use.https://github.com/buildroot/buildroot/tree/master/package for the package.
Buildroot units are simple and often reveal minimal configure flags needed for
embedded targets.Extract useful information: required dependencies, recommended configure flags, known patches, and license details. Do not blindly copy — adapt to Yoe's conventions and verify the information is current.
Fetch and inspect the upstream source to determine:
Build system — look for these files in priority order:
configure.ac / Makefile.am → autotools class (autotools)CMakeLists.txt → cmake class (cmake)go.mod → go class (go_binary)package.json with a Bun lockfile / Bun runtime → bun class (bun_app)package.json with npm/Node → nodejs class (nodejs_app)pyproject.toml / requirements.txt) → python class
(python_venv, takes a pip_packages list)binary, fetches a release artifact by URL + sha256)Makefile only → custom build steps with unit()meson.build → custom build steps (no meson class yet)The bun_app, nodejs_app, python_venv, and binary classes are the "app
mode" classes — use them for application payloads (services, tools shipped as
an interpreted app or a prebuilt binary) rather than libraries compiled into
the system.
Version — latest stable release tag or version string
Dependencies — scan configure.ac, CMakeLists.txt, go.mod,
pkg-config requires, or #include directives to identify build and runtime
dependencies. Cross-reference against existing units in the project's modules
and the findings from Step 2.
License — check LICENSE, COPYING, or source headers. Use SPDX
identifiers (e.g., MIT, Apache-2.0, GPL-2.0-or-later).
Before creating a new unit, search all modules for an existing unit:
Glob: modules/**/units/**/<name>.star
If one exists, inform the user and suggest /update-unit instead.
Write a .star file following the conventions of existing units in the project.
Use the appropriate class:
Autotools example:
load("//classes/autotools.star", "autotools")
autotools(
name = "example",
version = "1.2.3",
source = "https://github.com/example/example.git",
tag = "v1.2.3",
license = "MIT",
description = "Short description of the package",
deps = ["zlib", "openssl"],
runtime_deps = ["zlib", "openssl"],
configure_args = ["--with-ssl"],
)
CMake example:
load("//classes/cmake.star", "cmake")
cmake(
name = "example",
version = "1.2.3",
source = "https://github.com/example/example.git",
tag = "v1.2.3",
license = "MIT",
description = "Short description of the package",
deps = ["zlib"],
runtime_deps = ["zlib"],
cmake_args = ["BUILD_SHARED_LIBS=ON"],
)
Go example:
load("//classes/go.star", "go_binary")
go_binary(
name = "example",
version = "1.2.3",
source = "https://github.com/example/example.git",
tag = "v1.2.3",
license = "Apache-2.0",
description = "Short description of the package",
)
Custom build (no class):
unit(
name = "example",
version = "1.2.3",
source = "https://github.com/example/example.git",
tag = "v1.2.3",
license = "MIT",
description = "Short description of the package",
deps = ["zlib"],
runtime_deps = ["zlib"],
build = [
"./configure --prefix=$PREFIX",
"make -j$NPROC",
"make DESTDIR=$DESTDIR install",
],
)
Place the unit in the appropriate category directory within the project's module or the module-core module:
| Category | Directory | Examples |
|---|---|---|
| Libraries | units/libs/ | zlib, openssl, ncurses |
| Networking | units/net/ | openssh, curl |
| Base system | units/base/ | busybox, linux |
| Debug tools | units/debug/ | strace, vim |
| Bootloaders | units/bootloaders/ | syslinux |
If no existing category fits, create a new one (e.g., units/multimedia/).
Present the complete unit to the user for review before writing the file. Show the file path and contents. Only write after confirmation.
After writing the unit, build it to verify:
yoe build --force <unit-name>
If the build fails, use the diagnose workflow to fix it iteratively.
source with a .git URL and
tag for version pinningv1.2.3, release-1.2.3, openssl-3.4.1)deps are build-time only (headers, static libs);
runtime_deps are needed at runtime. Most libraries are both.--prefix=$PREFIX (the class handles it).provides is a []string of virtual
package names this unit satisfies; it is reserved for leaf artifacts that
get swapped per machine or per project: kernel, base-files, init, bootloader.
Do not set provides on a build-time library, a generic tool (less, htop,
file, etc.), or a daemon that has a busybox alternative — those should ship
side-by-side and be selected at boot from init scripts. Misusing provides
forks every transitive consumer into a machine-specific apk variant. See
docs/naming-and-resolution.md §"When NOT to use provides".[]string listing packages whose files this unit may overwrite
at install time. Set this only when the unit ships a path that is also owned
by another package and you want apk to accept the shadow rather than fail.
Example: util-linux ships real dmesg/mount/umount etc. at paths
busybox also claims, so its unit declares replaces = ["busybox"]. Without
the annotation, apk add rejects the conflict at image-assembly time.deps and runtime_deps should reference only other libraries and
tools. Never add linux, base-files, or any unit that varies by machine to
a generic unit's deps — it will fork that unit's apk per machine.Never install missing dependencies in the Dockerfile. The container provides only the minimal bootstrap toolchain (gcc, binutils, make, etc.). Every library and build tool the unit needs must exist as a unit:
deps (and runtime_deps if
it's a shared library needed at runtime).$PREFIX, $DESTDIR,
$NPROC environment variables.module-core unless it's truly a core system component.
Project-specific units go in the project's own module.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub yoebuild/yoe --plugin yoe