Skip to content

Repository files navigation

scripteratops πŸ¦–

One CLI-tool architecture, expressed in seven languages. Each *-script skill teaches the same design β€” pure logic split from side effects, side effects hidden behind injectable boundaries, a real test suite, and a type/test/format gate β€” adapted to one language's idiomatic toolchain.

Pick the language by need (see Choosing); the shape of the tool is identical everywhere, so moving between them is muscle memory.

Install

scripteratops is built on the Agent Skills open standard (SKILL.md), which Claude Code, OpenAI Codex, and Gemini CLI all read natively β€” the skills auto-trigger by relevance on each. Install with whichever tool you use.

These skill / plugin / extension systems are new and moving fast (late 2025 / early 2026). If a command below has drifted, check your tool's current docs β€” the Any tool fallback always works.

Claude Code

In an interactive claude session:

/plugin marketplace add https://github.com/buildosaurus/scripteratops
/plugin install scripteratops@scripteratops
/plugin marketplace update scripteratops   # later, to pull new versions

OpenAI Codex

codex plugin marketplace add buildosaurus/scripteratops

Then install scripteratops from the plugin directory. Codex reads the same skills and implicit invocation is on by default, so they auto-trigger as in Claude.

Gemini CLI

gemini extensions install https://github.com/buildosaurus/scripteratops

Gemini asks for a one-time consent the first time each skill activates.

Any tool β€” no packaging

Codex and Gemini both scan ~/.agents/skills/, so you can skip the manifests entirely:

