A modern task runner with Lua taskfiles and built-in background task management.
Real code over YAML Β· Global tasks Β· Detached background jobs
- β‘ Tasks are real code β
Taskfile.luatasks are Lua functions with conditionals, parameters, and inter-task calls. No YAML gymnastics. - π Global tasks β Define once in
~/.config/taskx/Taskfile.lua, run from any directory. - π Background execution β
tkx bstartdetaches a task from your terminal. Close the window, the job keeps running. - π Live output β
tkx watchtails any background job's log in real time; Ctrl+C leaves the viewer without killing the job. - π Multi-instance with a safety gate β Same task can run twice; tkx warns about conflicts and asks before launching.
- π§© Shell registry β Default pwsh on Windows, bash elsewhere. Register custom shells in
config.lua, pick per-run with--shell. - π¦ Single binary β One ~8 MB executable, zero runtime dependencies.
| Doc | What's inside |
|---|---|
| Taskfile Authoring Reference | Complete ctx API, args shape, task declaration forms, common patterns, debugging |
| Design Spec | Architecture decisions, background mechanism, shell model |
| Example Taskfile | Runnable sample with args and OS branching |
Scoop (Windows):
scoop bucket add XiaTian-AC https://github.com/XiaTian-AC/XiaTian-AC-bucket
scoop install tkxHomebrew (macOS/Linux):
brew tap XiaTian-AC/XiaTian-AC-bucket
brew install tkxFrom source:
go install github.com/XiaTian-AC/taskx@latestCreate ~/.config/taskx/Taskfile.lua:
local tasks = {
hello = function(ctx)
ctx:echo("hello world")
end,
}
return taskstkx hello # β hello world
tkx ls # list all tasksDeclared args get validated by tkx; unknown flags are rejected with the allowed list.
local tasks = {
release = {
desc = "test, commit, tag, push",
args = {
tag = { type = "string", required = true, desc = "git tag, e.g. v0.1.2" },
force = { type = "bool", required = false, desc = "force push" },
},
run = function(ctx, args)
ctx:sh("go test ./...")
ctx:exec("git", {"tag", args.tag}) -- no shell, no injection
if args.force then
ctx:sh("git push --force --tags")
else
ctx:sh("git push --tags")
end
end,
},
}
return taskstkx release --tag v0.1.2
tkx help release # shows desc + typed arg listtkx bstart dev-server # detach; survives terminal close
tkx ls-running # dev-server#1 pid 12345 running
tkx watch dev-server # live tail; Ctrl+C exits viewer only
tkx stop dev-server # kills the process tree
tkx clean --older-than=7d # prune ended instances + logstkx config # dump effective config (display + shells)
tkx ls-running # filtered by display.ls_running.time (default 1h)
tkx --taskfile .\dev\My.lua hello # run from any Taskfile without touching global
tkx build --shell bash # pick a registered shell for one runflowchart TD
A["tkx <task>"] --> B[CLI dispatch<br/>internal/cli]
B --> C[config.lua<br/>shells + display]
B --> D[Taskfile.lua<br/>global or --taskfile]
C --> E[gopher-lua VM<br/>tasks as functions]
D --> E
E --> F{mode?}
F -->|foreground| G["ctx:sh / ctx:exec<br/>stdio inherited"]
F -->|bstart| H[detached child<br/>tkx _run name#N]
H --> I[("logs/name#N.log")]
H --> J[("run.json")]
I --> L[watch / stop / clean]
J --> L
classDef entry fill:#3B82F6,stroke:#2563EB,color:#fff,stroke-width:2px
classDef process fill:#10B981,stroke:#059669,color:#fff,stroke-width:2px
classDef decision fill:#F59E0B,stroke:#D97706,color:#fff,stroke-width:2px
classDef store fill:#8B5CF6,stroke:#7C3AED,color:#fff,stroke-width:2px
classDef tools fill:#06B6D4,stroke:#0891B2,color:#fff,stroke-width:2px
class A,B,C,D,E,H process
class F decision
class G entry
class I,J store
class L tools
Files live under the tkx config directory (~/.config/taskx/ on Windows too, honoring XDG_CONFIG_HOME).
| File | Purpose |
|---|---|
Taskfile.lua |
Your global tasks. Must return a tasks table. |
config.lua |
Optional: custom shells + display preferences. |
config.lua options:
return {
shells = {
gitbash = "C:\\Program Files\\Git\\bin\\bash.exe",
mysh = "/opt/mytool/bin/mysh",
},
display = {
ls_running = {
time = "1h", -- "0" running only Β· 30m/1h/2d/1w window for ended jobs
running_first = true, -- running on top of ended
newest_first = true, -- newest first within each group
},
},
}Inspect the effective values with tkx config. Full option reference: Taskfile Authoring Reference.
Every task receives a ctx. Eight methods, all colon-called:
| Method | Behavior |
|---|---|
ctx:sh(cmd, opts?) |
Run through the resolved shell; non-zero exit raises a Lua error |
ctx:exec(name, args?) |
Run a binary directly β no shell, no injection |
ctx:run(name, args?) |
Call another task in the same file |
ctx:echo(...) |
Print to stdout |
ctx:ask(prompt, default?) |
Read a line; falls back to default when stdin is gone (background) |
ctx:cwd() / ctx:os() |
Working directory / "windows" | "linux" | "darwin" |
Environment variables: use Lua's built-in os.getenv(name).
See docs/lua.md for the complete surface, args semantics, and safety notes.
main.go entry point, delegates to internal/cli
internal/
βββ cli/ command dispatch (ls/bstart/watch/stop/clean/configβ¦)
βββ config/ XDG paths, config.lua loader, duration helpers
βββ taskfile/ Taskfile.lua loader (function + table forms)
βββ runtime/ ctx bindings: sh/exec/run/echo/ask/cwd/os
βββ argparse/ generic --flag parser, strict spec validation
βββ shell/ shell resolution incl. Windows Git Bash detection
βββ bg/ registry, detached launcher, procs, watch
docs/lua.md Taskfile authoring reference
testdata/Taskfile.lua runnable example
integration/ end-to-end tests
.goreleaser.yaml multi-platform release config
.scoop/ .brew/ package manager templates rendered by CI
| Layer | Choice |
|---|---|
| Core | Go 1.22+, standard library only |
| Task language | Lua 5.1 via gopher-lua (embedded, no external runtime) |
| Releases | GoReleaser + GitHub Actions |
| Packaging | Scoop bucket + Homebrew tap, hashes updated automatically per tag |
Pushing a v* tag runs the release pipeline: multi-platform builds (windows/linux/darwin Γ amd64/arm64), checksums, GitHub Release β then re-renders the Scoop manifest and Homebrew formula with fresh SHA256s and commits them to their buckets. See .github/workflows/release.yml.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing) - Open a Pull Request
Run tests locally: go test ./... and go test -tags integration ./integration/.