Command line
The groupshelf command groups the windows you have open, adds to and takes apart groups, and lists windows, all from Terminal or a script. It comes with GroupShelf 1.3.0 and later.
Install
The command is part of GroupShelf.app. Installing it means linking it into a folder on your PATH.
With Homebrew, installing GroupShelf links the command for you:
brew install --cask alpharium-tech/tap/groupshelfIf you installed GroupShelf with Homebrew before 1.3.0, updates from within the app don't add the link. Run this once:
brew reinstall --cask groupshelfWithout Homebrew, GroupShelf's Settings › Command Line shows the command that makes the link, ready to copy. Run it in Terminal; it asks for your Mac password. For GroupShelf in the Applications folder, it is:
sudo mkdir -p /usr/local/bin && sudo ln -sf '/Applications/GroupShelf.app/Contents/Helpers/groupshelf' /usr/local/bin/groupshelfSettings offers it only once GroupShelf is in the Applications folder and opened from there: a link to a copy in Downloads would break when the app moves.
Once the command is in place, Settings › Command Line shows where it's installed, and groupshelf version prints the version of GroupShelf it belongs to.
Turn it on
Commands that act on windows work only once you allow them: in GroupShelf's Settings › Command Line, turn on “Allow control from the command line and scripts”. It stays off until you do. help and version work either way.
While it's on, apps you allow to control GroupShelf, such as your terminal app, and anything run in them can group windows and read the title of every window on this desktop.
Permissions
The first time, macOS asks whether your terminal app may control GroupShelf, and the command waits for your answer. If you declined, turn it on in System Settings › Privacy & Security › Automation, under your terminal app.
Visual Studio Code
The Universal build of VS Code, the one most people download (versions 1.104 and later), lacks an entry macOS requires before it asks for this permission. macOS would refuse without asking and never list GroupShelf under Visual Studio Code, so groupshelf gets macOS to ask anyway. It prints this line, and the dialog names Visual Studio Code:
groupshelf: asking macOS whether your terminal app may control GroupShelf…
Answer it once and it works from then on. If GroupShelf still isn't listed under Visual Studio Code in Automation, install VS Code's Apple silicon or Intel build instead of Universal: both carry the entry. Homebrew's visual-studio-code cask installs the build for your Mac's chip.
Naming windows
Wherever a command takes a <window>, write it in one of three ways. Quote anything with spaces in it.
- An app
One of its windows: the one active in that app, otherwise its frontmost. Name the app as the Dock does or by its file name, in any letter case, or by its bundle ID or path.Safari"Visual Studio Code"com.apple.Safari/Applications/Safari.app - An app and part of a title
Its window whose title contains the text, in any letter case.Code:proj-a"Terminal:my server" - A window ID
A number fromgroupshelf list.
Name an app twice to take two of its windows.
A title that fits several windows is an error unless you add --frontmost, which takes the frontmost of them, or --all, which takes them all. --all also takes every window of an app named without a title.
groupshelf never opens an app, so the app must be running. group and add wait up to 30 seconds for a running app's window to appear, so an app opened just before works:
open -a Safari && open -a Terminal && groupshelf group Safari TerminalOnly windows on this desktop count, minimized ones included. A full-screen window has a desktop of its own, so it doesn't.
A relative path is read from the current folder. To add a title to a path, put it after .app:, as in /Applications/Safari.app:Docs.
Commands
groupshelf help lists the commands, and groupshelf help <command> explains one. If GroupShelf isn't running, a command starts it in the background; it never opens the apps you name.
Options can go anywhere before a --, with their value after a space or =: --name Work or --name=Work. Put an app whose name starts with - after --.
group
groupshelf group [--name <name>] [--all | --frontmost] <window> <window>...
Groups windows into one set of tabs, in the order given. The first window sets the group's position and size, and its tab is shown when done. A window already in another group leaves it for this one.
It needs at least two windows, or a single <window> with --all. If some don't appear within 30 seconds, the group is made from the rest and the command reports the missing ones. Run on a group of exactly those windows, it only puts the tabs in the given order.
--name <name>- Names the new group, to find it later with
--group. --frontmost- When a title fits several windows, takes the frontmost.
--all- Takes every window that fits: every window of an app, every window carrying a title.
groupshelf group Safari Terminal Notes Mailgroupshelf group --name Work Code:proj-a Terminal:proj-a Safariopen -a Safari && open -a Terminal && groupshelf group Safari Terminalgroupshelf group --all Terminal
add
groupshelf add [--all | --frontmost] --group <name> <window>... groupshelf add [--all | --frontmost] <member> <window>...
Adds windows to the end of an existing group on this desktop, which keeps its position, size and shown tab. A window already in another group moves. As with group, only windows already open are used, waited for up to 30 seconds.
Name the group with --group, or by one of its windows (<member>, written like a <window>). An app, or a title with --frontmost, takes a window the group doesn't have yet when there is one; a window already in the group stays as it is.
--group <name>- A group by its name, in any letter case: the one given with
--name, or with “Name Group…” when you right-click its tab bar. --frontmost- When a title fits several windows, takes the frontmost.
--all- Takes every window that fits: every window of an app, every window carrying a title.
groupshelf add --group Work Slackgroupshelf add Code:proj-a Terminal:proj-a
detach
groupshelf detach [--all | --frontmost] <window>...
Takes windows out of their groups; a group left with one window dissolves. Nothing is opened or waited for: the windows must already be on this desktop. A window in no group is left as it is.
--frontmost- When a title fits several windows, takes the frontmost.
--all- Takes every window that fits: every window of an app, every window carrying a title.
groupshelf detach Slackungroup
groupshelf ungroup [--group <name>]... [<member>]...
Dissolves groups, leaving their windows where they are. Name a group with --group (repeat it for more), or by one of its windows (<member>, written like a <window>). A window in no group is skipped. If any of them can't be found, no group is dissolved.
--group <name>- A group by its name, in any letter case: the one given with
--name, or with “Name Group…” when you right-click its tab bar.
groupshelf ungroup --group Workgroupshelf ungroup Code:proj-a
list
groupshelf list
Lists the windows on this desktop that can be grouped, front to back, minimized ones last. GroupShelf's own windows and those of excluded apps are left out. Each line is one window, with these fields separated by tabs and no header line:
- Window ID
- App name
- Title
- Group number, or empty
- Group name, or empty
minimized, or empty
groupshelf list4211 Safari GroupShelf — Command line 1 Work4198 Terminal proj-a — zsh 1 Work4305 Mail Inbox3977 Notes Shopping minimized
A tab or line break inside a title becomes a space, so the fields always line up. The ID works as a <window> for the other commands; with fzf, you can pick windows and group them:
groupshelf list | fzf -m | cut -f1 | xargs groupshelf grouphelp
groupshelf help [<command>]
Shows the list of commands, or the usage of one: group, add, detach, ungroup or list.
groupshelf help groupversion
groupshelf version
Shows the version and build of the GroupShelf the command came with. The two are always one build.
Exit status
Scripts can branch on the exit status. Messages go to standard error, each starting with groupshelf:.
0- Success.
1- GroupShelf reported an error: the request needs fixing, such as an app that isn't running. A group made without some of its windows counts too.
2- Usage error: the command line itself is wrong. The command says what's wrong and shows its usage, without contacting GroupShelf.
3- GroupShelf could not be reached: the setup needs fixing, such as a missing permission.
Troubleshooting
What each message means, and what to do. Names and numbers in them are examples: yours show what you typed.
Reaching GroupShelf · Exit status 3
groupshelf: GroupShelf.app not found
The command can't find the GroupShelf it came with: it's a copy kept outside the app, and no GroupShelf is installed. Install GroupShelf, then link the command as under Install instead of copying it.
groupshelf: GroupShelf could not be launched: <reason>
GroupShelf wasn't running, and macOS couldn't open it, for the reason given. Open GroupShelf yourself, then run the command again.
groupshelf: GroupShelf did not start within 15 seconds
GroupShelf was opened in the background but wasn't ready in time. Run the command again.
groupshelf: GroupShelf did not answer in time; if macOS asked whether your terminal app may control GroupShelf, answer that and try again
Most often, macOS is still waiting for your answer to its permission prompt. Answer it, then run the command again.
groupshelf: not allowed to control GroupShelf; allow your terminal app in System Settings › Privacy & Security › Automation
Your terminal app may not control GroupShelf. In System Settings › Privacy & Security › Automation, turn on GroupShelf under your terminal app.
groupshelf: if Visual Studio Code doesn't list GroupShelf there, install VS Code's Apple silicon or Intel build; its Universal build can't ask macOS for this permission
In VS Code's terminal, this line follows the one above. See Visual Studio Code under Permissions.
groupshelf: this version of GroupShelf doesn't support groupshelf; update GroupShelf
The GroupShelf the command reached predates it, such as an old copy elsewhere on your Mac. Update GroupShelf to 1.3.0 or later, and remove any old copy.
groupshelf: could not reach GroupShelf (error <number>)
macOS couldn't deliver the request for another reason; the number is its error code. Quit and reopen GroupShelf, then try again. If it keeps happening, contact support with the number.
GroupShelf's settings and state · Exit status 1
groupshelf: control from the command line is off; turn on "Allow control from the command line and scripts" in GroupShelf Settings › Command Line
Turn on the switch in Settings › Command Line, as under Turn it on.
groupshelf: GroupShelf needs Accessibility access; allow it in System Settings › Privacy & Security › Accessibility
GroupShelf can't move windows without it. Turn on GroupShelf in System Settings › Privacy & Security › Accessibility.
groupshelf: GroupShelf is paused while Stage Manager is on
GroupShelf can't run alongside Stage Manager. Turn Stage Manager off in System Settings, then restart GroupShelf.
groupshelf: GroupShelf hasn't finished starting; try again
Run the command again in a moment.
groupshelf: another group request is still running
GroupShelf takes one request at a time, and group and add can wait up to 30 seconds for windows. Run it again once the other one is done.
groupshelf: "Slack" is excluded from grouping in GroupShelf settings
The app is in Excluded Apps under Settings › Grouping. Remove it there to group its windows.
Naming windows · Exit status 1
groupshelf: "Safari" isn't running; open it first — groupshelf only works with windows already open
Open the app first. group and add then wait up to 30 seconds for its window.
groupshelf: no application named "Safary"
Check the spelling. An app that isn't running and isn't in an Applications folder can only be named by its bundle ID or path.
groupshelf: "/Applications/Utilities" is not an application
The path leads to something other than an app. Point it at the .app itself.
groupshelf: "Terminal:proj" fits 2 windows ("proj-a — zsh", "proj-b — zsh"); give more of the title, a window ID from groupshelf list, or --frontmost or --allThe titles of several windows contain the text. Narrow it down in one of the ways the message lists.
groupshelf: no window 4211 on this desktop; groupshelf list shows the windows there
The window has closed, or it's on another desktop or in full screen. Run groupshelf list for the current IDs.
groupshelf: no window for "Slack" on this desktop
detach, ungroup and a <member> go by the windows already on this desktop, without waiting. Check that the window is open here.
groupshelf: "4211" names a window another argument already names
Two arguments point to the same window. Remove one of them.
groupshelf: GroupShelf can't group its own windows
Leave GroupShelf out of the command.
Naming groups · Exit status 1
groupshelf: no group named "Work"
No group has that name. groupshelf list shows each window's group name in its fifth field.
groupshelf: --group "Work" fits 2 groups; name the group by one of its windows instead
Several groups share the name. Name the group by one of its windows instead, as a <member>.
groupshelf: the group of "Work" isn't on this desktop
add adds only to a group on this desktop. Go to the group's desktop and run it there.
groupshelf: "Safari" isn't in a group; groupshelf group makes one
The window named as <member> is in no group. Make one with group.
Partial results · Exit status 1
groupshelf: grouped 3 windows; no window for "Mail" on this desktop within 30 seconds groupshelf: added 1 window; no window for "Mail" on this desktop within 30 seconds groupshelf: nothing added; no window for "Mail" on this desktop within 30 seconds
Everything before the first semicolon was done. The windows listed after it are missing: no window of theirs appeared on this desktop within 30 seconds.
groupshelf: no group created: only 1 window to group; no window for "Mail" on this desktop within 30 seconds
Fewer than two windows turned up, so nothing was changed.
groupshelf: grouped 2 windows; "Mail" could not be added to the group
The window's group, or the group it would join, is locked. Right-click the locked group's tab bar, choose “Unlock Group”, then run the command again.
Usage errors · Exit status 2
groupshelf: unknown option '--names' Usage: groupshelf group [--name <name>] [--all | --frontmost] <window> <window>... Run 'groupshelf help group' for more.
The command line itself is wrong: the first line says how, and the command's usage follows. Nothing was sent to GroupShelf.