shelbi changelog

View as markdown
shelbi changelog add    --title <TITLE> [--date <DATE>] [--author <NAME>]
                        [--issue <ID>]... [--pr <N|URL>]... [--link <URL>]...
                        [-d <DESC> | stdin] [--format text|markdown|json]
shelbi changelog list   [--limit <N>] [--since <DATE>] [--format text|markdown|json]
shelbi changelog search <QUERY> [--limit <N>] [--format text|markdown|json]
shelbi changelog show   <ENTRY-ID> [--format text|markdown|json]

Every project keeps a changelog: a blog-style record of what has shipped, so you and your agents can see what recently landed. The orchestrator adds an entry each time it merges work (see Changelog for the rule it follows), and you can add your own. The Changelog page in the sidebar of the TUI and the desktop app shows it, newest first.

Where entries live

Each entry is one markdown file with YAML frontmatter, in the project's Shelbi state directory:

~/.shelbi/projects/<project>/changelog/2026-10-28-213005-changelog-lands-in-the-sidebar.md

Files are named <date>-<time>-<slug>.md from the entry's date in UTC, so they sort by date. The slug is the title in lowercase words joined by -, cut at a word boundary to at most 48 characters. The file name without .md is the entry's id. Entries stay out of your repository whatever the project's config mode.

---
title: Changelog lands in the sidebar
date: 2026-10-28T14:30:05-07:00
author: orchestrator
issues:
- changelog
prs:
- 1953
links:
- https://shelbi.dev/docs/cli/changelog
---
The sidebar's Activity row becomes Changelog.

