write scripts for Lua, execute from shell. This is a tiny Lua module that makes it easy to write utilities that work from either bash or Lua.
Example script (see also: #pkg-script)
#!/usr/bin/env -S lua -e "require'pkglib'()"
local G = G or _G -- typosafe globals

--- module documentation
local M = G.mod and mod'myCmdName' or {} -- self-documenting module
G.MAIN = G.MAIN or M

local mty = require'metaty'
local shim = require'shim'

--- Comamnd documentation
M.Args = mty'Args' {
  'name [string]: the name to say hello to', name='World',
}

M.main = function(args)
  args = M.Args(shim.parseStr(args))
  print('Hello '..args.name..'!')
end

if M == MAIN then os.exit(M.main(G.arg)) end
return M -- return as library

Then in PKG.luk add main = 'myCmdName.Main' to enable better documentation formatting. Get documentation with doc 'myCmdName'.

Mod shim

shim: use a lua module in lua OR in the shell. Functions This is for convinience, use a table if it's not enough. Note: if the input is already a table it just returns it. * fn parseList(strlist) -> args
Note: typically use parse() or parseStr() instead. * fn short(args, short, long, value) -> nil
Helper for dealing with -s --short arguments. Mutates args to convert short paramaters to their long counterpart. * fn boolean(v)
Duck type: always return a boolean (except for nil). See BOOLS (above) for mapping. * fn boolean(v)
Duck type: always return a boolean (except for nil). See BOOLS (above) for mapping. * fn bools(args, ...) * fn number(num)
Duck type: always return a number * fn string(v)
Duck type: always return a string This is useful for some APIs where you want to convert number/true/false to strings Converts nil to '' * fn list(val, default, empty) * fn listSplit(val, sep)
Duck type: if val is a string then splits it if it's a list leaves alone. * fn file(v, default, mode--[[w+]]) -> file, error?
Duck type: get a file handle. If v or default is a string then open the file in mode default='w+' * fn expand(args)
expand string keys into --key=value, ordered alphabetically. This is mostly useful for interfacing with non-lua shells. * fn getEnvBool(k) * fn popRaw(args, to) -> to
pop raw arguments after '--' Removes them (including '--') from args. * fn runSetup(args, force)
Setup lua using require(LUA_SETUP).setup(args, force). The default is to use ds.setup. * fn construct(Args, args) -> ok, err?
Construct a metaty-like object from args. If Args.subcmd is truthy then treats it as a table of subcmds. Looks for a subcmd at args[1