Group tab commands under tabs, scope flags per command - #23
Merged
Conversation
`run` and `serve` shipped as top-level commands in #22 but never made it into a released binary (v0.5.3 is tagged before it), so they're removed outright — no aliases, no deprecation. - `run` -> `tabs push`. The old name claimed something the command cannot do: it has no browser access and only queues a script for the user to run, which the README had to disclaim twice. `docs push` <-> `tabs push` now read as one mental model. - `serve` -> `tabs serve`, so the whole tab workflow lives under one noun. - New `tabs list` shows what the extension last exported, with the export's age (the file is a snapshot on disk and its ids go stale). Tolerant reader: the two extension surfaces POST different shapes. - New `registry.ts` is the command surface as data. `printHelp` renders from it and `assertFlagsAllowed` validates against it, so help can't drift from the routing and one command's flag can't leak into another. Fixes a real bug this surfaced: `parseArgs` uses one flat option table, so `run --port` parsed fine and was discarded — with a `serve` on a non-default port, `run --port <that>` reported "Script queued" while delivering to 49227 instead. `tabs push` now takes `--port`, and both commands resolve it through one `resolveServePort()`. Also: `init --skill <variant>` -> `--variant` (same three prompts as `tabs prompt --variant`; `--no-skill` stays the install-or-not switch), a typed error instead of a bare `Error` for a bad variant, `tabs push` rejects a zero-op script locally rather than surfacing the server's `invalid_payload`, help is grouped with the oversized header line trimmed, and ENVIRONMENT is split into common vs. endpoint overrides. Table helpers move to `table.ts` so `docs list` and `tabs list` share the display-width padding. The wire protocol and port 49227 are unchanged, so the extension keeps working. Its UI copy still names the old commands — tracked separately in colevels/tabbrew. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
runandserveshipped as top-level commands in #22 but never made it into a released binary —v0.5.3is tagged at the commit before it. No user has them, so they're removed outright: no aliases, no deprecation warnings.run→tabs push. The old name claimed something the command cannot do: it has no browser access and only queues a script for you to click Run on — which the README had to disclaim twice. Someone typingtabbrew run close-duplicates.tbrewand seeing✓ Script queuedcould reasonably believe their tabs had already closed.docs push↔tabs pushnow read as one mental model.serve→tabs serve, so the whole tab workflow lives under one noun.tabs list— the counterpart todocs list.tabs servewrote a file and said nothing more about it; the only way to see the result wascat | jq. Leads with the export's age, since the ids go stale.src/registry.ts— the command surface as data.printHelprenders from it andassertFlagsAllowedvalidates against it, so help can't drift from the routing and one command's flag can't leak into another.Fixes a real bug
parseArgstakes one flat option table, so--portwas accepted by every command and read by only one. With aserveon a non-default port:tabs pushnow takes--port, and both commands resolve it through a singleresolveServePort()— they can't disagree. Off-command flags are now rejected:Also
init --skill <variant>→--variant— same three prompts astabs prompt --variant;--no-skillstays the install-or-not switch. Bad variant now throws a typed error instead of a bareError.tabs pushrejects a zero-op script locally instead of surfacing the server'sinvalid_payload, which read as a bridge failure. (Slightly beyond the agreed scope — but the confusing message came from the error path this PR rewrites, so shipping it as-is meant knowingly keeping a bad message.)tabs servestartup prints what to do next in Chrome instead of a raw route table (routes still shown underTABBREW_DEBUG).src/table.ts, shared bydocs list/tabs list. Unchanged logic: still measures display width viaBun.stringWidth.Compatibility
The wire protocol (
POST /tabs,GET|POST /script,GET /health) and port 49227 are untouched, so the extension keeps working against this build. Its UI copy still names the old commands — filed as colevels/tabbrew#92, to fix before either side's next release.Test plan
bun run typecheckandbun run buildpassrun/serveare gone (Unknown command);--skillrejectedtabs serve --port 49299+tabs push --port 49299round-trips — verified the script lands on 49299, which is the case that previously misdeliveredtabs pushto a port with no listener, and with an empty script, both give actionable errorsdocs push --port/tabs list --porttabs listagainst Thai + Japanese + untitled tabs — columns verified equal by measured display width, not by eyeinit/init --variant compact/--dry-run/--uninstallrun from the compiled binary (exercises theSKILL.*.mdtext imports); installedTABBREW-CLI.mdcontains no dead command names🤖 Generated with Claude Code