Plugins
A plugin is a folder with a cornercase-plugin.toml that declares commands. cornercase runs those commands at the right moments: when you pick an entry in a menu, when something happens in the server, or for as long as the server or a window runs. The commands talk back to cornercase through the same commands for scripts and agents you use in a shell (cornercase status --json, events --json, send, notify, todo…), so a plugin can be written in any language: a shell script, Python, a compiled binary.
There is no SDK and no runtime inside cornercase. Whatever the commands can do, a plugin can do.
Install from GitHub
Section titled “Install from GitHub”cornercase plugin install acme/notes # the newest commit of its default branchcornercase plugin install acme/notes@v1.2.0 # a tag, a branch or a commitcornercase plugin install https://git.example.com/acme/notes.gitcornercase plugin install acme/tools/plugins/notes # a plugin in a folder of the repositorycornercase plugin install https://git.example.com/acme/tools.git//plugins/notes@v2install takes owner/repo on GitHub, any https:// git address, or a git folder on your machine. A repository can hold several plugins, each in its own folder with its own cornercase-plugin.toml: on GitHub, add the folder after the repository (owner/repo/plugins/notes); for any other address or a folder on your machine, put it after // (https://host/acme/tools.git//plugins/notes, ~/code/tools//plugins/notes). The folder is the plugin’s: only it is installed, and its commands must stay inside it. Folder parts are letters, digits, ., _ and -, never ., .. or starting with -. It fetches the plugin at one commit and, before anything in it runs, shows where it comes from, that commit, what it asks for, how it is built or downloaded, and every command it will run once on. Then it asks; with no terminal to ask in, pass --yes once you have read the preview. The plugin is installed turned off.
A plugin can ship ready-made archives, one per platform, each with its sha256. cornercase downloads the one for your machine, checks it against that sha256 before anything is unpacked, and unpacks only files and folders into the plugin’s folder. Without an archive for your machine, the plugin’s build steps run instead, once you have confirmed, in its folder; their output goes to the plugin’s log.
cornercase plugin update # every installed plugincornercase plugin update acme.notes # onecornercase plugin remove acme.notes # turns it off, waits for it to stop, deletes its installed copyupdate fetches the same address, folder and tag or branch again. If the commit did not change, it says the plugin is up to date and changes nothing; otherwise it shows the whole preview again and asks. An update keeps the plugin on or off as it was. A plugin pinned to a commit stays on it: install it again at another one. remove keeps the plugin’s settings and data folders.
Find plugins
Section titled “Find plugins”The plugin list is a marketplace of every public GitHub repository with the cornercase-plugin topic and a valid cornercase-plugin.toml, at its root or in a folder, refreshed every hour. The page has a Rising this week shelf (the fastest-growing repositories relative to their own size), Just landed (projects that joined recently), and Every plugin: a search over repositories, owners, languages and plugins, and five sorts (popular, trending, recently updated, newest, name). Each repository is a card with its description, stars, language and last push, and the command that installs it, pinned to the commit the list saw; a repository with several plugins lists each one’s command under its N plugins pill. The shelves need a few weeks of history to appear, and stay hidden until three repositories qualify.
Publish a plugin
Section titled “Publish a plugin”Put your plugin in a public repository, with its manifest at the root or in a folder (one repository can hold several plugins), and add the cornercase-plugin topic (the gear next to About on GitHub). It shows within an hour, at the latest commit of its default branch, and is read again when that commit changes. There is nothing to submit and no review queue. Forks, archived repositories and manifests that cannot be read or have malformed metadata are left out, and when two repositories declare the same id the older one keeps it.
Trust and security
Section titled “Trust and security”Nobody reviews the listed plugins: a listing only means the repository tagged itself. A plugin runs as you, so read its code or trust its author before you turn it on. The install command shows where the plugin comes from, the commit and every command it will run before anything installs, and a plugin installs turned off. The list’s pages load each owner’s avatar from GitHub in your browser, which tells GitHub you opened the page.
Link a folder, turn on and off
Section titled “Link a folder, turn on and off”While you write a plugin, link its folder instead: cornercase reads it where it is, and picks up your changes.
cornercase plugin link ~/code/my-plugin # adds it, turned offcornercase plugin enable my-plugin # turns it on, then says what it asks forcornercase plugin list # every plugin, on or off, and what it runscornercase plugin actions --context pane # what the plugins add to the menus (asks the server)cornercase plugin logs my-plugin -f # what its commands printcornercase plugin disable my-plugin # stops its service and window processescornercase plugin unlink my-plugin # forgets it; the folder staysA plugin starts off, installed or linked: none of its actions, hooks, services or window processes run until you turn it on. An installed plugin’s build steps, if it has any, run once you confirm the install. plugin list and Settings show what it asks for before you do. You can also turn plugins on and off in Settings › Plugins: each row shows the plugin’s name and version, whether it is on, and what it asks for. Enter switches it.
A running server notices changes within a second: turning a plugin on starts its service, turning it off stops it, and editing the manifest of a linked plugin reloads it. No restart is needed.
The manifest
Section titled “The manifest”id = "acme.notes" # lower-case letters, digits, . _ -; at most 64 charactersname = "Notes"version = "1.0.0"description = "A notes file per workspace"min_cornercase = "0.12.87" # optional: refused by an older cornercaseapi = 1 # optional: the plugin API it was written forplatforms = ["macos", "linux"] # optional: where it runs; empty means everywherepermissions = ["read", "send"] # shown before turning it on, not enforced
[[actions]] # an entry in the row and pane menusid = "open"title = "open notes"contexts = ["workspace", "pane"] # project, workspace, tab, panecommand = ["bin/notes", "open"]
[[events]] # a command run on each eventon = "status" # opened, closed, status, exitedcommand = ["bin/notes", "on-status"]
[[service]] # kept running by the serverid = "sync"command = ["bin/notes", "sync"]
[[window]] # kept running by each window on this machineid = "panel"command = ["bin/notes", "panel"]
[[build]] # run once when installing, after you confirmcommand = ["make"]
[download.aarch64-apple-darwin] # a ready-made archive instead of buildingurl = "https://github.com/acme/notes/releases/download/v1.0.0/notes-aarch64-apple-darwin.tar.gz"sha256 = "9f2c…" # 64 hexadecimal digits[download] may name aarch64-apple-darwin, x86_64-apple-darwin, aarch64-unknown-linux-gnu and x86_64-unknown-linux-gnu. An archive is a .tar.gz served over https://; its files are unpacked into the plugin’s folder. Links, devices and paths that lead out of the folder refuse the whole archive. When the archive’s sha256 differs, nothing is unpacked. If a build step changes cornercase-plugin.toml, nothing is installed.
Fields cornercase doesn’t know are ignored, so a manifest written for a newer cornercase still loads. The plugin’s folder and its manifest must be yours and not writable by other users. Its texts may not hold control characters or invisible ones (bidirectional overrides, zero-width spaces and the like), which could make a command read differently from what runs. A manifest that cannot be read shows in plugin list and in Settings with the reason, and never hides the other plugins.
Commands
Section titled “Commands”A command is a list: the program, then its arguments. It never goes through a shell, so nothing in it is expanded or split. A program with a / in its name, like bin/notes, is looked up in the plugin’s folder and must stay inside it: .., and symbolic links that lead out of the folder, are refused when the plugin is linked, installed or turned on, and plugin list and Settings say why. So is a program that is not there yet: build it before you link the folder. That program, and every folder between the plugin’s folder and it, must be yours and not writable by other users. A bare name, like python3, is looked up on the PATH.
Each command’s output, and its errors, go to the plugin’s log, with a line from cornercase when a service or window process starts, stops or exits. cornercase plugin logs <id> prints it.
Actions
Section titled “Actions”Each action shows at the bottom of the menus for its contexts: the ⋯ and right-click menus of project, workspace and tab rows, the tab bar, and the right-click menu of a pane. It runs in the folder of the workspace you clicked (the project’s folder for a project), and gets the ids of what you clicked in CORNERCASE_PROJECT, CORNERCASE_WORKSPACE, CORNERCASE_TAB and, for a pane, CORNERCASE_PANE. With CORNERCASE_PANE set, commands like cornercase send and read act on that pane by default. An action that fails shows its exit status in the window. It is stopped after 5 minutes.
Another app or a script lists the actions with cornercase plugin actions and runs one with cornercase plugin run PLUGIN ACTION --pane ID, the same way, and gets how it ended; see Plugins in Scripts and agents.
Event hooks
Section titled “Event hooks”A hook runs once for every matching event, the same events cornercase events prints. The event is in CORNERCASE_EVENT, as the JSON line events --json prints, with the ids of the item and its parents:
{"time":"2026-10-14T11:18:24.210Z","event":"status","agent":"claude","from":"working","to":"waiting","project":4,"workspace":3,"tab":6,"pane":5}on |
When |
|---|---|
opened |
A project, workspace, tab or pane opened (kind says which) |
closed |
One closed |
status |
An agent’s state changed: working, shell, waiting, done, idle, or null when the agent left |
exited |
The program in a pane’s foreground ended (program names it) |
A hook is stopped after 60 seconds. At most 16 hooks run at once; past that, events are dropped and the server log says so. For a long-running reaction, use a service that follows cornercase events --json.
Services
Section titled “Services”A service runs in the server for as long as the plugin is on. It starts once the session has come back, so the commands it runs find your projects. If it ends, it starts again after 1 second, then 2, 4… up to a minute between tries; one that ran for a minute counts as healthy again. It is stopped with the server, on kill-server and on restart (the new server starts it again). The Settings row says whether it runs or when it starts again.
Keep a service in the foreground: don’t detach it with nohup, setsid or & and exit. cornercase stops a service by stopping its process group, and a process that left it keeps running after the plugin is turned off.
Window processes
Section titled “Window processes”A window process runs on the machine that shows the window, for as long as that window is open: two windows run two copies. It runs only in local windows, never in one opened with cornercase remote, which has no socket on its machine.
What every command gets
Section titled “What every command gets”| Variable | Holds |
|---|---|
CORNERCASE_BIN |
The running cornercase binary, to call commands with the same version |
CORNERCASE_PLUGIN_ID |
The plugin’s id |
CORNERCASE_PLUGIN_ROOT |
The plugin’s folder |
CORNERCASE_PLUGIN_CONFIG |
A folder for its settings, private to you |
CORNERCASE_PLUGIN_STATE |
A folder for its data, private to you; the log lives there too |
CORNERCASE_EVENT |
For hooks: the event, as JSON |
CORNERCASE_PLUGIN_ACTION, CORNERCASE_CONTEXT |
For actions: which action, and whether a project, workspace, tab or pane was clicked |
CORNERCASE_PROJECT, CORNERCASE_WORKSPACE, CORNERCASE_TAB, CORNERCASE_PANE |
For actions: the ids of what was clicked |
Commands also inherit the server’s environment (or the window’s, for window processes), including CORNERCASE_SOCKET when it is set, so cornercase reaches the right server. Claude Code’s session variables and the issue trackers’ token variables (SHORTCUT_API_TOKEN, LINEAR_API_KEY, JIRA_API_TOKEN, PLANE_API_KEY) are removed.
An example
Section titled “An example”A plugin that sends a desktop notification when an agent needs you, and adds copy pane id to the pane menu:
id = "nudge"name = "Nudge"version = "0.1.0"permissions = ["notify"]
[[events]]on = "status"command = ["./on-status"]
[[actions]]id = "id"title = "show pane id"contexts = ["pane"]command = ["./show-id"]#!/bin/shcase "$CORNERCASE_EVENT" in *'"to":"waiting"'*) "$CORNERCASE_BIN" notify "an agent needs you" ;;esac#!/bin/sh"$CORNERCASE_BIN" notify "this is pane $CORNERCASE_PANE"chmod +x ~/code/nudge/on-status ~/code/nudge/show-idcornercase plugin link ~/code/nudgecornercase plugin enable nudgeWhere plugins keep things
Section titled “Where plugins keep things”| File | Location |
|---|---|
| Which plugins are installed or linked, from where, and on | plugins.json, next to config.json, readable only by you |
| An installed plugin’s files | plugins/installed/<id>/<commit>/, next to session.json, one folder per version |
| A plugin’s settings folder | plugins/config/<id>/, next to config.json |
| A plugin’s data and log | plugins/state/<id>/, next to session.json; the log is plugin.log, moved to plugin.log.1 past 1 MiB |