The pb command
pb reads and edits a Project Board file (.pb) from a shell. It exists for three users:
- Claude Code and other agents that work in a terminal. This is how Claude reads issues, comments on them, closes them and plans new ones while you keep working in the editor.
- Scripts and CI: validate a board, build a report, import an export of another tracker.
- People who prefer a terminal.
1. Getting the command
The extension installs it for you. Every integrated terminal of VS Code has pb on its PATH, and so has
every shell Claude Code starts from one. Outside VS Code, call the same program directly:
node <extension folder>/out/cli.js <command> …
<extension folder> is the folder the extension is installed in, one of the folders in
~/.vscode/extensions/. The Assistant section of the board’s settings page shows the exact command of your
installation.
2. Working together with the editor
When a board is open in VS Code, pb does not touch the file. It talks to the editor that has the board
open, and the editor makes the change:
- The change shows up on the page at once, signed with the name of whoever ran the command.
- It goes through the same queue as your own edits, one whole action at a time. You can type a description while Claude comments on the same issue; nothing is lost and nothing has to be merged afterwards.
- It is one more step in the editor’s undo history. Ctrl+Z takes it back.
- If the board had no unsaved changes, it is saved again after the edit, so the file on disk (and git) keeps up. If you had unsaved work, the edit joins it and reaches the disk when you save.
- Reading (
pb issues,pb issue 12) sees what the editor holds, including what you have not saved.
When no editor has the board open, pb edits the file itself. A lock file next to the board
(.<name>.pb.lock) lets several commands run at the same time without overwriting each other, and the
file is replaced in one step, so nobody ever reads half a board.
pb where tells which of the two is the case. --no-live forces the file even when an editor has the
board open; the editor then sees a file that changed on disk, as with any other outside edit.
The editor and the command find each other through a folder only your user can read
($XDG_RUNTIME_DIR/project-board, or project-board-<uid> in the temporary folder; PB_BRIDGE_DIR
overrides it). Each running editor leaves a small file there with the address of a local socket and a
random token. Nothing listens on the network.
3. Which board, and who signs
The board is, in this order: the .pb file named right after the command, --file <path>, the PB_FILE
environment variable, or the only .pb file in the current folder.
Edits are signed, in this order: --as <name>, the PB_ACTOR environment variable, Claude when the
command runs inside Claude Code (which sets CLAUDECODE in its shells), or your git name.
4. Commands
Read
pb show project, milestones with progress, workflow columns, labels
pb issues open issues; --all, --state closed, --milestone "0.4", --label bug,
--assignee ada, --kind requirement, --column "In review", --search text, --limit 20
pb issue 12 one issue in full, with links and every comment
pb milestones
pb labels
pb log --limit 30 who did what, newest first
Change
pb add-issue "Title" [--body text | --body-file path|-] [--milestone title] [--label name]…
[--assignee name] [--kind task|requirement|heading] [--column name] [--parent number]
[--start date] [--due date]
pb edit 12 [--title text] [--body text | --body-file path|-] [--milestone title|none]
[--label name]… [--add-label name]… [--remove-label name]… [--assignee name] [--kind kind]
[--start date|none] [--due date|none] planned dates for the Gantt chart
pb comment 12 "Text" or --body text, or --body-file path|-
pb close 12 13 [--comment text]
pb reopen 12 [--comment text]
pb move 12 "In progress" a column name, or todo / doing / done; [--milestone title|none]
pb link 12 depends 7 satisfies, derives, verifies, refines, depends,
conflicts, relates
pb add-milestone "Version 1.0" [--body text] [--start date] [--due date]
pb edit-milestone "1.0" [--title text] [--body text] [--start date|none] [--due date|none]
pb close-milestone "1.0"
pb reopen-milestone "1.0"
pb add-label "needs design" [--color "#8b5cf6"] [--body text]
pb apply --actions file|- many changes in one step (see below)
pb apply --events file|- raw events of the file format, without "seq"
File
pb init [name.pb] [--name "Project"] a new, empty board
pb validate is this a board this version reads? exit code 1 when not
pb upgrade rewrite a board of an older format in the current one
pb consolidate fold the event list into the tree
pb where which board, who signs, is an editor involved
In and out
pb report --out folder [--format html-book] [--title text] [--milestone title]
[--no-closed] [--no-comments] [--no-activity] [--no-requirements]
pb export --format csv [--out file or folder] [--no-closed] [--no-comments]
pb import --from file|- [--format id] [--dry-run] [--no-closed] [--no-comments]
pb formats the report, export and import formats
pb skill [--install] [--dir folder] the Claude Code skill for boards
An issue is named by its number (12 or #12), a milestone by its title or a unique part of it ("0.4"
finds “Version 0.4 - Presentation Changes”), a label by its name. --body-file - reads standard input,
which is the safe way to pass several lines of Markdown:
pb comment 12 --body-file - <<'EOF'
Fixed in `abc1234`.
- the parser accepts empty files
- added a test
EOF
Every command takes --json and then prints one JSON document: the data for reading commands, and
{ ok, file, live, saved, dirty, outcomes: [{ ok, error?, result? }] } for changing ones.
Exit codes: 0 done, 1 the board refused something or the file is not usable, 2 the command was not
understood. With several steps in one command (pb close 7 8 9), a refused step is reported and the
others still run.
5. Many changes at once
pb apply --actions takes a JSON list of the same actions the editor itself uses:
[
{ "action": "issue.create", "ref": "parser",
"payload": { "title": "Write the parser", "labels": ["feature"], "milestone": "0.4" } },
{ "action": "issue.create", "payload": { "title": "Test the parser", "parent": "$parser" } },
{ "action": "comment.add", "payload": { "issue": 12, "body": "Split into two issues." } },
{ "action": "issue.close", "payload": { "issue": 12 } }
]
"ref" names what an action creates and "$name" uses it later in the list. The actions most useful
here are issue.create, issue.update (with addLabels and removeLabels), issue.close,
issue.reopen, issue.move, comment.add, link.add, milestone.create, milestone.update,
milestone.close and label.create; ai-agents.md describes them.
6. Claude Code
Two things make Claude good at this:
- The skill. “Install the Claude skill” on the settings page (or
pb skill --installin the project folder) writes.claude/skills/project-board/SKILL.md. It tells Claude that boards exist, how to callpb, and how to behave on a board: read the comments first, comment on what it did, close only what is done, open an issue instead of fixing unrelated things silently. - The live editing described above. Run Claude Code in the integrated terminal, keep the board open beside it, and watch the issues move.
Ask Claude things like “work on issue 12 and record what you did on the board” or “split the 0.5 milestone into issues”.
The page has its own assistant too (the sparkle button and “Ask the assistant” on an issue). That one
answers inside the editor and proposes changes you accept or discard; pb is for Claude working on its
own in the terminal.
7. Limits
- The command edits a board whether or not a subscription is active on this machine. The check for a subscription lives in the editor; an edit sent to an open editor is refused there like any other.
pbneeds Node.js 18 or newer when it is called outside VS Code. Inside VS Code it runs on the editor’s own runtime.- Attachments (images, sketches, files) cannot be added from the command line yet.