From 1e7e4f6b8f4dee0c303393891d9afdd50a0fe89a Mon Sep 17 00:00:00 2001 From: matthewdias Date: Mon, 5 Oct 2026 08:39:45 -0500 Subject: [PATCH 1/2] Install snippets: use @* with --tag-prefix, not @semver:* bb 0.45 rejects an explicit "semver:" spec alongside --tag-prefix ("Use --tag-prefix or an explicit "semver:" spec, not both"), so every README install line failed. The CLI rewrites --tag-prefix P with @R into @semver:P:R, so a bare @* resolves to the newest stable P vX.Y.Z tag and still never goes stale. Co-Authored-By: Claude Opus 5.5 --- CONTRIBUTING.md | 12 +++++++----- README.md | 12 ++++++------ plugins/follow-up/README.md | 7 ++++--- plugins/thread-badges/README.md | 7 ++++--- plugins/top-tabs/README.md | 2 +- plugins/workflow-stages/README.md | 7 ++++--- 6 files changed, 26 insertions(+), 21 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 782d5d0..41cdfa8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -100,8 +100,10 @@ Bump the version in the plugin's `package.json` in the same commit, and update its `PLUGIN_OVERVIEW.md` whenever `bb.description` or a surface changes — the store shows the two together and they must not disagree. -A README's install snippet needs no attention: it names `@semver:*`, which -resolves to the newest tag under that plugin's prefix. Do not put a version in -one. Every snippet that named a range went stale, and a caret range on a `0.x` -version goes stale on the very next release — `^0.1.0` cannot reach `0.2.0` at -all, so readers were installing a plugin two minor versions behind. +A README's install snippet needs no attention: it names the bare range `@*`, +which `--tag-prefix` resolves to the newest tag under that plugin's prefix. +Don't spell it `@semver:*` — bb rejects an explicit `semver:` spec alongside +`--tag-prefix`. Do not put a version in one either. Every snippet that named a +range went stale, and a caret range on a `0.x` version goes stale on the very +next release — `^0.1.0` cannot reach `0.2.0` at all, so readers were installing +a plugin two minor versions behind. diff --git a/README.md b/README.md index fd0a483..29ea35c 100644 --- a/README.md +++ b/README.md @@ -20,21 +20,21 @@ is installed, and Workflow Stages needs One at a time, by subdirectory: ```sh -bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@semver:*" \ +bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@*" \ --subdirectory plugins/follow-up --tag-prefix follow-up/ -bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@semver:*" \ +bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@*" \ --subdirectory plugins/thread-badges --tag-prefix thread-badges/ -bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@semver:*" \ +bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@*" \ --subdirectory plugins/top-tabs --tag-prefix top-tabs/ -bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@semver:*" \ +bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@*" \ --subdirectory plugins/workflow-stages --tag-prefix workflow-stages/ ``` -Each plugin is released under its own tag prefix, so `semver:*` resolves to the -newest release of that plugin alone and these lines never go stale. A caret +Each plugin is released under its own tag prefix, so the range `*` resolves to +the newest release of that plugin alone and these lines never go stale. A caret range would: on a `0.x` version `^0.1.0` cannot reach `0.2.0` at all, which is how this page came to offer a plugin two minor versions behind. `--plugin ` works instead of `--subdirectory` — the repository carries a diff --git a/plugins/follow-up/README.md b/plugins/follow-up/README.md index 2d5a219..b4f7a5b 100644 --- a/plugins/follow-up/README.md +++ b/plugins/follow-up/README.md @@ -12,12 +12,13 @@ nothing to run — no model to choose, no cooldown, no polling, no inflight lock ## Install ```sh -bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@semver:*" \ +bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@*" \ --subdirectory plugins/follow-up --tag-prefix follow-up/ ``` -`semver:*` resolves to the newest `follow-up/vX.Y.Z` tag, so this line stays -correct as the plugin releases and `bb plugin update` follows it. +With `--tag-prefix`, the range `*` resolves to the newest `follow-up/vX.Y.Z` +tag, so this line stays correct as the plugin releases and `bb plugin update` +follows it. ## What it does diff --git a/plugins/thread-badges/README.md b/plugins/thread-badges/README.md index d43967c..4cf3da0 100644 --- a/plugins/thread-badges/README.md +++ b/plugins/thread-badges/README.md @@ -14,12 +14,13 @@ knows nothing about what any badge means. ## Install ```sh -bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@semver:*" \ +bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@*" \ --subdirectory plugins/thread-badges --tag-prefix thread-badges/ ``` -`semver:*` resolves to the newest `thread-badges/vX.Y.Z` tag, so this line stays -correct as the plugin releases and `bb plugin update` follows it. +With `--tag-prefix`, the range `*` resolves to the newest `thread-badges/vX.Y.Z` +tag, so this line stays correct as the plugin releases and `bb plugin update` +follows it. ## What it does diff --git a/plugins/top-tabs/README.md b/plugins/top-tabs/README.md index 93329e9..ab6fc6b 100644 --- a/plugins/top-tabs/README.md +++ b/plugins/top-tabs/README.md @@ -16,7 +16,7 @@ open on that tab. ## Install ```sh -bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@semver:*" \ +bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@*" \ --subdirectory plugins/top-tabs --tag-prefix top-tabs/ ``` diff --git a/plugins/workflow-stages/README.md b/plugins/workflow-stages/README.md index 8616776..6ffd449 100644 --- a/plugins/workflow-stages/README.md +++ b/plugins/workflow-stages/README.md @@ -23,12 +23,13 @@ and selected under **Settings → Appearance → Sidebar**. Without it the stage have nowhere to draw. ```sh -bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@semver:*" \ +bb plugin install "git:https://github.com/matthewdias/bb-plugins.git@*" \ --subdirectory plugins/workflow-stages --tag-prefix workflow-stages/ ``` -`semver:*` resolves to the newest `workflow-stages/vX.Y.Z` tag, so this line stays -correct as the plugin releases and `bb plugin update` follows it. +With `--tag-prefix`, the range `*` resolves to the newest `workflow-stages/vX.Y.Z` +tag, so this line stays correct as the plugin releases and `bb plugin update` +follows it. Then pick **Workflow** in the sidebar's Groups menu. From 6fdd5a8c482ffd558c4528d6d36020704a824c88 Mon Sep 17 00:00:00 2001 From: matthewdias Date: Mon, 5 Oct 2026 10:03:41 -0500 Subject: [PATCH 2/2] bb-dev release: install @* and refuse untagged versions up front release pinned ^, which on 0.x cannot reach the next minor, so bb plugin update stopped following the plugin. It now installs @* with --tag-prefix, which tracks the newest stable tag. @* resolves whether or not the checkout's own version is tagged, so release checks every target's /vX.Y.Z on the remote before removing anything and refuses if one is missing (previously that case failed after remove, leaving the plugin uninstalled). Afterwards it reports the resolved tag, and notes when it differs from the checkout's or cannot be read. Co-Authored-By: Claude Opus 5.5 --- CONTRIBUTING.md | 8 ++++-- scripts/bb-dev.mjs | 71 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 41cdfa8..8ae2f97 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,9 +27,11 @@ bb plugin dev plugins/thread-badges # rebuild + reload on every save npm run release -- thread-badges # this checkout -> the released tag ``` -`link all` and `release all` take every plugin at once. `npm run bb -- reload -` is a one-shot build and reload for when you are not leaving the watcher -running. +`release` follows the newest tag, so `bb plugin update` keeps tracking it, and +refuses — before removing anything — when the checkout's version has no tag +yet. `link all` and `release all` take every plugin at once. `npm run bb -- +reload ` is a one-shot build and reload for when you are not leaving the +watcher running. What removal does *not* touch is the plugin's data directory under `~/.bb/plugins//`, so recorded follow-ups and the stage catalog survive a diff --git a/scripts/bb-dev.mjs b/scripts/bb-dev.mjs index 9380948..1dca60c 100644 --- a/scripts/bb-dev.mjs +++ b/scripts/bb-dev.mjs @@ -44,8 +44,8 @@ function plugin(slug) { dir, id, version: manifest.version, - range: `^${manifest.version}`, tagPrefix: `${id}/`, + tag: `${id}/v${manifest.version}`, subdir: `plugins/${slug}`, }; } @@ -70,16 +70,21 @@ function fail(message) { process.exit(1); } -/** The installed source line, or null when the plugin is not installed. */ -function currentSource(id) { +/** + * One line of `bb plugin source` — "requested" is what was asked for, "resolved" + * the tag and commit it landed on — or null when the plugin is not installed. + */ +function sourceLine(id, field) { try { const out = bb(["plugin", "source", id], { capture: true }); - return out.match(/^\s*requested:\s*(.+)$/m)?.[1]?.trim() ?? null; + return out.match(new RegExp(`^\\s*${field}:\\s*(.+)$`, "m"))?.[1]?.trim() ?? null; } catch { return null; } } +const currentSource = (id) => sourceLine(id, "requested"); + /** * Only the values that differ from their declared default are worth carrying * across a reinstall — a default that stays default needs no help, and writing @@ -128,18 +133,51 @@ function swap(p, source, label) { const link = (p) => swap(p, [`path:${REPO}`, "--plugin", p.id], "released tag -> this checkout"); -const release = (p) => +/** + * `@*` resolves to whatever the newest tag is, so a checkout whose bumped + * version was never tagged would quietly get the previous release. Refuse it + * here, before anything is removed — every plugin, so `release all` cannot stop + * halfway with some swapped. + */ +function assertTagged(ps) { + const missing = ps.filter((p) => { + const out = execFileSync( + "git", + ["ls-remote", "--tags", REMOTE, `refs/tags/${p.tag}`], + { encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] }, + ); + return out.trim() === ""; + }); + if (missing.length > 0) { + fail( + `Not tagged on ${REMOTE}: ${missing.map((p) => p.tag).join(", ")}.\n` + + "Push the tag first, or release from a checkout whose version is tagged. " + + "Nothing was changed.", + ); + } +} + +// A bare range, not a pinned `^X.Y.Z`: on a 0.x version a caret cannot reach +// the next minor, so `bb plugin update` would stop following the plugin. bb +// rejects `@semver:*` alongside --tag-prefix, which is why it is spelled `@*`. +function release(p) { swap( p, - [ - `git:${REMOTE}@${p.range}`, - "--subdirectory", - p.subdir, - "--tag-prefix", - p.tagPrefix, - ], - `this checkout -> ${p.tagPrefix}v${p.version}`, + [`git:${REMOTE}@*`, "--subdirectory", p.subdir, "--tag-prefix", p.tagPrefix], + `this checkout -> newest ${p.tagPrefix} tag`, ); + const resolved = sourceLine(p.id, "resolved"); + if (resolved === null) { + console.warn(" note: could not read which tag was installed"); + return; + } + console.log(` resolved: ${resolved}`); + // Not necessarily a newer one: `@*` skips pre-releases, so a checkout at + // 0.8.0-beta.1 gets the newest stable release, which is older. + if (!resolved.includes(`@${p.tag} `)) { + console.warn(` note: this checkout is ${p.tag}; a different tag was installed`); + } +} function reload(p) { const started = Date.now(); @@ -175,9 +213,12 @@ switch (command) { case "link": targets().forEach(link); break; - case "release": - targets().forEach(release); + case "release": { + const ps = targets(); + assertTagged(ps); + ps.forEach(release); break; + } case "reload": targets().forEach(reload); break;