starcli is the standard, fully-loaded Star* CLI. But it is built on a small
reusable core β the kit package β that you can use to assemble your
own CLI: a single Go shell that embeds your Starlark scripts and wires only
the modules you need. The standard starcli and your shell construct their
runtime through exactly the same path, so they behave the same.
The idea: Go is the shell, Starlark is the app. Your
main.gostays a few lines and never changes; your logic lives in.starfiles you//go:embed.
This folder is a separate Go module on purpose: each demo depends on only the
runtime plus the modules it wires, so its go.mod is the honest, minimal
dependency tree a real shell would have.
cd examples
go run ./hello # embed one .star, run it with a builtin module
go run ./qrcard # wire one starpkg module (qrcode) into the shellThe entire program is the embed + one call. Everything else is Starlark.
//go:embed app.star
var app string
func main() {
if _, err := kit.Run(app, kit.WithModules("math")); err != nil {
log.Fatal(err)
}
}A build-your-own shell picks the domain modules it wants and hands their loaders
to the kit. Here the shell imports just github.com/starpkg/qrcode:
import "github.com/starpkg/qrcode"
func main() {
kit.Run(app, kit.WithLoader(qrcode.ModuleName, qrcode.NewModule().LoadModule()))
}Its dependency tree is starbox + qrcode and nothing else β none of the other
starpkg modules the turnkey starcli carries.
kit follows starbox's resolution order, so you mix three styles freely:
| You write | Resolves as | Use for |
|---|---|---|
kit.WithModules("json", "math") |
starlet builtins (auto) | the standard library |
kit.WithLoader("qrcode", loader) |
an explicit loader you import | a specific starpkg/custom module |
kit.WithDynamicLoader(fn) |
a registry resolved on demand | exposing many modules at once (this is how starcli itself wires every starpkg module) |
For more than one script, embed a whole directory and run an entry point β the
other files are reachable via load():
//go:embed scripts/*.star
var scripts embed.FS
func main() {
kit.RunFS(scripts, "scripts/main.star", kit.WithModules("json"))
}kit.Run(app,
kit.WithModules("json", "math"),
kit.WithGlobal("env", "prod"), // inject host values as script globals
kit.WithMaxSteps(1_000_000), // bound runaway loops
kit.WithMaxOutputEntries(100), // bound result size
kit.WithPrintFunc(myPrinter), // control how print() renders
)See the kit package docs for the full option set.