All skills
simota avatar

/anvil

@16a3f28
by shingo imotasimota/agent-skills85 stars
15

Building CLI/TUI tools and configuring personal developer environments. Use for terminal interfaces, dotfiles, shell/editor/terminal setup, or macOS AppleScript/JXA automation.

Use this Skill: https://skilld.dev/gh/simota/agent-skills/anvil

This session only. Nothing lands on disk.

referencepkg-distribution.md

≈2.3k tokens on demand. Your agent reads this file only when SKILL.md points to it.

CLI Packaging and Distribution Reference

Purpose: Ship a CLI through the package managers users already trust (Homebrew, apt, dnf, npm, PyPI, cargo, go install), with cross-compiled binaries, signed artifacts, and a functioning update-check path. Get the first install right — fixing a broken release lives in users' shell history forever. Read when: Adding a pkg subcommand, authoring a Homebrew formula, generating deb/rpm, cross-compiling releases, signing binaries, or designing an update-check mechanism.

Scope Boundary

  • Anvil pkg: packaging manifests, cross-compile matrix, signing/attestation, install scripts, update-checker design, distribution-channel selection.
  • Builder (elsewhere): the business logic inside the CLI (packaging does not change behaviour).
  • Anvil env: user-side install into their own dotfiles environment (brew install in a bootstrap workflow).
  • Gear (elsewhere): CI pipeline that runs goreleaser/cross/napi-rs, uploads artifacts, dependency scans of the packaging toolchain itself.
  • Launch (elsewhere): release versioning strategy (SemVer/CalVer), changelog authoring, rollout plan, feature-flag gating.

If the request is "build deb/rpm artifacts and publish a Homebrew formula" → pkg. If it is "pick next version number and write release notes" → Launch. If it is "wire goreleaser into GitHub Actions" → Gear (with pkg owning the manifest content).

Channel Selection Matrix

Channel Ecosystem Pick when Skip when
Homebrew (tap or core) macOS + Linux developers Dev-tool CLI, auto-update via brew upgrade expected Windows-primary tool
deb / rpm Server Linux Distro-native install for ops/SRE consumers Rapid-release; packaging lag is a tax
npm Node ecosystem / web devs CLI ships as JS or bundles a Node binary (pkg, sea) Large binary (npm has soft size expectations)
PyPI Python ecosystem CLI is Python; users already have pip or pipx Non-Python users ("why do I need Python?")
cargo install Rust devs Rust CLI, acceptable build-from-source time Non-Rust users (compile time and toolchain friction)
go install Go devs Go CLI, users accept $GOPATH/bin in PATH Versioned releases required (go install re-resolves)
Scoop / winget Windows Windows-primary tool Unix-only dependencies
Static binary on releases All Universal fallback; curl | sh install script Users need auto-update
OCI image Container-first workflows Tool runs inside containers (linters, scanners) Interactive TUI — TTY forwarding breaks

Ship at least two channels. Power users on macOS/Linux expect Homebrew + static tarball; server users expect deb/rpm.

Cross-Compile Toolchain

Source language Recommended tool Targets it handles cleanly
Go goreleaser linux/{amd64,arm64}, darwin/{amd64,arm64}, windows/amd64, freebsd
Rust cross + cargo-dist or cargo-zigbuild aarch64-unknown-linux-gnu, x86_64-apple-darwin, musl targets
Node (native addons) napi-rs Prebuilt per-platform .node binaries published to GitHub releases
Python cibuildwheel manylinux + macOS universal2 + Windows wheels

Pin the cross toolchain version in CI. "Latest" silently breaks when the base image rotates (GLIBC version bumps are the classic symptom — user installs on RHEL 8 fail).

Target matrix at minimum: linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64. Produce both glibc and musl Linux builds for static-binary distribution.

Homebrew Formula Skeleton

class Myapp < Formula
  desc "One-line description under 80 chars"
  homepage "https://github.com/org/myapp"
  version "1.2.0"
  license "MIT"

  on_macos do
    on_arm do
      url "https://github.com/org/myapp/releases/download/v1.2.0/myapp-darwin-arm64.tar.gz"
      sha256 "..."
    end
    on_intel do
      url "https://github.com/org/myapp/releases/download/v1.2.0/myapp-darwin-amd64.tar.gz"
      sha256 "..."
    end
  end

  def install
    bin.install "myapp"
    generate_completions_from_executable(bin/"myapp", "completion")
    man1.install "myapp.1"
  end

  test do
    assert_match version.to_s, shell_output("#{bin}/myapp --version")
  end
end

generate_completions_from_executable (available in Homebrew) wires bash/zsh/fish completions automatically if the CLI implements the completion subcommand. Ship the man page or Homebrew's audit complains.

deb / rpm via nfpm

nfpm emits both formats from one YAML, avoiding per-distro boilerplate:

name: myapp
version: 1.2.0
maintainer: team@example.com
description: |
  CLI description.
