Skip to content

Register a bb:// URL handler so other apps can open a thread or project #2199

Description

@RIP21

The workflow

Linear Desktop can hand an issue to a coding tool two ways: a custom link with query params, or a local script (~/.linear/coding-tools.json, docs). I wanted the Linear "Work on issue" button to drop me into a bb thread on the right repo with the issue context already there.

The same shape shows up outside Linear: Raycast, Alfred, a browser extension, a Slack link, a bookmarklet, or any tool that can emit a URL but cannot run a shell script.

What happens today

The local-script path works. bb thread spawn creates the thread, bb thread open <id> pushes navigation to every connected client ("delivered": 3 in my case), and open -a bb raises the window, since the app takes a single-instance lock and focuses its first window on re-activation. A working script is at the end of this issue.

The URL path does not work, because bb registers no URL scheme:

  • /Applications/bb.app/Contents/Info.plist has no CFBundleURLTypes key.
  • The Electron main process (dist/main.js in app.asar) never calls app.setAsDefaultProtocolClient and has no open-url handler. The only deep-link string in the bundle is an unrelated comment in the thread-search schema.

So bb://... is inert, and any integration that can only produce a URL cannot reach bb. The CLI workaround also only works on the machine bb runs on, and only for a caller that can execute a script.

What you would expect

A registered bb scheme handled by the desktop app, focusing the window and routing to the existing SPA routes:

bb://thread/thr_abc123              -> focus window, open that thread
bb://project/proj_abc123            -> focus window, open that project

Behavior when the app is not running should match open -a bb: launch, wait for the server, then route. Behavior when the thread id is unknown should be a visible error rather than a blank pane.

Two details worth deciding explicitly:

  • Whether to also register bb-nightly:// or a scheme suffix for dev/nightly builds, so a nightly install does not steal links from a stable one.
  • Whether an inbound URL should be allowed to name a machine, or whether it always routes on the local instance.

I have not filed prefill-without-send here; that is a separate request (see the linked issue), since a URL handler is useful on its own.

Context and alternatives

Verified on bb 0.39.0, macOS 15.5 (Darwin 25.5.0).

The working script I use today, registered as Linear's openIssue.path:

# resolve the bb project whose source path covers Linear's work dir
PROJECT_ID="$(bb project list --json | jq -r --arg dir "$LINEAR_WORK_DIR" '
  [ .[] | . as $p | .sources[] | select(.type == "local_path") | .path as $src
    | select($dir == $src or ($dir | startswith($src + "/")))
    | { id: $p.id, depth: ($src | length) } ]
  | sort_by(-.depth) | .[0].id // empty')"

THREAD_ID="$(bb thread spawn --project "$PROJECT_ID" --environment "$LINEAR_WORK_DIR" \
  --title "$LINEAR_ISSUE_IDENTIFIER" --prompt "$LINEAR_PROMPT" --json | jq -r .id)"

bb thread open "$THREAD_ID"
open -a /Applications/bb.app

Two rough edges I hit building that, which I can file separately if useful:

  1. The CLI is a #!/usr/bin/env node script, so it fails with env: node: No such file or directory when invoked from a Finder-launched app whose PATH has no nvm or homebrew node. The workaround is to run the same script under Electron's bundled node, which always ships with the app: ELECTRON_RUN_AS_NODE=1 /Applications/bb.app/Contents/MacOS/bb <cli-path> …. A launcher shim that does not need an external node would remove the need for that trick.
  2. bb thread open [id] [path] reads its single positional as a path when BB_THREAD_ID is set, so bb thread open thr_abc opened a file literally named thr_abc in the current thread's panel instead of switching threads. Unsetting BB_THREAD_ID fixes it, but the silent reinterpretation is easy to miss.

Happy to prototype the handler in a fork if you want it, following CONTRIBUTING's issue-first rule.

Checks

  • I searched open and closed issues for the same request (deep link, url scheme, protocol handler, compose prefill, Linear).

AGENT GENERATED: by claude-opus-5

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions