Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
4586437
docs: design custom commands bound to keys per screen
JacobAtchley Oct 6, 2026
6a2d5c3
docs: plan custom commands
JacobAtchley Oct 6, 2026
7b97e74
docs: deliver command results as status messages; refuse list keys
JacobAtchley Oct 6, 2026
e2d43f3
docs: record approved spec changes for custom commands
JacobAtchley Oct 6, 2026
3ec4bc4
feat: read custom commands from the config
JacobAtchley Oct 6, 2026
b66e2c1
feat: run a shell command detached and report how it went
JacobAtchley Oct 6, 2026
dc7a144
fix: cmd.exe argument escaping in custom_windows.go
JacobAtchley Oct 6, 2026
12a08c2
feat: name what each screen shows for custom commands
JacobAtchley Oct 6, 2026
5e1f5c0
fix: show a status message, and its error colour, on every detail pane
JacobAtchley Oct 6, 2026
30b07c2
feat: run a custom command from its key, the help panel or the palette
JacobAtchley Oct 6, 2026
3e1fa54
fix: refuse viewport scroll keys for a custom command, and test help …
JacobAtchley Oct 6, 2026
e6b1548
feat: bind custom commands from the config
JacobAtchley Oct 6, 2026
d03ec48
fix: keep a custom command's stderr in a file, not a pipe
JacobAtchley Oct 6, 2026
764c8cf
fix: refuse a command key that every screen already binds
JacobAtchley Oct 6, 2026
66dcb40
fix: give custom commands their own palette id namespace
JacobAtchley Oct 6, 2026
217e6ed
fix: run a Windows custom command with cmd /S /C
JacobAtchley Oct 6, 2026
a978ddd
docs: correct the custom commands section
JacobAtchley Oct 6, 2026
ba2fb28
docs: spell out which ctrl keys custom commands cannot use
JacobAtchley Oct 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Everything boardwalk needs to know lives in one file at
| `project` | the team project within it |
| `reviewGroups` | optional — extra teams and security groups to treat as yours, named as Azure DevOps displays them |
| `paletteKey` | optional — the key that opens the [command palette](#command-palette), spelled `ctrl+k`, `:`, `f1`; defaults to `ctrl+p` |
| `commands` | optional — your own [custom commands](#custom-commands), each a key on a kind of screen |

Azure DevOps scopes a group's display name to wherever it lives —
`[TEAM FOUNDATION]\platform-devs` for a collection group, `[MyProject]\Team
Expand Down Expand Up @@ -158,6 +159,62 @@ menu's `↑`/`↓`/`j`/`k`), at startup. A key a view also binds is taken by the
palette on that view, so pick one that is free. Like every other key, it is left to a
prompt while one is open.

### Custom commands

Bind a key on a kind of screen to a shell command of your own. boardwalk runs
it with `sh -c` (`cmd /C` on Windows), detached — it keeps the screen and reports on the status line
whether the command ran, failed (with the last line it wrote to stderr), or is
still going after two seconds and has been left to run.

```json
"commands": [
{
"on": "pullRequest",
"key": "ctrl+r",
"name": "review with claude",
"run": "~/bin/pr-review.sh"
}
]
```

`on` is the kind of screen: `pullRequest` (the list, the pull request, its
diff and files), `workItem` (the list and the item) or `build` (the list and
the logs). The command appears in the `?` panel and the palette there.

What the screen shows reaches the command as environment variables, never
pasted into the command, so a title full of shell syntax stays a title:

| variable | on | |
|---|---|---|
| `BOARDWALK_KIND` | all | `pullRequest`, `workItem` or `build` |
| `BOARDWALK_ORG`, `BOARDWALK_PROJECT` | all | from the config |
| `BOARDWALK_ID`, `BOARDWALK_TITLE`, `BOARDWALK_URL` | all | the selected thing |
| `BOARDWALK_REPO`, `BOARDWALK_SOURCE_BRANCH`, `BOARDWALK_TARGET_BRANCH`, `BOARDWALK_AUTHOR`, `BOARDWALK_IS_DRAFT` | pullRequest | branches without `refs/heads/` |
| `BOARDWALK_TYPE`, `BOARDWALK_STATE` | workItem | |
| `BOARDWALK_NUMBER`, `BOARDWALK_PIPELINE`, `BOARDWALK_BRANCH`, `BOARDWALK_RESULT` | build | |

The command also inherits boardwalk's own environment, so a terminal
multiplexer's variables pass straight through. For example, a script that
opens a [herdr](https://herdr.dev) tab beside boardwalk and starts a Claude
Code review there (it needs `jq`; adjust the last line to your own review
skill):

```sh
#!/bin/sh
# ~/bin/pr-review.sh
pane=$(herdr tab create --label "review !$BOARDWALK_ID" --cwd "$PWD" --focus |
jq -r .result.root_pane.pane_id)
herdr pane run "$pane" "claude '/review-pr $BOARDWALK_ID'"
```

A key is one key, spelled as for `paletteKey`. boardwalk refuses at startup a
key it needs itself, one lists or panes move with (`j`, `k`, `l`, `h`, `b`,
`u`, `f`, `d`, `g`, `G`, `/`, space, `^d`, `^u`, the arrows, `home`, `end`,
page keys), one every screen uses (`r`, `y`, `^y`, `s`, `^s`, `o`, `^o`), or
the palette key. `^r` is free. A key a single screen already
uses keeps doing what it did there, and the command is left out of that
screen's help.

### Every list view

Work items, pull requests and builds share these. The log pane is a pager
Expand Down
Loading
Loading