Gists guide
A gist is a plain Python file in ./gists/ (project-local) or ~/.repld/gists/ (global) that the kernel hot-reloads on import. Re-import after editing — the kernel evicts the stale module and loads the new one.
Writing a gist
Section titled “Writing a gist”repld gist new myappThis scaffolds ./gists/myapp.py split the way a gist is meant to grow: a docstring, a commented-out __repld_deps__, a plain async def holding the logic, and an Annotated-typed _tool_* wrapper below it. The top half is portable and survives if you ever graduate the gist into a real service; the bottom half is the repld wiring you shed.
A typical gist wraps a web app’s internal API:
"""MyApp — accounts, transactions, reports."""
class MyApp: def __init__(self, tab): self._tab = tab
@classmethod async def connect(cls): import repld tab = await repld.browser.get("*myapp.com*") return cls(tab)
async def accounts(self): return (await self._tab.fetch("/api/accounts"))["body"]
async def create_order(self, items): return await self._tab.fetch("/api/orders", method="POST", body={"items": items})from myapp import MyAppapp = await MyApp.connect()await app.accounts()Auto-reload
Section titled “Auto-reload”Re-importing a gist reloads it:
from myapp import MyApp # first import# ... edit myapp.py ...from myapp import MyApp # picks up changesThe kernel tracks mtimes and evicts stale modules from sys.modules.
Dependencies
Section titled “Dependencies”Declare external dependencies:
__repld_deps__ = ["httpx>=0.27", "pandas>=2.3"]The kernel scans these at boot and asks before installing anything missing. Two other forms are accepted: "." installs the gist’s own project as editable, and "path:some/dir" just prepends a local directory to sys.path for vendored code with nothing to install.
__repld_deps__ = [ "httpx>=0.27", # PEP 508 requirement — installed ".", # the project containing this gist, editable "path:./vendor", # no install — straight onto sys.path]Installs land in a shared, interpreter-versioned directory (~/.local/share/repld/deps/py3.12), never in your project’s venv — uv sync would prune them, and a kernel bound to a project runs under an ephemeral uv run overlay that differs every invocation. The directory is appended to sys.path, so your project’s own packages always win.
MCP tool registration
Section titled “MCP tool registration”A gist can register MCP tools that appear alongside built-in tools. Name a handler _tool_{name} with typed parameters and the schema is inferred automatically — no separate declaration needed:
async def _tool_lookup_company(org_number: str) -> dict: """Look up a Norwegian company by org number.""" from brreg import Brreg b = Brreg() return await b.company(org_number)Type hints and defaults become the JSON schema (str→string, int→integer, float→number, bool→boolean, list→array, dict→object; no annotation defaults to string; no default marks the param required). The first docstring line becomes the tool description. Tools appear in tools/list automatically — no exec round-trip needed. repld gist new <name> scaffolds this pattern.
Describe an individual parameter by wrapping its type in Annotated:
from typing import Annotated
async def _tool_lookup_company( org_number: Annotated[str, "Nine-digit Norwegian organisation number"], include_roles: Annotated[bool, "Also fetch board members"] = False,) -> dict: ...Cross-project linking
Section titled “Cross-project linking”Gists are tracked in a central registry (~/.config/repld/gist-registry.json). Link a gist from another project without copying:
repld gist add weather # resolves from registry, writes ./gists/.linksrepld gist list # shows local + linked + linkablerepld gist rm weather # unlinkrepld gist rm --stale # clean up broken linksThe .links manifest records absolute paths and is meant to be committed — stale entries are skipped at load rather than rewritten. Local gists always shadow linked ones of the same name.
Starting from someone else’s gist
Section titled “Starting from someone else’s gist”fetch is new’s sibling, not add’s: it copies the .py files out of a GitHub gist into ./gists (or ~/.repld/gists with --global), stamping a # source: header.
repld gist fetch https://gist.github.com/someone/abc123repld gist fetch <url> --name weather --globalNothing tracks the file afterwards, so rm — which only unlinks — is not how you undo it; delete the file. A bare gist id works too, as does a raw gist.githubusercontent.com URL, but nothing else: the file lands somewhere a kernel imports at boot, so the set of origins stays one a reader recognises. The fetched file’s __repld_deps__ is deliberately not installed — this is code from a URL, and its dependency list shouldn’t drive an install before you’ve read the file.
Linting
Section titled “Linting”repld gist lint # everything a kernel here would importrepld gist lint --local # just ./gists — usable as a per-project CI gaterepld gist lint weatherChecks the docstring first line, documented return shapes, undeclared __repld_deps__, and the removed __repld_tools__ API. Suppress a rule inline with # gistlint: ignore=<rule>.
Conventions
Section titled “Conventions”- Module docstring first line becomes the gist’s description in MCP instructions
__repld_usage__overrides the auto-generated import hint__repld_help__overrides the first-line description- Async classes should have a
connect()classmethod that resolves browser tabs - Return dicts/lists, not custom objects — the agent works with JSON-serializable data