Upgrade to the latest version with
lde upgrade!
New REPL
lde repl has been rebuilt from scratch with a custom readline implementation — no external dependencies required.
lde repl

The new REPL supports line editing, history, and syntax highlighting, implemented in pure Lua with platform-specific raw terminal handling for both POSIX and Windows. It runs inside the lde environment, so require() works as expected for your project’s dependencies.
Android support
lde now runs natively on Android via Termux. Prebuilt binaries for aarch64-linux-android are included in every release.
The install script handles Android automatically:
curl -fsSL https://lde.sh/install | sh
Native curl, git, and archive libraries
lde’s three core I/O subsystems have been rewritten to use native C bindings rather than shelling out or using pure Lua implementations:
- curl-sys — HTTP downloads now use libcurl directly, replacing subprocess calls to the
curlbinary. OpenSSL is statically linked on all platforms, so no runtime TLS dependency. - git2-sys — Git operations now go through libgit2, replacing subprocess calls to the
gitbinary.gitno longer needs to be installed. On Linux and Android, libgit2 links dynamically to OpenSSL (which is typically already present). On macOS it uses SecureTransport, and on Windows it uses WinHTTP. - deflate-sys — Archive extraction now uses libdeflate via FFI, replacing subprocess calls to
tar,zip, andunzip. libdeflate is self-contained with no external dependencies.
These are compiled ahead of time and bundled into the lde binary.

Git submodule support
All git clones — including dependency installs — now recursively initialize submodules:
{
"dependencies": {
"mylib": { "git": "https://github.com/example/mylib" }
}
}
If mylib has submodules, they’re cloned too. This was previously only done in some cases.
Test lifecycle hooks
lde-test now supports afterAll and afterEach hooks for teardown logic:
local test = require("lde-test")
test.afterEach(function()
-- runs after every test in this file
cleanup()
end)
test.afterAll(function()
-- runs once after all tests in this file
db:close()
end)
test.it("does something", function()
-- ...
end)
afterEach runs after every individual test. afterAll runs once when all tests in the file are done.
Lockfile auto-invalidation on lde add / lde remove
Previously, adding or removing a dependency could leave the installed target/ out of sync until you manually ran lde sync. Now, lde add and lde remove automatically invalidate the lockfile cache so the next lde run or lde sync re-installs cleanly.
Fixes
- archive: handle deeply nested zip archives
- upgrade: pass
User-Agentheader to GitHub API to avoid rate limiting - repl: fix
os.tmpnameto useTMPDIRon Android/Termux - android: replace
os.tmpnamein runtime to resolve correctly under Termux - sea: fall through to
lddfallback when-dumpmachineoutput doesn’t contain a libc identifier - sea: use stdout when stderr is empty for compile error output
- sea: fix
string.findpattern mode for glob matching - process2: fix deadlock in async process reading
- process2: stop merging stdout and stderr into a single stream
- test: run test callbacks inside the lde environment to avoid upvalue leaks
- sea: use compiler-derived target triple for libc and architecture detection instead of host arch