stickypane

A terminal board your coding agent writes to and you read: kanban, checklists, charts, forms and live logs kept as plain Markdown files, with MCP tools for Claude Code, Codex and any agent.

Documentación

stickypane

A board in your terminal that your coding agent writes to and you read.

Kanban boards, checklists, charts, forms and live logs for AI coding agents such as Claude Code and Codex: plain Markdown files, a CLI, an MCP server and a terminal UI (TUI) written in Go, next to your agent in tmux, WezTerm or any terminal.

CI Release Go Report Card License: MIT

English · 한국어

Install · Quick start · Keys · Command line and MCP · Contributing · Roadmap · Discussions

An agent's shell on the left runs one-line commands; the board on the right ticks the checklist, moves the card to Done, and shows the log, the chat and the chart as they are written

An agent working in the pane next to you produces a lot of text, and the things that matter scroll away with it: where the plan stands, what is done, what it wants you to decide. Chat is the wrong place for that. A board is the right one.

stickypane is that board. It is a folder of plain files (.sticky/), shown as panes in a terminal window: your agent writes a file, it appears; it ticks an item, the box ticks; it asks a question, a form with buttons shows up, you press one, and the agent reads your answer back. You work the board with keys or the mouse and every change lands in the same files, so the agent sees what you did without being told.

The board next to an agent: a kanban with who has each card and since when, a checklist that keeps when each item was done, a chart counted from the kanban, and a chat

What it shows. Notes in Markdown, kanban boards, checklists, charts, Mermaid diagrams, forms you answer, logs followed as they grow, shell scripts with a Run button, and folders as books of pages. Any file of your project — the README, a log under logs/ — can be put on the board with one command.

What it asks of the agent. Nothing it does not already know how to do. Writing a Markdown file is enough; for the rest there are one-line commands that need no reading first (stickypane todo plan add "write tests", stickypane chart tokens add input 1200, stickypane show README.md) and the same as MCP tools. A one-screen guide, added to CLAUDE.md or AGENTS.md, or the plugin, tells it all of this.

What it asks of you. Nothing to configure: no server, no hooks, no terminal plugin. It runs in a plain window, in tmux, WezTerm or Zellij, next to Claude Code, Codex or any agent that can edit files. The files are yours, in your repository, readable without stickypane.

Status: design stage. stickypane is pre-1.0 and I am still deciding how the board should behave; file names, command names and sticky.json may change until v0.1.0. The most useful thing you can do is try it with your agent for a day and tell me what you expected the board to do: open a design feedback issue or an Idea. The open questions are in ROADMAP.md; the principles in VISION.md.

Contents

Install

stickypane is one program with no runtime dependencies. It runs on macOS, Linux and Windows, in any terminal that shows Unicode and color: Terminal, iTerm2, Ghostty, WezTerm, Kitty, Alacritty, Windows Terminal, and inside tmux or Zellij. No special font is needed.

Homebrew

brew install --cask LeeSwallow/tap/stickypane

A release archive

Every release has an archive for each system: darwin (macOS), linux and windows, each for amd64 (Intel and AMD) and arm64 (Apple silicon, ARM). Each holds the program, the license, and the licenses of the libraries it is built from.

macOS and Linux:

VERSION=0.1.0 OS=linux ARCH=amd64      # or OS=darwin, ARCH=arm64
curl -LO "https://github.com/LeeSwallow/stickypane/releases/download/v$VERSION/stickypane_${VERSION}_${OS}_${ARCH}.tar.gz"
tar -xzf "stickypane_${VERSION}_${OS}_${ARCH}.tar.gz" stickypane
sudo install stickypane /usr/local/bin/   # or any folder on your PATH

On macOS, a program downloaded by a browser is held back by Gatekeeper; xattr -d com.apple.quarantine stickypane lets it run. Homebrew does this for you.

Windows, in PowerShell:

$v = "0.1.0"
Invoke-WebRequest "https://github.com/LeeSwallow/stickypane/releases/download/v$v/stickypane_${v}_windows_amd64.zip" -OutFile stickypane.zip
Expand-Archive stickypane.zip -DestinationPath "$env:LOCALAPPDATA\stickypane"
# add it to your PATH, once
[Environment]::SetEnvironmentVariable("Path", "$env:Path;$env:LOCALAPPDATA\stickypane", "User")

Each release lists the archives' SHA-256 sums in checksums.txt; check one with shasum -a 256 <archive> or, in PowerShell, Get-FileHash <archive>.

With Go

go install github.com/LeeSwallow/stickypane/cmd/stickypane@latest

This needs Go 1.26 or later and puts the program in $(go env GOPATH)/bin.

From source

git clone https://github.com/LeeSwallow/stickypane
cd stickypane
go build -o stickypane ./cmd/stickypane

Check that it works

stickypane version     # stickypane 0.1.0
stickypane env         # the system, shell, pane tool, locale and editor it found

Update and remove

Update the way you installed it: brew upgrade --cask stickypane, a newer archive, or go install ...@latest again. To remove it, run stickypane setup --undo if you installed the plugin, then brew uninstall --cask stickypane or delete the program. Your notes stay in each project's .sticky/ folder; they are plain files, yours to keep or delete.

Quick start

1. Open the board next to your agent. In the project's folder, split the terminal and run stickypane in the new pane:

TerminalCommand
tmuxtmux split-window -h stickypane, or a popup: tmux display-popup -E stickypane
WezTermwezterm cli split-pane --right -- stickypane
Zellijzellij run --direction right -- stickypane
Windows Terminalwt -w 0 split-pane -V stickypane
anything elsea second window or tab, in the same folder: stickypane

stickypane env prints the right command for the terminal you are in.

2. The first run sets the project up. In a git repository it makes .sticky/ at the top of the repository and tells your agent about the board, then opens it. Nothing to configure: every setting has a default (S on the board changes them).

3. Teach your agent, once. Pick one:

WayHowFor
the pluginstickypane setupClaude Code and Codex: five skills, slash commands, a session hook
a guide in its instructionsdone by step 2: a short guide in CLAUDE.md or AGENTS.mdany agent that reads those files
MCPstickypane setup --mcp, or claude mcp add stickypane -- stickypane mcpany MCP client

For another MCP client, the server is the command stickypane mcp on standard input and output:

{ "mcpServers": { "stickypane": { "command": "stickypane", "args": ["mcp"] } } }

4. Ask for things. "Keep a checklist of this refactor on the board." "Explain the structure on the board as a page." "Ask me on the board before you deploy." The agent writes the files; the board shows them as they change, and what you do on the board lands in the same files.

More on the setup

Telling the agent means a short guide where it reads it: CLAUDE.md and AGENTS.md when they exist, a Claude Code skill (.claude/skills/stickypane/SKILL.md) when the project has a .claude folder but no CLAUDE.md, and a new AGENTS.md when there is nothing. An agent with the plugin installed gets nothing in its file, since the plugin teaches it. stickypane init does the same without opening the board, and works outside a git repository too; it is safe to run again. --skill writes the skill instead of the instruction files and --no-agent-docs makes the folder only.

stickypane setup installs the plugin for every agent it finds, through the agents' own commands:

stickypane setup                       # asks, then installs for Claude Code and Codex
stickypane setup --scope project --mcp --yes
stickypane setup --undo                # takes it out again

--scope is where Claude Code installs it: user (every project, the default), project (this repository, shared with the team) or local (this repository, just you). --mcp also registers stickypane mcp as an MCP server, --agents claude,codex limits it to some agents, and --dry-run prints the commands without running them. In a terminal it asks for what the flags did not say.

The plugin is in layers. The way in, using-the-board, says which of four skills fits a job: tracking-progress, asking-the-user, talking-in-chat and connecting-notes. The commands /board:show, /board:status, /board:index, /board:track, /board:ask, /board:say, /board:kinds and /board:setup each start one. A session hook tells the agent what is on the board when a session starts in a project that has one. In a git repository the agent's first stickypane show or stickypane todo makes the board, so no setup step is needed.

The folder is the board

One file in .sticky/ is one note. Create a file to stick a note, edit it to update the note, delete it to take the note down. A one-line file is a complete note:

check env before deploy

What a file is depends on its name:

FileIs
*.mda note; its front matter may give it a shape (below)
*.log *.txt *.outa log: shown as it is and followed as it grows
*.sh *.ps1a script: shown with a Run button, run with sh or PowerShell
a foldera tab: a screen of its own, with the files in it as notes
a folder in a taba book: one note whose pages are the files in it
anything elsenot shown
.sticky/
  sticky.json         the board: theme, language, which tab is open, the root tab's layout
  10-plan.md          a note of the root tab, named after your project
  build.log           a log, followed live
  deploy/             a tab
    sticky.json       this tab's title and layout
    run.sh            a script; its output goes to run.log
    docs/             one note with three pages
      01-intro.md
      02-usage.md
      03-faq.md

A number in front of a name orders the notes, the pages and the tabs and is not part of the title: 10-plan.md is shown as "plan" and a folder 20-release as the tab "release", until front matter or sticky.json gives it a title of its own.

Tabs. Each folder of .sticky/ is a tab, listed on the first line of the screen with its number; the files in the folder are its notes, and the root folder is the first tab, named after the project. 1–9, ( ) or a click switch tabs; the active tab is remembered. A tab's folder can hold its own sticky.json with a title and the layout of its notes, so one folder is one complete, shareable screen. stickypane show deploy/run.sh switches to that tab.

Books. A folder inside a tab is shown as one pane that scrolls like one long note: its files follow one another, each under a rule with its name, and the border says which page the view is on (docs · usage 2/3). j k, the wheel and the paging keys scroll through all of it; , and . jump to the page before or after; every other key acts on the page the view is on. stickypane link docs puts a folder of your project on the board as a tab without copying it, and stickypane link README.md does the same for a file; both make a symbolic link, so what you change on the board is changed in the real file.

Scripts. enter on a script asks first (Run deploy.sh in myproject?), then runs it with sh in the project folder. The output is written to a log of the same name next to the script, which the board opens and follows while the script runs. A script never runs without that yes, each time: it is a file your agent may have written.

Markdown notes. Front matter says what a note is. Every key is optional.

KeyValues
typenote (default), board, checklist, log, chart, form
titleshown in the title bar and the note's border
viewfor a chart: bar, spark, heat

sticky.json: how the board is arranged

Where a note is on the screen is not written into the note. It goes to sticky.json: the root one for the root tab and for the board as a whole, and one in each tab's folder for that tab's notes, the way Bruno keeps a folder's settings in the folder. The board writes them as you press keys:

{
  "notes": {
    "10-plan.md": { "open": true, "size": "half", "pin": true },
    "build.log": { "title": "CI build", "open": true, "rows": 12 },
    "docs": { "open": true, "color": "blue" }
  },
  "order": ["docs", "10-plan.md", "build.log"],
  "ignore": ["drafts", "*.tmp.md"],
  "theme": "catppuccin-mocha",
  "tab": "deploy",
  "version": 1
}

A tab's own file (deploy/sticky.json) has title, notes keyed by the names inside the tab, and order; theme, language, ignore and tab are only in the root file.

Key in notesValuesKey on the board
opentrue shows the note, false folds it awayo, enter
sizepage (whole width), half, card+ -
rowsthe height in lines the note asks for
coloryellow pink blue green purple orangec
pintrue keeps the note firstp
titlea name for a book, a log or a scriptR

Next to notes, theme names the theme (T on the board), language the language of the screen (stickypane language ko; auto follows LANG), order lists the notes you moved and ignore what to leave out.

order lists the notes you moved with { and }; the others follow by name. ignore is the one part you write by hand: names or patterns the board should not show.

Keeping this apart from the notes means three things. A log, a script or a linked README can be opened, sized and named although it has no front matter. The board never rewrites a note because you rearranged the screen, so it cannot collide with your agent writing that note. And you can commit the file to share a layout, or leave it out of git to keep it yours.

A Markdown note may still carry open, size, rows, color and pin in its front matter. That is the note's own proposal, which is how an agent says "show this now"; what sticky.json says wins. A note that neither opens is folded away, except that a note that appears while stickypane is running is shown for that run, so what your agent just wrote is in front of you at once.

Shapes of a Markdown note

Front matter gives a Markdown note its shape. Below, a release tab with four of them: a log, a form, a Mermaid diagram and a chart.

A release tab: a build log, a form that asks where to deploy with buttons to answer, a Mermaid diagram and a sparkline

Board. Each ## Heading is a column and each top-level list item is a card. Indented lines under a card are its details. Any other line under a column shows up as a card too, and text before the first heading is the board's description.

---
type: board
title: Auth work
open: true
---
## To do
- payments
## Doing
- login API
  - refresh tokens come later
## Done
- schema

Checklist. - [ ] and - [x] lines, shown with a progress bar.

Log. One entry per line. Next to other notes it asks for ten lines, and it follows its end as the file grows until you scroll up. A .log file is the same thing without front matter.

Chart. Each label: number line is a value; any other line is shown as text above the chart. view picks the drawing: bar (default), spark for a one-line trend, or heat for a calendar of days when the labels are dates.

---
type: chart
title: Tokens per day
view: heat
---
2026-09-28: 41,200
2026-09-29: 8,900
2026-09-30: 0
app       ████████████████████████ 61        9 10
board     ████████████▏            31   Mon  █ ·
checklist ████████▎                21        ▒ ▓
store     ██████▎                  16   Wed  ·

Diagrams. A mermaid code block in a plain note is drawn as a diagram instead of being shown as source. Flowcharts (graph, flowchart), sequence diagrams and entity-relationship diagrams (erDiagram) are drawn; a block that cannot be drawn keeps its source.

```mermaid
graph LR
  agent --> file --> board
```
┌───────┐     ┌──────┐     ┌───────┐
│ agent ├────►│ file ├────►│ board │
└───────┘     └──────┘     └───────┘

Chat. A conversation with your agent, kept in the folder. Each message is @name HH:MM text under a heading for its day; the board writes the time. You press n to answer, as $STICKYPANE_USER, git's user.name or your login name; the agent says things with stickypane say and hears you with stickypane watch --once --note chat --type message.added.

---
type: chat
---
## 2026-10-03
@min 14:02 can you rerun the tests?
@claude 14:03 41/41 pass

Form. A document that asks. Your agent writes the questions; you answer on the board; the answers land in the same file.

---
type: form
title: Deploy now?
open: true
---
The build is green. Where should it go?

## Target
- ( ) staging
  Try it there first.
- ( ) production

## Also
- [ ] run migrations
- [ ] clear the cache

## Note
> 

[ Deploy ] [ Cancel ]
  ○ staging                      ╭──────────────────────────────╮
      Try it there first.        │ after lunch                  │
› ◉ production                   ╰──────────────────────────────╯
  ☑ run migrations               ┏━━━━━━━━┓ ╭────────╮
  ☐ clear the cache              ┃ Deploy ┃ │ Cancel │
                                 ┗━━━━━━━━┛ ╰────────╯
LineIs
- ( ) textan option; choosing one clears the others
- [ ] textan option; choose as many as you like
indented line belowthe option's description
> texta line to fill in
[ Label ] [ Label ]buttons; a form without any gets [ Submit ]

Everything else is Markdown and is drawn as such. Options of one kind in a row are one question, named by the heading above it. Pressing a button writes submitted: <label> and submitted_at into the front matter; changing an answer afterwards takes them out again. The agent reads the answers from the file, or waits for them:

stickypane wait deploy --timeout 10m
# submitted: Deploy
# at: 2026-10-02T14:03:05+09:00
# Target: production
# Also: run migrations
# Note: after lunch

Notes are ordered by file name, pinned ones first, until you move them. A file that does not fit its shape is still shown, never hidden.

Keys

Any noteOpen board or checklist
tab / shift+tabnext, previous noteh lchange column
enteropen, then zoomj kchange card or item
oopen or closeH Lmove a card sideways
+ / -bigger, smallerJ Kreorder a card
Njot a notespacetick an item
aadd by shapennew card or item
p / c / Rpin, color, renameOpen script
{ / }move earlier, laterenterrun, after a yes
, / .turn a book's pages
mmove to a folder
x / D / uarchive, delete, undoZoomed note
e / Eedit here, in $EDITORescback
r / ? / qreload, help, quitj k g Gscroll
Tnext theme
zzoomOpen form
[ / ]previous, next screen
1–9 / ( )switch tab
j kchange control
enter / spacechoose, type, press

The focused open note takes the keys in the right-hand column where it is; you do not have to zoom in first. When the focused note has no cursor of its own, j k g G and the paging keys (pgdn pgup, space b, ctrl+f ctrl+b, ctrl+d ctrl+u) scroll it inside its pane; a note with a cursor scrolls to keep the cursor in view.

How the screen is shared. Open notes are placed left to right by size (page takes a row, two half notes share one, cards sit three or more to a row) and each row is stretched to the full width. The rows then share the height: a short note takes what it needs and the long ones split the rest, so the screen is always full and never scrolls as a whole. A note gets at least ten lines; notes that would get less go to the next screen, shown as 2/3 under the title bar. A form uses enter itself, so z is the way to zoom into one.

Notes that link

Notes name each other with [[name]], as in Obsidian: [[plan]], [[plan#write tests]], [[deploy/run|the run]]. A link is drawn as its name or alias, and a name finds its note with or without .md and its folder. f on a note lists the notes it links to and the notes that link to it, and enter goes there; the index shows how many notes link to each one, and stickypane index --json lists links and backlinks.

A commit calendar, a note that links to others and shows a checklist inside it, and a bar chart

A line that is only ![[plan]] shows that note there, in its own shape and read only: a checklist with its progress, a board with its columns. It follows the note it shows. Embeds go one level deep.

A chart can be computed from another note: give it from: and stickypane fills in its values and keeps them up to date, in the file itself.

---
type: chart
title: Progress
from: plan          # one note: done and open (a board: cards per column)
---

With from: plan, release it draws one bar per note: percent done for a checklist, cards for a board, answered questions for a form, lines for a log. That is the whole list; anything else is a stickypane watch --exec recipe.

Notes that react

Everything that happens on the board is an event: a note made or removed, an item ticked, a card moved, a form sent, a line logged, a message said, a chart value changed. stickypane watch prints them as they happen, one a line, and runs a command for each with --exec, so one note can answer another and anything outside can listen:

stickypane watch                                   # 14:02:05  plan.md  item.ticked  write tests
stickypane watch --json | jq .                     # for programs
stickypane watch --note plan --type item.ticked \
  --exec 'stickypane chart progress add done 1'    # a chart that counts the ticks
stickypane watch --type card.moved \
  --exec '[ "$STICKY_TO" = Done ] && printf "\a"'  # a bell when a card is done
stickypane watch --once --note deploy --type form.submitted   # wait for the user

--note and --type filter (--type card takes every card event), and --once ends at the first event, which is how an agent waits for the user to do something. The command given to --exec runs in your own shell with the event in STICKY_EVENT (JSON) and in STICKY_NOTE, STICKY_KIND, STICKY_TYPE, STICKY_ITEM, STICKY_FROM, STICKY_TO and STICKY_TIME. Nothing reacts unless you start a watch: the board itself never runs anything without asking. Over MCP the same events come from wait_event.

API requests (experimental)

A .http note is an API client on the board, in the format resterm and the VS Code REST Client read, so the same file works there too: REST requests, WebSocket sessions and gRPC calls side by side. enter sends the picked one after a yes; under the list, the board shows what that request got last time.

An API tab: a POST, a WebSocket session and a gRPC call, with the picked session's transcript under them

# @env dev

### Create user
POST {{rest}}/users
Content-Type: application/json

{"name": "{{user}}"}
# @assert response.statusCode == 201
# @capture file id {{response.json.id}}

### Live updates
# @websocket timeout=5s idle-timeout=1s subprotocols=json
# @ws send {"type":"subscribe","channel":"builds"}
# @ws wait 500ms
# @ws close 1000 done
# @assert response.received >= 1
GET {{socket}}/events

### Get user
# @grpc users.v1.Users/Get
# @grpc-plaintext true
# @grpc-metadata x-trace-id: demo-1
# @assert response.grpc.status == "OK"
GRPC {{grpc}}

{"id": "{{id}}"}
RESTWebSocketgRPC
written asa method and a URLa ws:// URL, or # @websocket, and # @ws steps: send, send-json, send-base64, ping, pong, wait, closeGRPC host:port, # @grpc package.Service/Method, a JSON body
the log showsthe bodythe transcript, → sent and ← received, to the millisecondthe messages; a stream as a list
checks readresponse.statusCode, response.json("a.b"), response.header("X")response.received, response.json("[0].type")response.grpc.status, response.json("name")

gRPC without generated code. Messages are written as JSON. The descriptors come from # @grpc-descriptor api.protoset (what protoc --descriptor_set_out or buf build -o writes), else from the server's reflection service, else fields go by number ({"1": 7}), as protoc --decode_raw shows them. # @grpc-plaintext true speaks HTTP/2 without TLS. Unary calls and server streams are sent; a status other than OK ends the request like an HTTP error (404 NOT_FOUND · user 7 not found).

Environments. {{name}} works in the URL, headers, body, @ws steps, gRPC metadata and target. Values come from @name = value lines, captures of earlier responses, rest-client.env.json or resterm.env.json (the $shared block, then the environment chosen with # @env, which e on the board changes), env:NAME, and the project's .env. Credentials in headers and metadata are hidden in the log.

For agents. stickypane api api lists the requests (#2 WS Live updates), stickypane api api "live" --env dev sends one and --all every one, printing each log; the MCP tool api does the same and returns the logs of a failed run too. Each request's last result stays in api.log under its title, a line such as [101 Switching Protocols · 1.1s · Live updates · 2↑ 2↓ · 1/1 ✔] at its end, so stickypane watch --note api.log hears when one is sent.

Languages

The screen speaks English and Korean: stickypane language ko, or leave it on auto and set LANG. Key names and the agent's guide stay English; see docs/translating.md to add a language.

The index

A board with many notes stays easy to read: i or / opens the index, every note of every tab in a line, the way a wiki's index page lists its pages. Each line has the note's shape, title, what it counts (2/5, 7 cards), a gist and when it changed; open notes are marked ●. Type to filter, choose with the arrows, and enter goes to the note, switching tab and opening it.

The index: every note of every tab in a line, with what it counts and a gist

The gist is the note's own summary: from its front matter when it has one, so a note can say in a line what it is; otherwise it is what to look at first: the next open item of a checklist, the latest line of a log, the first line of anything else.

stickypane index prints the same index as Markdown (--json for programs), and the MCP tool index gives it to agents, which read it instead of every note.

Settings

Nothing has to be set: every setting has a default and the board works as it is. S on the board opens the settings panel: j k pick a setting, h l change it, and it applies at once. stickypane config does the same from the command line.

The settings panel: theme, language, time format, editor and when a card counts as stalled, each with a default

SettingDefaultChanges
themeautothe colors; auto follows the terminal's background
languageautothe screen's words
timeautohow days and times are written; auto follows the locale's country
editorautowhat E opens; auto is $VISUAL, $EDITOR, then the first editor found
stale30mwhen a kanban card that has not moved is flagged; off never
stickypane config                # every setting, its value, and which are defaults
stickypane config stale 1h

A setting at its default is not written; the others go to the root sticky.json.

Themes

Everything on the screen is drawn from a dozen color roles (text, muted, accent, selection, good, warn, bad, info, and the six note colors), so a theme is one palette and the whole board follows it. T tries the next theme, stickypane theme lists them and stickypane theme nord chooses one; the choice is kept in sticky.json as "theme". With no choice (auto) the board asks the terminal whether it is dark and picks stickypane-dark or stickypane-light.

ThemeFor aPalette from
stickypane-dark, stickypane-lightdark, lightstickypane
catppuccin-mocha, catppuccin-lattedark, lightcatppuccin/nvim (MIT)
tokyonight-night, tokyonight-daydark, lightfolke/tokyonight.nvim (Apache-2.0)
gruvbox-dark, gruvbox-lightdark, lightellisonleao/gruvbox.nvim (MIT)
norddarkgbprod/nord.nvim (WTFPL)
draculadarkMofiqul/dracula.nvim (MIT)

The palettes are those themes' own values, used the way the themes use them: blue for focus, mauve for headings, the comment color for what is muted. A theme never paints the background; the terminal's stays, so pick a theme made for the background you have.

How the colors are used follows what terminal tools agree on: focus is the border color, not a different shape alone; the title sits in the top border in the note's color; the selected line has a background tint; key hints are the accent; everything secondary is muted; and faint is never used, since it is unreadable on a light background.

Deleting and moving

Nothing you do on the board erases a file.

  • D asks, then moves the note to .sticky/.trash/. x moves it to .sticky/archive/ without asking. u takes back whichever you did last.
  • m lists the folders: pick one and the note becomes a page of that book, pick the top level to take a page out of its book, or name a new folder.
  • { and } swap the note with its neighbor; the order is kept in sticky.json.
  • R renames what you see, not the file: a Markdown note's title, or for a book, a log or a script the title in sticky.json.

From the command line:

stickypane mv plan deploy/       # into the tab deploy
stickypane mv deploy/plan .      # back to the first tab
stickypane mv plan roadmap       # a new name; it keeps its place on the screen
stickypane rm roadmap            # to .sticky/.trash/
stickypane restore roadmap       # and back
stickypane archive roadmap       # to .sticky/archive/
stickypane link README.md        # show a project file without copying it

None of these writes over another note or reaches outside .sticky/.

Editing a note

e opens the focused note's file in a small editor that works like vi, so you can fix a note without leaving the board. E hands the file to your own editor instead: $VISUAL or $EDITOR, any of them. Terminal editors (vim, nvim, helix, nano, emacs -nw, micro) take the terminal until you quit; GUI editors (VS Code, Cursor, Zed, Sublime, JetBrains, Kate, TextMate) are told to wait until you close the file, so you need not add --wait. With neither variable set, the first of nvim, vim, vi and nano found is used.

KeysDo
i a I A o Oinsert text; esc goes back
h j k l w b e 0 ^ $ gg Gmove
ctrl+f ctrl+b, ctrl+d ctrl+ua page, half a page
x dd dw D cc cw C r Jdelete, change, replace, join
yy p P, u ctrl+rcopy and paste, undo and redo
/text n N, :12search, go to a line
:w :q :wq ZZ :q!save, quit, both, quit without saving

The border shows the cursor's line and column and which page of the file it is on. If the file changes on disk while it is open, usually because your agent wrote to it, a buffer you have not touched follows the file. One with your changes is kept, you are told, and :w does not overwrite the file: :w! does, and :e! loads it again. Counts (3dd) and visual mode are not there.

Mouse

Do thisAnd
click a title in the bara closed note opens, an open one takes the focus, the focused one closes
click a noteit takes the focus
click an item, option, button or cardit is ticked, chosen, pressed or selected
double-click a notezoom; on a script, run it after a yes
click Yes or No on a questionanswer it; a click anywhere else is no
wheelscroll the note under the pointer

In tmux the mouse reaches stickypane only with set -g mouse on.

Command line and MCP

Editing the files is all an agent needs. For scripts, and for agents that would rather call a tool, the same notes are reachable two more ways.

The shortest path: put something in front of the user.

stickypane show plan                 # a note, by name
stickypane show README.md            # any file of the project: linked and shown
stickypane show logs/app.log         # a log file: linked, shown and followed live
stickypane show docs/                # a folder: linked as a tab of its own
stickypane hide plan

Everything else:

stickypane list --json                       # every note: name, title, type, open, size
stickypane cat plan                          # print a note's file
echo "- [ ] build" | stickypane write plan --type checklist --title "Release" --open
stickypane answers deploy                    # what the user answered in a form
stickypane wait deploy --timeout 10m         # the same, once a button is pressed

wait exits with code 3 when its time runs out. Both take --json.

Small changes without reading the note. An agent that ticks an item by rewriting the whole checklist has to read it first, and may write it back wrong. These commands change one thing and print where the note stands:

stickypane todo plan add "write tests"       # plan.md: 0/1
stickypane todo plan check tests             # plan.md: 1/1
stickypane card work add "login API" --to Doing
stickypane card work move login --to Done    # work.md: 2 cards
stickypane chart tokens set input 1,200      # tokens.md: input = 1,200
stickypane chart tokens add input 800        # tokens.md: input = 2,000
stickypane log worklog --time "tests passed" # worklog.md: 12 lines
stickypane say chat "41/41 pass" --as claude # chat.md: 3 messages
stickypane set plan open=true size=half      # to sticky.json
stickypane set plan title="The plan"         # to the note's front matter

A note that does not exist yet is made, open on the screen, so an agent never has to know the file format for these. An item, a card or a column is named by its text, by a part of it that nothing else has, or by its position (#2). When the name fits nothing, or more than one thing, the error lists what there is.

stickypane mcp serves the notes over the Model Context Protocol on standard input and output. It is layered the way the plugin is, so an agent finds its way without reading everything:

LayerOver MCPAt the command line
the way inthe guide toolstickypane guide
a skill for a job: tracking progress, asking the user, talking in a chat, connecting notesguide with the skill as topic, or the prompt of that namestickypane guide tracking-progress
one thing changedindex, read_note, write_note, todo, card, chart, log, say, set_keys, arrange_note (show, hide, move, remove, restore)the commands above
waiting for the userread_answers, wait_eventstickypane wait, stickypane watch

The skills are the plugin's own files, carried in the program, so a client without the plugin reads the same ones.

claude mcp add stickypane -- stickypane mcp

write and write_note replace the whole note, exactly as overwriting the file would. Read a note before rewriting it. A name may point into a book (docs/intro) or at a log (build.log).

How edits are written

The board never overwrites a file with what it last saw. An edit is an intent ("move this card to that column") applied to the file as it is at that moment, and the file is then replaced in one step. If your agent changed that card in the meantime, nothing is written and the board tells you. A note that is a symlink is edited where it really lives.

Replacing the file has two limits:

  • A program that keeps a note open and keeps appending to it (some-command >> .sticky/log.md) goes on writing to the old file after you edit that note from the board. Writing a line at a time, the way agents do, is fine.
  • A write that lands in the instant between the board reading a file and replacing it is lost.

Adding a shape

A shape is one package under internal/widget/ that implements the widget.Widget interface, plus one line in internal/kinds/kinds.go.

Contributing

Design feedback is worth more than code right now: what you expected the board to show, and what it did. CONTRIBUTING.md says how an idea becomes an issue, a proposal, a pull request and a release, how to take an issue, and how to set up and test. Good places to start: issues labelled good first issue or accepted. Coding agents read AGENTS.md first.

Support

Questions go to Discussions, bugs and ideas to the issue tracker, and security problems privately, as SECURITY.md says. More in SUPPORT.md.

Alternatives

If you want a kanban that lives in Markdown files and nothing else, kanban-md and Backlog.md do that well. If you want to watch commands and metrics in a configured dashboard, sampler and wtf are for that. stickypane is for the space between you and an agent: files the agent writes, a screen you read, and answers that go back.

License

MIT, © 2026 LeeSwallow. The libraries stickypane is built from are all under MIT or BSD licenses; they and the color palettes of the themes are listed in THIRD_PARTY_NOTICES.md, and every release archive carries their license texts.