A modular, editor-agnostic build system for font development. Tasks are composed
of "chutes" — single-operation modules chained together with >>.
make init # runs uv sync + lefthook installuv run pytest tests # run tests
uv run ruff check . # lint Python
uv run ruff format . # format Python
uv run mdformat . # format Markdown
shfmt -w <file> # format a shell file
uv run lefthook install # (re)install git hooksDo not call python or python3 directly — asdf manages Python here and
requires a .tool-versions entry that isn't set for this project. Always
prefix with uv run.
Hooks are defined in gh-actions/lefthook.yml (referenced via lefthook remotes
in the local lefthook.yml) and installed as a Python package via uv (not the
system binary). On commit, lefthook automatically formats staged .py, .md,
and .sh files and re-stages the results.
If the hooks aren't firing, run uv run lefthook install.
Two jobs in .github/workflows/test.yml:
- lint — runs once; checks Python formatting + linting (ruff), Markdown
(mdformat), and shell (shfmt). Markdown and shell checks use
git ls-files | xargsto avoid descending into.venv. - test — runs against Python 3.11, 3.12, and 3.13.
Hellbox— class-level task registry; used as a context manager to define tasksTask— holds a chain of chutes and optional requirements (other task names)Chute— base class for pipeline steps;>>wires chutes togetherReadFiles/WriteFiles— built-in chutes for I/O; exposed astask.read()/task.write()Autoimporter— discovers installed packages viaimportlib.metadataand imports them into the caller's namespace; called viaHellbox.autoimport()
No runtime dependencies. Version is defined in hellbox/__version__.py and
read dynamically by the build system via [tool.setuptools.dynamic].
Static site built with Astro + Svelte, deployed to GitHub Pages via
.github/workflows/docs.yml. The workflow triggers on any push to docs/**
and requires Pages source to be set to "GitHub Actions" in repo settings.
cd docs
npm install # first time
npm run dev # dev server at localhost:4321
npm run build # builds to docs/dist/docs/
src/
layouts/Layout.astro # base HTML shell; accepts title and description props
pages/index.astro # the index page; add new pages here as *.astro files
styles/global.css # global styles; imported by Layout.astro
public/ # copied as-is to dist/
fonts/ # 16th Century Gothic woff2
CNAME # hellbox.dev domain mapping
install.sh # the curl-able install script
astro.config.mjs # enables the Svelte integration
svelte.config.js # Svelte compiler config (generated by astro add svelte)
@astrojs/svelte is installed and registered, so .svelte files work anywhere
in src/. Import them into pages or layouts the same way as .astro components:
---
import MyWidget from "../components/MyWidget.svelte";
---
<MyWidget client:load />Use a client:* directive when the component needs to run in the browser
(e.g. client:load, client:visible). Omit it for purely static/server-rendered
output — Astro will render the component to HTML at build time and ship no JS.