Imported from max-sixty/leaf (
scripts/AGENTS.md). Install upstream withnpx skills add max-sixty/leaf --skill scripts. Copyright stays with the author.
Repository tooling
These scripts are developer tooling: the installed plugin is the whole tracked tree,
so a host copies these along with it, but nothing under skills/leaf reads them at
runtime. Python tools use the environment pinned by the root pyproject.toml and
uv.lock; the JavaScript builds — the browser framework and every vendored bundle —
use package.json and package-lock.json.
Because the payload is the tracked tree, what a script generates lands under .tmp/
unless a committed path is where the output's reader finds it: the vendored bundles
below, examples/corpus.html and its data, the catalog pin the site resolves example
previews through, and the demo frames the README and the site's cards draw from.
Evidence, previews, staged sites, and probe results have no such reader, so a run of
one of them leaves the tracked tree unchanged.
Each script's own docstring and --help own its behavior, flags, and lifecycle. This
file says which script owns what, and the rules that hold across them.
Examples and previews
preview.py [page]serves one public example or developer fixture as a live page under.tmp/previews/<source-stem>, or underLEAF_PREVIEWS_ROOTwhere that names a directory, watching the fixture and the selected runtime. It re-vendors when the layer it watches changed or the source asked for different packages, and copies the source alone otherwise, because vendoring mints a fresh layer generation and the generation is part of what a revision is as executable code. A preview that re-vendored on every save could therefore only ever show a revision arriving as a fresh document, which is the half of the behavior a user is least likely to be looking for.--exportinstead writes the page as one file that opens offline. A preview runs in the foreground like a dev server, and each start builds its page fresh. It takes no task claim;--userclaims the page so a user's presses reach this session./developing-leafstates which to choose.corpus.pygenerates the internalexamples/corpus.htmlstress fixture and its companion data from the examples, regression pages undertests/fixtures/pages/, and the developer feature gallery.example_assets.pyfetches the immutablemax-sixty/leaf-assetscommit named byexample-previews.jsoninto.tmp;site.pycalls it when that revision is absent.example-previews.py, invoked aswt refresh-previews, draws the stills selected bydocs/examples.htmlthrough the live published-example server. It refuses fallback fonts, pushes the complete image set, and updates the tracked commit pin and catalog.
Edit a source page, then regenerate the corpus. examples/AGENTS.md owns the fixture
rules a new or changed example has to meet.
Website and demo
site.pybuilds https://leaf.page/ as complete page directories in.tmp/siteand their derived live shells in.tmp/site-assets, then bundles the public runtime with the website's esbuild dependency. Runnpm ci --prefix workerfirst.--serveopens the same Wrangler asset and container boundary the deployed site uses and also needs a running Docker. It also writes what a crawler reads:robots.txt, asitemap.xmlof the clean routes, and each page's card. A page's title and description are authored in its own source, and the build refuses one that has neither. The rest of the card — the Open Graph and Twitter declarations and the image — comes fromsite_metadatainworker/server.pyand enters Leaf's document composer for both the edge shell and the container response. The canonical link is not the site's: every Leaf document names its own page root, so the three addresses a page answers collapse onto one wherever a page directory is published. Each page's image is named in the manifest,docs/session-card.pngfor a product page and the catalog preview for an example, andcheck_linksresolves it the way it resolves an href.verify-site-local.shchecks that built output through that boundary and prints the document, widget-upgrade, and presentation milestones with the requests and bytes loaded by presentation. It requires the browser's startup profile to reach the Worker and checks that activating one page leaves a neighboring page on the edge. A failed check prints the Worker's log beside the browser's own account of the page that stopped it. Pull requests run it for review evidence..github/workflows/publish-site.yamldeploys both halves for relevant pushes tomain; it runs the local check before the first public operation, then verifies the exact release again after deployment. That production pass also sends one private comment and requires the hosted Codex task to publish a revision and reply. Withverify_site.py --agent, the verifier prints the request acknowledgement, activity transitions, publication, reply, and changed-page presentation timings. The Worker'sstartup_failedreceipt triggers one retry; rate limits and all other unsuccessful endings fail the deployment on the first ask. The gate reads the receipt'sfailurecode, never its wording.worker/README.mdowns the failure contract.uv run scripts/verify_site.py localruns the same delivery, App Server, edit, publication, reply, and browser-reload path against the host's Codex login. It bypasses the Cloudflare Worker, container resources, and outbound credential proxy, so it checks agent behavior without measuring production infrastructure.benchmark-site.py local|ORIGINemits that complete journey as one JSON sample: browser presentation, a comment sent through the real Threads composer, acknowledgement and activity, the first agent reply text visible in the open thread, requested publication and durable reply, then the changed page's presentation and revision follow. Both targets run the same HTTP and browser checks.localonly provisions the canonical Python adapter and explicitly starts its turn; it does not emulate Cloudflare's Worker, container allocation, or routing.deploy-site-dev.shpublishes the current checkout to the one standingleaf-website-devCloudflare environment and runs that benchmark against itsworkers.devorigin. The command always selects thedevWrangler environment; production deployment stays inpublish-site.yaml. Hosted-agent diagnostics live in Workers Observability. Query its REST API directly with the canonical event id or visible session reference; once the Container starts a Codex turn, itsturnIdalso finds the model-request timings. Analytics Engine is aggregate product telemetry, not a log index.eval_claude_delivery.py [BASE_REF]is a basic, imperfect paired eval of theleaf waitcarrier. It runs headless Claude Code sessions with BASE_REF's plugin and this checkout's at the same time, and compares how promptly each acknowledges and replies to one real comment. Its docstring lists what it does not yet measure.record-demo.shregeneratesdocs/demo.gif;record-demo.pydraws it and the three photographs of the same staged scene beside it — the README's light and dark session stills, andsession-card.pngat the 1.91:1 an unfurler draws a card at. Keep the latter while the product can make those frames stale.
MCP Apps probe
mcp-app/run-direct-probe.sh bundles the runtime into a ui:// resource and runs it
in a pinned checkout of the official reference host; mcp-app/README.md owns its
inputs, flags, and what each run checks. Its evidence is scratch under
.tmp/mcp-app/experiments/<number>/, replaced whenever that number runs again. No
install reads it, so what survives a run is the part a maintainer copies into
notes/mcp-apps/experiments/<number>/results/ because the written-up result cites it.
Vendored bundles
browser/build.mjs owns the TypeScript sources under scripts/browser/ and the
committed outputs: the framework module, lit.js — the page's one copy of Lit, which
the framework and the Web Awesome bundle both import — and their dependency licenses
under skills/leaf/assets/vendor/, plus source maps and a build manifest under
scripts/browser/generated/ for contributors. The manifest records inputs, exports,
dependencies, and byte hashes. Run npm ci,
then npm run build:browser to regenerate them. npm run check:browser typechecks
and rebuilds in memory, failing if committed outputs are missing or differ;
npm run test:browser exercises reproducibility, stale-output refusal, the import
gate, and immutable snapshot publication. Node runs only for contributors. Plugin
installation, page initialization, source activation, and export copy or consume
the committed browser output without invoking a compiler.
vendor.py rebuilds the other third-party bundles — all of them by default, or the
ones you name — from the same install. package.json is the one manifest for every
JavaScript version that ships: it pins each package a build's own source imports,
esbuild among them, and package-lock.json settles the rest of every closure, so
a package another one depends on is held where its dependant's range and the lock
put it. Each bundle lands in the package whose widget
imports it, except mcp-app, which no widget imports and which lands in
skills/leaf/mcp-app/ for an MCP host to read from the install.
scripts/vendor-src/pierre/ is Pierre's native generator source. Its shiki-leaf.mjs
contains exactly one /* LEAF_PIERRE_LANGUAGES */ sentinel; vendor.py replaces it
with one "<name>": () => import("@shikijs/langs/<name>"), entry for every registry
language before bundling.
Run npm ci first. After it, every bundle reproduces its tracked bytes exactly, so a
clean git status after vendor.py is the check that the bundles still match the
lock and the script; CI's test job runs it. A diff means the lock moved without a
rebuild, or the script or registry changed.
webawesome builds a chrome entry and an optional-widget entry with shared chunks.
The chrome entry loads the standard search and copy controls; optional widgets
load their remaining controls on demand. Shared dependencies and scoped theme defaults
are registered once in the document and declared shadow stages. The chrome entry and
shared chunks live under assets/vendor/; the optional entry remains in the default
package's vendor directory. Page vendoring composes both into the page's vendor root.
Rerun vendor.py after npm install moves a pin or the lock, or after changing the
registry input a bundle reads; do not patch a generated bundle or
examples/corpus.html directly.