git clone https://github.com/buildosaurus/scripteratops
cp -R scripteratops/skills/* ~/.agents/skills/

Prefer just one skill, by hand? For Claude Code, copy a single folder into your config instead:

git clone https://github.com/buildosaurus/scripteratops
cp -R scripteratops/skills/rust-script ~/.claude/skills/rust-script

Installing everything is cheap β€” a skill is free when idle (it only triggers when relevant), so there's no cost to having all eight.

Quickstart

Skills trigger from what you ask β€” you don't invoke them by name. Just describe the tool:

  • Name the language and its skill runs directly:

    "Write a Rust CLI that fetches a URL and prints the JSON pretty-printed."

  • Skip the language and the router picks one from your constraints, then hands off:

    "I need a small command-line tool that shells out to git, ships as a single static binary, and has tests."

Either way you get the same shape β€” pure logic, injected boundaries, an exit-code contract, and a passing type/test/format gate.

The skills

Not sure which language? Start with the router β†’ scripteratops picks one from your constraints and hands off. The seven language skills:

The shared architecture (identical across all seven)

  • Boundaries are the only mock hooks. Everything that leaves the process β€” subprocess, network, filesystem, environment β€” sits behind one seam (an interface/trait/protocol, or a wrapper function in shell). Tests inject a fake; production wires the real one.
  • Pure logic is separate β€” input β†’ output, no I/O, tested by direct call.
  • Config precedence, resolved in one pure function: `CLI flags > environment

    config file > defaults`. Secrets get no CLI flag (argv is world-readable); a config is never logged raw (secret fields are redacted).

  • Colorized logging to stderr (stdout is for program data), -v/-q verbosity, --color auto|always|never.
  • --dry-run short-circuits before mutating anything.
  • Typed errors mapped to an exit-code contract: 0 ok Β· 1 runtime Β· 2 usage (where the CLI library cooperates β€” Python/Rust/shell emit 2; Clikt and Cliffy map usage errors to 1, see caveats) Β· 124 subprocess timeout Β· 130 SIGINT Β· 143 SIGTERM (141 broken pipe where the language surfaces it).
  • Self-contained & pinned β€” one file or one project, dependencies pinned, a man page via the toolchain's generator or help2man.

Toolchain at a glance

Language Packaging / run CLI library Tests Type gate Format + lint Distribution
Python uv + PEP 723 (single file) argparse (stdlib) pytest mypy ruff uv run (+ install.py)
Swift SwiftPM package swift-argument-parser Swift Testing compiler swift-format swift build -c release
Kotlin Gradle (core+app) Clikt JUnit 5 + kotlin.test compiler ktlint installDist / GraalVM native
TypeScript Deno (one project) Cliffy deno test deno check (tsc) deno fmt + deno lint deno compile (single binary)
Go Go module cobra go test compiler gofmt + go vet go build (single binary)
Rust Cargo (lib+bin) clap (derive) cargo test compiler rustfmt + clippy cargo build --release (single binary)
Shell one Bash script hand-rolled while/case bats-core β€” (shellcheck) shfmt + shellcheck the script itself (chmod +x)

The type gate is free (the compiler) everywhere a language has one; Python adds mypy as a separate step, and shell has no type system so shellcheck plays that role.

Building blocks at a glance

Language Config source HTTP client Subprocess Indent
Python TOML / JSON (stdlib) httpx subprocess 4-space
Swift JSON (Codable) URLSession Foundation.Process 2-space
Kotlin JSON (kotlinx.serialization) java.net.http ProcessBuilder 2-space
TypeScript JSON fetch Deno.Command 2-space
Go JSON (encoding/json) net/http os/exec tabs
Rust JSON (serde_json) ureq std::process 4-space
Shell key=value file curl direct (timeout) 2-space

Installing the toolchains

You only need the toolchain for the language(s) you actually use β€” each is a one-line install.

Toolchain macOS Linux Windows (native)
uv (Python) brew install uv curl -LsSf https://astral.sh/uv/install.sh | sh winget install astral-sh.uv
Deno (TypeScript) brew install deno curl -fsSL https://deno.land/install.sh | sh winget install denoland.deno
Go brew install go distro package / go.dev/dl winget install GoLang.Go
Rust brew install rust (or rustup) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh winget install Rustlang.Rustup
JDK (Kotlin) brew install openjdk (or SDKMAN!) SDKMAN! / distro package winget install EclipseAdoptium.Temurin.21.JDK
Swift Xcode CLT (xcode-select --install) swiftly / swift.org swift.org installer (rougher)
Shell tools brew install bash shellcheck shfmt bats-core distro package (apt install shellcheck …) via Git Bash / WSL
  • uv is recommended, not required β€” a dependency-free Python script runs with plain python3; uv (or pipx run) only becomes necessary once the script pins third-party deps, where it builds the ephemeral env for you. uv can even install Python itself (uv python install).
  • Kotlin needs only a JDK β€” the Gradle wrapper is committed, so there's no separate Gradle install.
  • Man pages install into a MANPATH directory on macOS/Linux; there is no native man on Windows.

On Windows: native vs WSL

The five cross-platform toolchains β€” uv, Deno, Go, Rust, and the JDK (Kotlin) β€” run natively on modern Windows (winget covers them all). The two Unix-centric variants are the exceptions:

  • Shell isn't native β€” run it under Git Bash (ships with Git for Windows) or WSL.
  • Swift has a native Windows toolchain, but it's less mature than on macOS / Linux / WSL.
  • man pages don't exist on native Windows.

For a uniform experience β€” and to get the Unix-centric tools cleanly β€” WSL (Windows Subsystem for Linux) gives you a full Linux environment where all seven work exactly as on Linux. And whatever you develop on, Go / Rust / Deno binaries cross-compile to Windows regardless.

Differences & honest caveats

Every variant carries a wart or two β€” better named than discovered:

  • Python β€” the only one where the type check is a separate gate (mypy). uv is recommended, not required: a dependency-free script runs under plain python3 script.py (the PEP 723 header is just a comment), and pipx run is an alternative β€” uv only earns its keep once the script pins third-party deps. Mind the uv run flag order (uv run --with X tool …, flags for tool go after its name).
  • Swift β€” swift test (Swift Testing) needs the Xcode toolchain (DEVELOPER_DIR), not the bare Command Line Tools; the binary is per-OS and the ecosystem is Apple-centric.
  • Kotlin β€” runs on the JVM (needs a JDK to build, a JRE to run the app image); the first Gradle build is slow; the Gradle wrapper is committed for reproducibility; Clikt maps usage errors to exit 1, not 2.
  • TypeScript β€” Deno is deny-by-default: the tool declares --allow-run/net/env/read (baked into deno compile); Cliffy's usage errors exit non-zero but not the conventional 2.
  • Go β€” the module name must be path-form (example.com/tool), not a bare word (a bare name trips go mod tidy); tabs (gofmt is non-configurable); cobra groups subcommands, not flags, so --help lists all flags flat.
  • Rust β€” no HTTP or JSON in std, so serde + ureq earn their place; the subprocess timeout is hand-rolled (std has none); Rust ignores SIGPIPE, so a tool | head can panic on a broken pipe (handle the write error if you stream); 4-space (rustfmt); the only variant that natively uses exit 2 for usage errors.
  • Shell β€” no type system (shellcheck is the safety net); the CLI is hand-rolled (-vv bundling not parsed β€” repeat -v -v); a hard subprocess timeout needs GNU timeout (macOS β†’ brew install coreutils); no JSON without jq; needs bash 4+ (macOS ships 3.2 β€” brew install bash).

Choosing a language

If you need… Reach for
A single self-contained binary, cross-platform, systems-level speed Go (simplest, fast builds, glue-first) or Rust (strictest, safest)
Ubiquitous glue with zero toolchain, present on every box Shell (bash 4+) β€” or Python for anything non-trivial
Rapid prototyping, data wrangling, the largest library set Python
To live in the JS/TS ecosystem, or a modern batteries-included runtime TypeScript (Deno)
Apple platforms, or an existing Swift codebase Swift
The JVM / Android, or an existing Kotlin codebase Kotlin

Rules of thumb:

  • Distribution matters most? Go and Rust ship one self-contained binary with no separate runtime (statically linked to varying degrees β€” fully static on Linux via a musl target); Deno compiles one too; a shell script needs only bash; Python needs uv; Swift/Kotlin are platform- or JVM-bound.
  • Correctness matters most? Rust's type system is the strictest, then Swift/Kotlin/Go/TypeScript (all statically typed), then Python (opt-in via mypy), then shell (shellcheck, no types).
  • Speed of writing matters most? Python and shell are the quickest to a first working version; the compiled languages trade a little ceremony for a binary and stronger guarantees.

How each skill is structured

Every *-script skill ships a SKILL.md (the decisions) plus an assets/ template that is a real, gate-passing project β€” copy it, adapt it, delete what the tool doesn't earn. (The scripteratops router is the exception: it carries only a SKILL.md, since its job is to choose a language and hand off, not to ship a template.) A request for a tool in language X produces the project shape, the CLI contract, the exit codes, and only the robustness rules the job triggers (shelling out earns the timeout + preflight; the network earns the HTTP boundary + timeout; holding config/secrets earns precedence + redaction).

License

MIT β€” see LICENSE.

About

Agent Skills that write command-line tools the same way in seven languages and testable by design, with a real suite and a lint/type gate. Claude Code, Codex, Gemini CLI.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages