Skip to content

feat: reins group / ungroup — create, edit and dissolve tab groups - #35

Merged
karngyan merged 17 commits into
mainfrom
feat/tab-groups-write
Sep 26, 2026
Merged

karngyan merged 17 commits into
mainfrom
feat/tab-groups-write

Conversation

@karngyan

@karngyan karngyan commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #34. Review that one first; this PR's diff is against feat/tab-groups.

What

This is the write side of tab groups. The agent can mark the tabs it works in, or organize the user's tabs when asked. reins still never groups anything on its own.

reins group --tab 12 --tab 13 --title reins --color orange   # new group → "group 7"
reins group --tab 14 --group 7                               # add to an existing group
reins group --group 7 --collapse                             # edit: title / color / collapse / expand
reins ungroup --tab 12                                       # take tabs out
reins ungroup --group 7                                      # dissolve the group; tabs stay open
  • Protocol:
    • GroupTabsParams, UpdateGroupParams (needs at least one prop), and UngroupTabsParams (exactly one of tabIds/groupId).
    • Colors are the nine Chromium ones.
    • All three methods are tier read, because grouping touches the tab strip, never page content. Readonly sites can be grouped; denied ones can't.
  • Extension handlers:
    • A new group is pinned to the first tab's window via createProperties.windowId. Chrome's default is the focused window, which would drag tabs across windows.
    • Props are only sent when set, so collapsing doesn't reset the title.
    • Chrome's own errors (bad group id) pass through unchanged.
  • Policy gate:
    • group_tabs/ungroup_tabs check every target tab's host. For ungroup --group, that means the group's current tabs.
    • The first denial refuses the whole call and tags the audit record with that tab.
    • The checked set is pinned into params, and ungroupTabs only accepts that set, so the handler can't act on a tab the gate didn't check.
    • The unsupported check runs before any lookup, because on a browser without groups tabs.query({groupId}) may ignore the filter and return every tab.
    • update_group only edits the label and has no host check.
  • CLI:
    • reins group covers both create/add and edit through a small methodFor hook on ToolCommand.
    • Validation: --collapse and --expand together, a missing --tab/--group, and an unknown color are all usage errors.
  • Docs:
    • SKILL.md documents the commands and adds one line of guidance: you can group the tabs you open for a task so the user sees which are yours, but don't regroup the user's tabs unless asked. It also notes that grouping moves tabs into the group's window.
    • Web commands page and a minor changeset.

Test plan

  • pnpm build && pnpm test && pnpm typecheck && pnpm lint: all green (515 tests).
  • Unit tests:
    • Handlers: new group in the first tab's window, adding to an existing group, props only when set, ungroup, empty set is a no-op, unsupported.
    • Gate: every tab checked, one denied tab refuses all and tags its tabId, ungroup --group resolves the group's tabs once and ungroups exactly those, update_group ungated, schema validation, unsupported before any policy check.
    • CLI: build, method selection and every usage error.
  • Live in Dia, only on tabs reins opened: create, collapse, rename, recolor, ungroup, dissolve. The groups show in Dia's vertical sidebar. Of the nine colors, only cyan changes: it comes back as blue.
  • Live in Chrome, only on tabs reins opened:
    • open two tabs → group (title + color) → groups/tabs show them → collapse → expand + rename + recolor → ungroup one tab → dissolve (tabs stayed open) → close.
    • Error paths: bad group id, a missing flag, an invalid color.
    • Re-run after the review fixes.
  • Not live-tested: a denied site, because adding a deny rule from the CLI can only be undone in the popup. Unit tests cover it.

🤖 Generated with Claude Code

karngyan and others added 13 commits September 26, 2026 22:33
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Daemon fans list_groups out across connected browsers, tagging each
group with browserId + browser. A browser that fails (no tab-group
support, or an older extension) contributes nothing; only when every
targeted browser fails is the first error rethrown. Adds the groups
command, groupsText, and a g<id> marker on grouped tabs in reins tabs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Route group_tabs, update_group and ungroup_tabs through dispatch. The
gate checks every target tab's host (for ungroup --group, the group's
current tabs); the first denial refuses the whole call with that tab's
id in PolicyDenied.meta. The resolved tabIds are pinned into params so
gate and handler act on the same set. update_group has no host check.
The unsupported-browser check runs before any lookup or policy check.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
One command covers two bridge methods: --tab groups tabs (group_tabs),
--group alone edits a group (update_group). ToolCommand gains an
optional methodFor(params) hook, used at the single rpc call site.
Also folds the duplicated tabs/groups build bodies into browserOnly.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
karngyan and others added 3 commits September 27, 2026 00:19
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Dia was verified live: groups show in its sidebar (cyan renders as blue).
Remove the claim that Dia (and, unverified, Arc) lack tab groups from
the changeset and comments, use a neutral browser in test fixtures, and
bring the spec in line with the skipped-browser reporting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also correct the skill: Dia supports tab groups (cyan shows as blue);
browsers without the API now get an error naming them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@karngyan
karngyan added this pull request to stack #36 September 26, 2026 18:55
Base automatically changed from feat/tab-groups to main September 26, 2026 18:56
main's tree equals the pre-squash PR1 tip, so every conflict resolves
to this branch's side; the net diff against main is unchanged PR2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@karngyan
karngyan merged commit 1033778 into main Sep 26, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant