# 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](https://github.com/LeeSwallow/stickypane/actions/workflows/ci.yml/badge.svg)](https://github.com/LeeSwallow/stickypane/actions/workflows/ci.yml) [![Release](https://img.shields.io/github/v/release/LeeSwallow/stickypane?include_prereleases)](https://github.com/LeeSwallow/stickypane/releases) [![Go Report Card](https://goreportcard.com/badge/github.com/LeeSwallow/stickypane)](https://goreportcard.com/report/github.com/LeeSwallow/stickypane) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) English · [한국어](README.ko.md) [Install](#install) · [Quick start](#quick-start) · [Keys](#keys) · [Command line and MCP](#command-line-and-mcp) · [Contributing](#contributing) · [Roadmap](ROADMAP.md) · [Discussions](https://github.com/LeeSwallow/stickypane/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](demo/live.gif) 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](demo/screenshots/1-board.png) **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](https://github.com/LeeSwallow/stickypane/issues/new?template=design_feedback.yml) > or an [Idea](https://github.com/LeeSwallow/stickypane/discussions). The > open questions are in [ROADMAP.md](ROADMAP.md); the principles in > [VISION.md](VISION.md). ## Contents - [Install](#install): Homebrew, a release archive, `go install`, from source - [Quick start](#quick-start): the board next to your agent in four steps - [The folder is the board](#the-folder-is-the-board) and [the shapes of a note](#shapes-of-a-markdown-note) - [Keys](#keys), [mouse](#mouse), [settings](#settings), [themes](#themes) - [Notes that link](#notes-that-link), [notes that react](#notes-that-react), [the index](#the-index) - [Command line and MCP](#command-line-and-mcp) - [Contributing](#contributing), [support](#support), [license](#license) ## 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 ```sh brew install --cask LeeSwallow/tap/stickypane ``` ### A release archive Every [release](https://github.com/LeeSwallow/stickypane/releases) 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: ```sh 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: ```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 ` or, in PowerShell, `Get-FileHash `. ### With Go ```sh 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 ```sh git clone https://github.com/LeeSwallow/stickypane cd stickypane go build -o stickypane ./cmd/stickypane ``` ### Check that it works ```sh 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: | Terminal | Command | | --- | --- | | tmux | `tmux split-window -h stickypane`, or a popup: `tmux display-popup -E stickypane` | | WezTerm | `wezterm cli split-pane --right -- stickypane` | | Zellij | `zellij run --direction right -- stickypane` | | Windows Terminal | `wt -w 0 split-pane -V stickypane` | | anything else | a 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: | Way | How | For | | --- | --- | --- | | the plugin | `stickypane setup` | Claude Code and Codex: five skills, slash commands, a session hook | | a guide in its instructions | done by step 2: a short guide in `CLAUDE.md` or `AGENTS.md` | any agent that reads those files | | MCP | `stickypane setup --mcp`, or `claude mcp add stickypane -- stickypane mcp` | any MCP client | For another MCP client, the server is the command `stickypane mcp` on standard input and output: ```json { "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: ```sh 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: ```markdown check env before deploy ``` What a file is depends on its name: | File | Is | | ----------------------- | ----------------------------------------------------- | | `*.md` | a note; its front matter may give it a shape (below) | | `*.log` `*.txt` `*.out` | a log: shown as it is and followed as it grows | | `*.sh` `*.ps1` | a script: shown with a Run button, run with sh or PowerShell | | a folder | a tab: a screen of its own, with the files in it as notes | | a folder in a tab | a book: one note whose pages are the files in it | | anything else | not 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. | Key | Values | | ------- | ------------------------------------------------------ | | `type` | `note` (default), `board`, `checklist`, `log`, `chart`, `form` | | `title` | shown in the title bar and the note's border | | `view` | for 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: ```json { "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 `notes` | Values | Key on the board | | -------------- | --------------------------------------------- | ---------------- | | `open` | `true` shows the note, `false` folds it away | `o`, `enter` | | `size` | `page` (whole width), `half`, `card` | `+` `-` | | `rows` | the height in lines the note asks for | | | `color` | `yellow` `pink` `blue` `green` `purple` `orange` | `c` | | `pin` | `true` keeps the note first | `p` | | `title` | a name for a book, a log or a script | `R` | 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](demo/screenshots/2-release.png) **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. ```markdown --- 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. ```markdown --- 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. ````markdown ```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`. ```markdown --- 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. ```markdown --- 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 │ ┗━━━━━━━━┛ ╰────────╯ ``` | Line | Is | | --------------------- | ---------------------------------------------- | | `- ( ) text` | an option; choosing one clears the others | | `- [ ] text` | an option; choose as many as you like | | indented line below | the option's description | | `> text` | a 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: