Seven commands and the communication layer that emerged

https://thoughtbot.com/blog/seven-commands-and-the-communication-layer-that-emerged
Rob Whittaker in United Kingdom

On Tuesday, 11 February, I made seventeen commits to my management system. That is more than any other day in the project so far. The previous two weeks had been about structure. Daily routines. Meeting sync. Project tracking. This week was about communication.

The trigger was simple. I ran /inbox and spotted the pattern. Every time: fetch the item, decide what to do, place it somewhere, move on. The first version of the command automated that loop:

# Inbox Command

Process the Things inbox one item at a time, newest first.

## Instructions

### Step 1: Load Context

Fetch in parallel:

1. Call `mcp__things__get_inbox` to get all inbox items
1. Call `mcp__things__get_projects` to get project names

Sort inbox items newest first (by creation date).

If the inbox is empty, report "Inbox is empty" and stop.

### Step 2: Present the Next Item

For each inbox item, present:

- Title
- Age
- Tags (if any)
- Notes (truncated if long)
- Related project (fuzzy-match title against project names)

Then wait for the user to say what they want to do.

Within thirty minutes, that command went through three revisions. The loop version advanced on its own. I changed it to single-item mode because I wanted control. Then I added reading detection: if the notes contain a URL, fetch the page title and suggest a tag. I created three commits, three lessons about how I process information.

The one-day command

The same morning, I created /reply for Slack DMs. It standardised the flow: find the user, open the DM, fetch the history, draft the reply, and send.

It lasted twenty-four hours.

By Wednesday, I had split it into /dm for direct messages and /thread for channel thread replies. Both shared a patterns file that held the common steps:

# DM Command

Send a direct message on Slack to $ARGUMENTS.

Follow shared patterns from
`.claude/commands/slack-patterns.md`.

## Instructions

### Step 1: Setup and Find User

1. **Rube Session Setup** (see `slack-patterns.md`)
1. **Find User** (see `slack-patterns.md`)

### Step 2: Open DM and Fetch History

1. Use `SLACK_OPEN_DM` with the user's ID
1. **Fetch History** on the DM channel
   (see `slack-patterns.md`)

The split happened because DMs and threads are different conversations. A DM is private, one-to-one, with full history. A thread is public, anchored to a specific message, with context that the whole channel can see. The same “reply” verb hid two different communication patterns.

The communication stack

That refactoring revealed something. Each command I built that week mapped to a communication channel:

  • /dm — Slack direct messages
  • /thread — Slack channel threads
  • /slack — new channel messages
  • /email — Gmail replies and composition
  • /hub — reading saved Hub pages
  • /draft — anything else (LinkedIn, talking points, Hub replies)

Six commands. Six ways I talk to people at work. The /draft command became the catch-all for channels without an API:

# Draft Command

Draft a reply or message for any context. Does not send.

Use this for LinkedIn messages, in-person talking points,
Hub replies, or any situation where `/dm`, `/thread`, and
`/email` don't apply.

## Pattern: Voice

Blend the user's natural tone with DHH and Nicholas Lezard:

- Direct and opinionated, but not abrasive
- Concise sentences that carry weight
- Avoid corporate filler
- Match the formality of the channel

The voice pattern is the part I did not expect to matter. I had assumed Claude would write in a generic assistant tone. Instead, by defining a voice, every draft came back in a register I recognised as mine. Not perfect. Close enough to edit rather than rewrite.

Plan mode and command boundaries

Not everything went well. On Tuesday, I hit a bug where plan mode leaked between commands. When I ran /waiting, it accumulated a state that bled into /retro. That broke both commands.

The fix took two commits and a revert. The first attempt added “Never use EnterPlanMode” to every command. That was wrong. The real fix was removing the auto-advance loop from /waiting. Each command invocation stayed self-contained. Commands are not functions. They are conversations. And conversations should end clean.

What I learned

Building these commands showed me something. My job as a director is communication:

  • Write messages
  • Respond to threads
  • Follow up on waiting items
  • Process my inbox
  • Draft replies
  • Read Hub posts

The actual management decisions happen in the gaps between those conversations.

The system I built in the first two weeks gave me structure: routines, meeting sync, and project files. This week gave me flow. The difference is that structure tells you what to do. Flow tells you how to do it without thinking about the mechanics.

I am not faster. I am less distracted. Each command removes one decision about where to go and what to type. That compounds over a day of fifty small conversations.

Try it yourself

Pick one communication pattern you repeat daily. Write a command for it. Not a script. A conversation. Define the steps, the voice, the context. Then run it and see what breaks. The breaking is where the learning is.

About thoughtbot

We've been helping engineering teams deliver exceptional products for over 20 years. Our designers, developers, and product managers work closely with teams to solve your toughest software challenges through collaborative design and development. Learn more about us.