The frontmatter holds the title, the date (with time and offset), the author (orchestrator, user, or an agent's name) and the links given as flags. The body is the description. You can edit an entry by hand; a bare date: like 2026-10-28 still loads.

Two writers adding at the same moment never clobber each other: each add claims a new file name, adding -2, -3 and so on when the name is taken.

An entry can link Shelbi issues, pull requests, and any other URL. Give them as flags, or write them in the description:

FlagIn the descriptionLinks to
--issue <id>issue:<id>A Shelbi issue by id, such as issue:tui-about. The TUI and the desktop app open it inside Shelbi.
--pr <n|url>#<n>A pull request: a number in the project's GitHub repository (#1942), or a PR URL.
--link <url>any http:// or https:// URLAny other page. A GitHub pull request URL counts as a PR.

Code spans and fenced code blocks in the description are not scanned, and a link must start a word (foo#12 is not a PR). A PR number resolves to a URL against the project's GitHub issue tracker or, failing that, its repository's origin remote when it points at github.com. With neither, the PR keeps its number and has no URL.

The template

New entries are written from the project's changelog template:

~/.shelbi/projects/<project>/changelog-template.md

For an in-repo project it sits in <repo>/.shelbi/ beside the other project config. Every new project gets the default, and Shelbi adds it on start to any project that predates it, never overwriting one that exists. Open it from Settings, or from the command palette's Edit Changelog Template.

add fills these placeholders:

PlaceholderBecomes
{{title}}The entry's title.
{{date}}Its date, YYYY-MM-DD.
{{time}}Its time of day, HH:MM.
{{author}}Who wrote it.
{{description}}The description from -d or stdin.
{{links}}The flag links, one per line as a markdown list.

The default template is the description alone. The links live in the entry's frontmatter, and the Changelog page in the TUI and the desktop app shows them under every entry, so the default leaves {{links}} out. Add it back to your template if you want the list in the description too. A comment at the top of the template (<!-- ... -->) explains the placeholders and is left out of every entry. Blank lines left by an empty placeholder are tidied away.

Older Shelbi releases shipped a template that ended with {{links}}. A project whose template is still exactly that default moves to the current one on start. A template you have edited is never changed. Entries written with the old template keep their list until you run strip-links.

add

Adds an entry and prints its id and file.

OptionMeaning
--title <TITLE>The entry's title, one line. Required.
--date <DATE>When it shipped. Defaults to now. Takes YYYY-MM-DD (keeping the current time of day), a local date and time (2026-10-28 14:30), or RFC 3339 (2026-10-28T14:30:00-07:00).
--author <NAME>orchestrator, user, or an agent's name. Defaults to $SHELBI_AGENT inside an agent's session, else user.
--issue <ID>Link a Shelbi issue. Repeatable.
--pr <N|URL>Link a pull request (1942, #1942, or a URL). Repeatable.
--link <URL>Link any other URL. Repeatable.
-d, --description <DESC>The description, in markdown. Without it, the description is read from stdin when stdin is not a terminal.
--format <FORMAT>How to print the new entry. Default text.

Each link is stored once: a --link that is the URL of a --pr is dropped, as is a flag given twice.

add refuses an --issue that isn't a valid issue id, a --pr that isn't a number or URL, a --link that isn't an http(s) URL, an unreadable --date, and a title or author that is empty or longer than one line. It writes nothing when one is refused.

list

Lists entries, newest first.

OptionMeaning
--limit <N>Show at most N entries.
--since <DATE>Only entries on or after this date. A bare date means the start of that day.
--format <FORMAT>Default text.

Lists the entries whose title, description, or link targets contain every word of the query, ignoring case, newest first. Link targets include issue ids (board-perf), PR numbers (#1953) and URLs (pull/1953).

OptionMeaning
--limit <N>Show at most N entries.
--format <FORMAT>Default text.

show

Prints one entry. <ENTRY-ID> is the id list shows, or any prefix of it that names one entry (2026-10-28-2130). The default format is markdown.

Removes the list of links at the end of an entry's description when every item in it is a link the entry already has in its frontmatter (issues:, prs: and links:). That is the list the old default template wrote. A list with anything else in it, or one that sits right under a line of prose, is left alone, and the frontmatter is never changed. Prints each entry it changes.

OptionMeaning
--dry-runList the entries that would change without writing them.

Formats

text is a compact list: the date and title, then the id, the author and the links. With nothing to show, it prints No changelog entries yet. (or No changelog entries match `<query>`. for search). An entry file that can't be read is skipped with a warning on stderr.

2026-10-28 14:30  Changelog lands in the sidebar
                  2026-10-28-213005-changelog-lands-in-the-sidebar  orchestrator  issue:changelog  #1953

markdown reads like the blog, full entries ready to paste or for an agent to read: a heading, a line with the date, author and id, the description, and any links the description doesn't already show.

json is for agents and scripts. list and search print an object with the entries; show and add print one entry:

{
  "schema_version": 1,
  "project": "shelbi",
  "query": "sidebar",
  "entries": [
    {
      "id": "2026-10-28-213005-changelog-lands-in-the-sidebar",
      "title": "Changelog lands in the sidebar",
      "date": "2026-10-28T14:30:05-07:00",
      "author": "orchestrator",
      "description": "The sidebar's Activity row becomes Changelog.",
      "links": [
        { "kind": "issue", "id": "changelog" },
        { "kind": "pr", "number": 1953, "url": "https://github.com/jlong/shelbi/pull/1953" },
        { "kind": "url", "url": "https://shelbi.dev/docs/cli/changelog" }
      ],
      "path": "/Users/you/.shelbi/projects/shelbi/changelog/2026-10-28-213005-changelog-lands-in-the-sidebar.md"
    }
  ]
}
  • schema_version changes only when the shape breaks.
  • query appears on search only. show and add print entry in place of entries.
  • date is RFC 3339 with the offset the entry was written in.
  • description is the markdown body.
  • links holds every link, the flags' first and then the description's, without duplicates. Each has a kind: issue with an id; pr with a number and a url, either of which can be null (a #12 with no known repository has no URL, a non-GitHub PR URL has no number); url with a url.

Examples

Record a merge, linking its issue and PR:

shelbi changelog add --title "Faster board" --issue dt-board-perf --pr 1900 \
  -d "Board columns are a virtualized list, so a long column scrolls smoothly."

Take the description from a file on stdin:

shelbi changelog add --title "Zen pill redesign" --pr 1951 < notes.md

What landed this week, as an agent would read it:

shelbi changelog list --since 2026-10-26 --format markdown

Everything about the board, as JSON:

shelbi changelog search board --format json