license: MIT
section: utils
contents:
  - src: ./dist/myapp
    dst: /usr/bin/myapp
  - src: ./completion.bash
    dst: /usr/share/bash-completion/completions/myapp
  - src: ./completion.zsh
    dst: /usr/share/zsh/site-functions/_myapp
  - src: ./myapp.1
    dst: /usr/share/man/man1/myapp.1
deb:
  fields:
    Bugs: https://github.com/org/myapp/issues

Serve the repo from a signed apt/yum endpoint (cloudsmith, packagecloud, or self-hosted with GPG-signed metadata). curl | sudo apt-key add is deprecated — use /etc/apt/keyrings/myapp.gpg + signed-by= in the sources list.

Signing and Attestation

Platform Required Tooling
macOS binaries Notarization for Gatekeeper codesign + notarytool; without it users hit "cannot be opened" dialog
Windows binaries Authenticode signing EV code-signing cert or Azure Trusted Signing
Linux packages GPG-signed repo metadata gpg --detach-sign on deb Release files, rpm --addsign
Container images Cosign keyless (OIDC) cosign sign --yes <registry>/<image>:<tag>
Artefact attestation SLSA provenance actions/attest-build-provenance in GitHub Actions

Publish checksums (SHA256SUMS) and their signature (SHA256SUMS.sig or .intoto.jsonl attestation) alongside every release. Users installing via curl | sh must be able to verify.

Install Script Best Practices

Curl-pipe-sh is the universal fallback. Make it safe:

#!/bin/sh
set -eu
# fail fast, fail loud — never `|| true`

REPO="org/myapp"
VERSION="${MYAPP_VERSION:-latest}"
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
ARCH="$(uname -m)"
case "$ARCH" in
  x86_64|amd64) ARCH=amd64 ;;
  aarch64|arm64) ARCH=arm64 ;;
  *) echo "unsupported arch: $ARCH" >&2; exit 1 ;;
esac
# verify checksum, never skip

Rules:

  1. set -eu at the top; never swallow errors.
  2. Download checksum and signature; verify before extracting.
  3. Install to $HOME/.local/bin unless root — no surprise sudo.
  4. Print the exact install path and how to add it to PATH.
  5. Re-runnable (idempotent) — second run must be a no-op, not "already exists" error.
  6. Support MYAPP_VERSION=1.2.0 pinning and DRY_RUN=1.

Update-Checker Mechanism

Opt-in, async, privacy-preserving:

Decision Recommended
When to check Once per 24h, cached to $XDG_CACHE_HOME/myapp/update-check.json
What to send User-Agent: myapp/<version> (<os>/<arch>) only — no telemetry
Where to fetch GitHub releases API or static JSON behind CDN
How to notify One-line on stderr after command completes; never block
How to disable MYAPP_NO_UPDATE_CHECK=1, --no-update-check, and honour DO_NOT_TRACK=1
In CI Auto-disable when CI=1 or stdout is not a TTY

Never self-update binary files from the CLI — path-traversal, signing, and permissions all break. Delegate to the package manager (brew upgrade myapp, apt upgrade).

Anti-patterns

  • Publishing to npm a package that contains compiled binaries for only one platform (breaks for every other user).
  • curl | sudo bash install scripts that do not verify checksums — one compromised CDN hop owns every user.
  • Hard-coding /usr/local/bin in install scripts (Apple Silicon Homebrew uses /opt/homebrew/bin).
  • Shipping darwin/amd64 only and hitting Rosetta for ARM users (slow, and breaks signed-binary flows).
  • Leaving debug symbols in the release binary (5x size for no user benefit; strip or use -s -w).
  • Auto-updating the binary in place without signature verification.
  • Forgetting to bump the formula version SHA — brew install pulls the old tarball silently.
  • Running the update-check synchronously before the command's real work — adds network latency to every invocation.
  • Emitting the update-check notice during --json output — corrupts the JSON stream.

Handoff

  • To Launch: SemVer bump justification, changelog, rollback plan if artifacts are yanked.
  • To Gear: goreleaser/cargo-dist/napi-rs CI workflow, secret wiring (signing keys, notarization creds), release trigger.
  • To completion: completion install paths per package format.
  • To config: default config path that deb/rpm post-install should create.
  • To Sentinel (adjacent): supply-chain check — sign every artifact, publish SBOM, attest provenance.

Source: SKILL.md on GitHub

1 warning4mo5 checks · Risk SAFE
  • Gen Agent Trust Hub4mo

    The 'anvil' skill is a comprehensive development resource for building Command Line Interfaces (CLIs) and Terminal User Interfaces (TUIs). It provides standard templates and best-practice guidelines for Node.js, Python, Go, and Rust. The skill focuses on security-centric development, including safe secret handling, binary integrity verification, and cross-platform robustness. No malicious code or suspicious behaviors were detected, and identified static analysis flags were confirmed as false positives in the context of security-focused technical documentation.

  • Socket4mo

    No alerts

  • Snyk4mo

    Risk: LOW · No issues

  • Runlayer6mo

    7/9 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 16a3f28. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated last month

README badge

README badge for simota/agent-skills/anvil