Five skills covering this repo's own conventions: adding a bootstrap module, adding a CLI to bin/, bumping a pinned version, adding an MCP server, and verifying a module change. Lives in .agents/skills/ because opencode discovers that path natively alongside .opencode/skills/ and .claude/skills/, making it the most tool-neutral of the three. Distinct from the repo's skills/ tree, which ships to every covert-gpt user via sync.sh; these stay in the checkout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2.9 KiB
name, description
| name | description |
|---|---|
| add-cli-tool | Use when adding a userland CLI binary to bin/install.sh (jq, kubectl, helm, k9s, stern, yq, tofu, task, op, opencode, …). Covers the install_<name> function pattern, arch tokens, the .versions idempotency stamp, and when a tool does NOT belong in bin/. |
Adding a CLI tool to bin/
bin/install.sh installs self-contained release binaries into ~/.local/bin:
no sudo, nothing outside $HOME, so tools persist across Kasm redeploys.
Does it belong here?
bin/ is for single, statically linked binaries. If the tool is a tree
(interpreter + libraries), it does not fit install_*_bin and needs its own
module that unpacks into ~/.local/opt/<name> and symlinks the CLIs — that is
exactly why ansible/ and node/ exist. Prefer static builds: after a Kasm
redeploy the root filesystem's shared libraries are gone, and a dynamically
linked binary in $HOME will break.
The pattern
One install_<name> function, pinned version, guard, install, stamp:
# <name> — one line on what it is. Note any asset-naming quirk.
install_<name>() {
ver=v1.2.3
have_version <name> "$ver" && { fsay "<name> $ver already installed."; return 0; }
install_tarball_bin \
"https://github.com/<org>/<name>/releases/download/$ver/<name>_linux_$DEB.tar.gz" \
"<path/inside/archive>" <name>
mark_version <name> "$ver"
}
Then call it at the bottom of the file with the others.
Keep ver byte-identical to the upstream tag. Some tools tag v1.2.3,
some 1.2.3 (jq uses a bare 1.8.2). have_version does a string compare
against the stamp in ~/.local/bin/.versions, so a mismatch just means the
tool silently reinstalls on every bootstrap.
Arch tokens
bin/install.sh precomputes both spellings — pick the one the upstream asset
actually uses, don't call uname yourself:
$ARCH→x86_64/aarch64(rust-style release names)$DEB→amd64/arm64(Go/Debian-style — most devops tools)
Picking the helper
install_raw_bin <url> <dest>— the asset is the binary (jq, kubectl, yq).install_tarball_bin <url> <path-in-archive> <dest>—.tar.gz(helm, k9s).install_zip_bin <url> <path-in-archive> <dest>—.zip(the 1Password CLI).
Asset names are inconsistent and this is where it usually goes wrong: k9s
capitalizes Linux, helm nests under linux-$DEB/. Fetch the release page and
read the real asset name rather than guessing — a 404 from curl -fsSL will
abort bootstrap under set -eu.
Conditional installs
A tool that only works in some environments gets a gate, like kind — installed
only when a rootful Docker daemon answers, because it does not work against
rootless Docker on Kasm. Reuse docker_full_available() rather than writing a
new probe.
Finally
Add the tool to the bin/ bullet in README.md. Then run it twice and confirm
the second run prints "already installed."