Give local LLMs gated control of your Emacs environment
El Yayster is a single-file Emacs package that lets a local LLM read your open buffers and act via gated Emacs Lisp, prompting before every mutation.
1.0.0Add to Favorites
Why it matters
Turn Emacs into an agentic coding environment where a local LLM can perceive your live buffers, modes, and working directory, then act on them through gated Emacs Lisp-drafting code, running commands, and exploring documentation while you retain approval over every mutating action.
Outcomes
What it gets done
Read open buffers and current editor state to give the model situational awareness
Execute approved Emacs Lisp expressions and shell commands in your working directory
Explore Emacs documentation via apropos and describe for self-discovering capabilities
Stream non-blocking responses from any OpenAI-compatible endpoint with token tracking
Source
Get it from source
Spark does not host a copy of it.
Open sourceReports
Agent outcome reports
No reports yet
Overview
yayster.el
El Yayster is a single-file Emacs package that lets a local LLM agent perceive your open buffers, mode, and directory, then act inside your Emacs session via gated Emacs Lisp capabilities. Read-only actions auto-approve; mutating actions (eval elisp, write a buffer, run a shell command) always show the full command and prompt for approval before running. Use it when you want an agent that can act directly inside a running Emacs session against a local model, and you're willing to review and approve every mutating action it proposes.
What it does
El Yayster turns Emacs into a body a local LLM agent inhabits, rather than a chat buffer the model replies into: each turn it perceives your live environment (open buffers, current major mode, working directory) and acts on it through a gated set of Emacs Lisp capabilities - read_buffer, list_buffers, apropos, and describe are read-only and auto-approve by default; eval_elisp, write_buffer, and run_command are mutating and always prompt for permission (Allow / YOLO / Deny on a graphical frame, [y]/[n]/[!] in a terminal), showing the full, untruncated command or elisp that will run. It runs the ReAct loop (ACTION:/ARGS:/FINAL: text protocol, with a FINAL: line ending the turn and never executed even if quoted) against any OpenAI-compatible /chat/completions endpoint, with local Ollama as the default happy path and no cloud requirement; it has been tested with local models like Qwen and GLM with no code changes. Requests run in a non-blocking curl subprocess so Emacs stays responsive, streaming progress into a *yayster* buffer whose header line shows the live model, context-window occupancy (auto-detected from Ollama's /api/show), and per-turn/session token counts. It ships as a single self-contained file (yayster.el) with no ELPA package dependencies beyond cl-lib, json, subr-x, and the curl binary, plus an optional yayster-mode-line.el for a mode-line indicator that pulses while a turn is in flight.
To install:
(add-to-list 'load-path "/path/to/yayster.el") ; the directory
(require 'yayster)
(yayster-awaken)
When to use - and when NOT to
Use it if you want an agent that can actually read your open buffers and act inside your running Emacs session - editing a buffer, evaluating elisp, running a shell command - rather than one confined to a separate chat window, and you're comfortable reviewing and approving every mutating action it proposes. It is deliberately v0 and small, meant to be read end to end and extended, not a polished production tool; YOLO mode (skip prompts for the rest of the session) is opt-in and session-scoped, never the default, and read-only tools auto-approve by default - meaning any open buffer, including one holding secrets, can be sent to the model without a prompt, which is a real footgun if you aren't paying attention to what's open. It is not suited to anyone who wants a fully sandboxed or hosted agent; a hosted API key is written to a mode-0600 temp file and passed to curl via -H @FILE so it never appears in ps, but the model itself still runs against whatever endpoint you configure.
Inputs and outputs
Input is a plain-English request via M-x yayster-step, run against the buffer and directory current at that moment. Output is the agent's FINAL: answer, echoed in the minibuffer and logged in the *yayster* buffer, plus any tool calls it made along the way (buffer reads, elisp evaluations, shell command output capped at yayster-command-max-bytes with a yayster-command-timeout-second limit, default 30s).
Integrations
Works against any OpenAI-compatible chat-completions endpoint - Ollama (localhost:11434) is the default, with yayster-hosts supporting switching between multiple configured endpoints via M-x yayster-use-host. Requires Emacs 28.1+ and the curl binary on PATH.
Who it's for
Emacs users who want a local, model-portable coding/editing agent that operates directly inside their live session with an explicit, readable permission gate on every mutating action - not people who want an unattended or fully autonomous agent. MIT licensed.
Source README
El Yayster
The Emacs. The Lisp. A wild little yes: give a local LLM the keys.
Most "LLM in Emacs" packages make Emacs a mouth that talks to a model: you type,
it replies into a buffer. El Yayster inverts that. It makes Emacs a body the
model inhabits - the agent perceives your live environment, and acts on it
through gated Emacs Lisp. Decide → call a tool → read the result → repeat, against
any OpenAI-compatible endpoint. Local Ollama is the happy path. No cloud required.
The name is the joke and the thesis. El is the definite article - the Yayster -
and it is also el, as in Emacs Lisp. The rest is the dare: a carefree, fun-loving
mindset, the kind that looks at eval and M-x and says yes, seat the model
there too. The agent drafts; you still commit.
One self-contained file: yayster.el. No ELPA packages - cl-lib, json,subr-x, the curl binary, and a reachable model.
Why it's different
- Situated. Every turn, the agent is told what buffers you have open, your current
mode, and your working directory. It knows it's in Emacs and speaks from inside it. - It acts, gated. Capabilities like
eval_elisp,write_buffer, andrun_command
let the agent operate the editor - but every mutating action asks your permission
first. The agent drafts; you commit. - Self-discovering.
aproposanddescribelet it explore Emacs's own
self-documentation. Its toolset grows from the editor itself. - Local-first & model-portable. Any OpenAI-compatible
/chat/completions
endpoint. Tested across local models (Qwen, GLM) with no code changes. - Non-blocking. Requests run in a
curlsubprocess; Emacs stays responsive.
Progress streams into a*yayster*buffer. - Observable. The
*yayster*header line shows the live model, context-window
occupancy (auto-detected from Ollama's/api/show), and per-turn / session token
counts.
Install
Drop
yayster.elsomewhere on disk.Add to your init:
(add-to-list 'load-path "/path/to/yayster.el") ; the directory (require 'yayster) (yayster-awaken) ;; Optional: quick keys (global-set-key (kbd "C-c y") #'yayster-step) (global-set-key (kbd "C-c Y") #'yayster-use-host)Point it at your model (defaults to
http://localhost:11434/v1/chat/completions):(setq yayster-endpoint "http://localhost:11434/v1/chat/completions" yayster-model "qwen3:8b")
Requirements
- Emacs 28.1+
curlon yourPATH(built into macOS;apt install curlon Linux)- An OpenAI-compatible chat endpoint. Easiest is Ollama:
ollama pull qwen3:8bthen it serves onlocalhost:11434.
Use
M-x yayster-step(orC-c y) - ask in plain English.
Watch*yayster*; the answer also echoes in the minibuffer.
Tools run in the buffer and directory that were current when you issued the turn.M-x yayster-use-host(orC-c Y) - switch between endpoints inyayster-hosts.M-x yayster-abort- kill a hung turn, unlink temp files, clear the busy flag.
Optional: mode-line indicator
yayster-mode-line.el shows the current host, model, and a pulse while a turn
is in flight:
⌬ local qwen3:8b (idle)
⌬ local qwen3:8b ◐ (a turn is running)
(require 'yayster-mode-line) ;; installs itself into mode-line-format
It refreshes via advice on turn start/finish, host switch, abort, and safety
re-arm - no polling. Remove it with M-x yayster-mode-uninstall.
Try:
- "What buffers do I have open?" → read-only, instant
- "Count the functions matching org-agenda." → the agent discovers + calls
apropos - "Give me a directory listing of my home dir, 5 most recent." →
run_command
(you'll be asked to approve it)
Capabilities
| Capability | Mutating? | What it does |
|---|---|---|
read_buffer |
no | Read a buffer's text |
list_buffers |
no | List buffers + major modes |
apropos |
no | Find functions matching a pattern (self-discovery) |
describe |
no | Read a function's docstring |
eval_elisp |
yes | Evaluate an Emacs Lisp expression |
write_buffer |
yes | Replace a buffer's contents |
run_command |
yes | Run a shell command |
Read-only capabilities auto-approve (yayster-auto-approve-readonly, default on).
Mutating capabilities always prompt via yayster-confirm-function. The
prompt includes the capability name and the full argument payload - the
command or elisp that will run, no truncation - so a rebind to y-or-n-p is
not blind. The same text is shown in *yayster-permission*. On a graphical
frame: Allow, YOLO (stop asking for the rest of the session; re-arm withM-x yayster-safety-on), and Deny. On a terminal: [y]/[n]/[!]. C-g /
cancel is deny.run_command times out after yayster-command-timeout seconds (default 30) and
caps output at yayster-command-max-bytes.
The ReAct protocol: ACTION: / ARGS: must start their own lines; a FINAL: line
ends the turn and is never executed, even if an ACTION pair is also quoted. Malformed
JSON ARGS is not a tool call.
⚠️ Safety
Mutating tools (eval_elisp, write_buffer, run_command) always prompt
(Allow / Deny). The prompt shows the full command or elisp - no truncation.
Default install is that gate - read the payload before you Allow.
YOLO is opt-in and session-scoped until M-x yayster-safety-on. It is not
the default. Demo the package with prompts on.
Read-only tools (read_buffer, list_buffers) auto-approve and can still send
any open buffer (including secrets) to the model. That is the point, and also
the footgun. The gate only protects you if you read what you approve.
If you set yayster-api-key for a hosted API, the bearer token is written to
a mode-0600 temp file and passed to curl as -H @FILE. It never appears on ps.
How it works
Five small pieces, all in one file:
- Capabilities - a registry of gated Elisp functions (the agent's hands).
- Permission gate - read-only vs. mutating; mutating always asks.
- Situating prompt - live environment + a tool manifest.
- Agent loop - ReAct (
ACTION:/ARGS:/FINAL:) against your endpoint. awaken- seats El Yayster at startup.
It's a v0 and deliberately small - easy to read end to end, easy to extend.
Tests
emacs -Q --batch -L . -l yayster.el -l test/yayster-tests.el \
-f ert-run-tests-batch-and-exit
Roadmap
- Native tool/function-calling (JSON-Schema
tools) as an alternative to the ReAct text protocol - Token-level streaming into
*yayster* - Automatic host failover
- org-roam-backed memory (remember / recall as a knowledge graph)
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.