Check unread Outlook emails, Teams chats, and your next meeting from the terminal.
blick replaces as much day-to-day Outlook and Teams usage as fits the keyboard. The aperture is deliberately narrow: it surfaces the things that need action right now — unread mail, unread chats, the meeting that's about to start — and gives you keyboard verbs to reply, mark read, compose, or join. It is not where you go to search history, organize folders, browse Teams channels, or take a call. Teams stays the place for calls and meetings, permanently.
Unread mail means the Inbox folder, the same count Outlook badges. Junk Email, Deleted Items, and anything a rule filed elsewhere stay out of the queue. search still covers the whole mailbox so a legitimate message that landed in Junk can be found; those hits carry a [junk] marker, render links as text only, and refuse to save or open attachments, the same restrictions Outlook puts on that folder.
Scope grows by friction, not by speculation. If something missing bites in real use it goes on the list; if it doesn't, it doesn't. The Graph API is the outer limit on what blick can do, but blick deliberately doesn't try to expose everything Graph offers.
Add the Excelano apt repository once (one-time setup):
curl -fsSL https://excelano.com/apt/setup.sh | sudo shThen install it, so apt upgrade keeps it current:
sudo apt install blickOn macOS or Linux, tap and trust the repository once — Homebrew gates third-party taps behind explicit trust (one-time setup):
brew tap excelano/tap
brew trust excelano/tapThen install it, so brew upgrade keeps it current:
brew install blickWith WinGet, so winget upgrade keeps it current:
winget install Excelano.blickOr download the windows_amd64 zip from the releases page and unzip it.
curl -fsSL https://raw.githubusercontent.com/excelano/blick-cli/main/install.sh | shThis downloads the latest release binary for your platform, verifies the SHA-256 checksum, and installs it to /usr/local/bin (or ~/.local/bin if /usr/local/bin isn't writable). Override the destination with BLICK_INSTALL_DIR=$HOME/bin sh; pin to a specific tag with BLICK_VERSION=v0.4.0 sh. To uninstall: curl -fsSL https://raw.githubusercontent.com/excelano/blick-cli/main/uninstall.sh | sh (or sudo apt remove blick if installed via apt).
git clone https://github.com/excelano/blick-cli
cd blick-cli
go build ./cmd/blick
mv blick ~/bin/Requires Go 1.25+.
You'll need an Azure app registration before blick can authenticate. Both options below assume you've already installed the binary.
curl -fsSL https://raw.githubusercontent.com/excelano/blick-cli/main/setup.sh -o setup.sh
chmod +x setup.sh
az login
./setup.sh(Or if you installed via apt, setup.sh ships at /usr/share/doc/blick/setup.sh.)
- Go to Azure Portal → Azure Active Directory → App registrations
- New registration
- Name:
blick - Supported account types: Accounts in this organizational directory only
- Name:
- Authentication → Add a platform → Mobile and desktop applications
- Check
https://login.microsoftonline.com/common/oauth2/nativeclient - Enable Allow public client flows (required for device code flow)
- Save
- Check
- API permissions → Add a permission → Microsoft Graph → Delegated permissions:
User.ReadMail.ReadWriteMail.SendCalendars.ReadPresence.ReadWritePeople.ReadChat.ReadWrite(optional, Teams chat support)Chat.Create(optional, Teams chat support)
- Copy Application (client) ID and Directory (tenant) ID from the Overview page
Create the config file:
mkdir -p ~/.config/blick
cat > ~/.config/blick/config.json << 'EOF'
{
"client_id": "YOUR_CLIENT_ID_HERE",
"tenant_id": "YOUR_TENANT_ID_HERE",
"enable_teams": true
}
EOFBy default, none of these Graph permissions require admin consent — they are all user-consentable, including the Chat scopes. Some tenants tighten the default and require admin consent for one or more of these; if yours does, ask your IT admin to grant consent:
az ad app permission admin-consent --id YOUR_CLIENT_IDIf a specific scope is blocked, the corresponding feature degrades: set "enable_teams": false to skip Teams chat. If Presence.ReadWrite is blocked, blick presence won't work but everything else does. The address book seed (blick contacts seed) needs People.Read; if that's blocked, hand-edit ~/.config/blick/contacts.json instead.
$ blick
📅 Standup with Tony — in 47 min · 10:30 AM — Online
📧 unread emails (3):
1. Alex K. — "Deck revisions" (10 min ago · 9:42 AM)
2. Jordan R. — "RE: Contract draft" (1 hour ago · 8:53 AM)
3. Newsletter — "Weekly digest" (3 hours ago · 6:48 AM)
💬 unread chats (2):
4. Sam P. — "quick question on timeline" (32 min ago · 9:20 AM)
5. Riley T. — "can you check the numbers" (1 hour ago · 8:51 AM)
Commands:
<N> view r<N> reply
d<N> done r refresh
t show today x exit (mark all read)
h help q quit
blick> 1
(shows full email body)
blick> r4
Reply in Sam P.:
(end with `.` on a line by itself, or Ctrl-C to cancel)
> Should be ready by EOD tomorrow.
> Let me know if you want to walk through it on a call.
> .
Sent.
blick> d3
Marked as read: Weekly digest
blick> x
All marked as read.
Each short command has a full-word equivalent — reply 4, done 3, refresh, exit, quit, help, today. Type h (or help) at the prompt for the full reference.
t (or today) shows the full calendar for the day, with past events dimmed and the current event highlighted:
$ blick today
Tuesday, June 9, 2026
9:00 AM – 9:30 AM Daily standup Online
10:30 AM – 11:00 AM Tony 1:1 Online
1:00 PM – 2:00 PM Project review · now Online
4:00 PM – 5:00 PM Demo prep Conf Room A
4 events · 4h scheduled
The same view is available inside the REPL by typing today.
~/.config/blick/config.json— client ID, tenant ID, and feature flags~/.config/blick/token.json— cached OAuth token (auto-created)
All scopes are user-consentable by default per Microsoft's stock Graph policy. Individual tenants can require admin consent for any of them — see the Admin Consent section.
| Permission | What it does |
|---|---|
| User.Read | Verify authentication |
| Mail.ReadWrite | Read and mark-read emails |
| Mail.Send | Send new emails and reply-all to existing ones |
| Calendars.Read | Show next meeting and today's calendar |
| Presence.ReadWrite | Set and read your Teams presence (blick presence) |
| People.Read | Seed the address book from frequently-contacted people |
| Chat.ReadWrite | Read/reply Teams chats |
| Chat.Create | Start new 1:1 chats with address-book contacts |
Set your Teams status from the shell:
blick presence busy # available | busy | dnd | brb | away | offline
blick presence # no argument reports your current statusp/presence work the same way inside the dashboard REPL.
The mechanic is Graph's setUserPreferredPresence (the same override the
Teams status picker writes), paired with a setPresence session so the
status shows even with no Teams client running. A manually set status takes
precedence over Teams' own session-level state.
One caveat worth knowing: Microsoft presence is activity-driven. busy,
dnd, brb, and offline hold as a solid status, but available will
drift to "AvailableIdle" and eventually Away without an active client sending
activity — a fire-and-forget CLI can't keep a clean green "Available" alive
on its own. A manually set status also lapses after a few hours as its
session expires; re-run the command (or drive it from cron) to refresh.