# Claude Code Dashboard Mods: The Set We Built and Use

> The Claude Code dashboard mods we run above the prompt for activity, context, repo, CI, memory and usage, with how each works and a code sketch to start from.

Source: https://capitalandcompute.net/blog/claude-code-dashboard-mods/
Published: 2026-10-03
Updated: 2026-10-04

**The short answer.** Claude Code dashboard mods each answer one nagging question for free: what is running, how full the context is, which branch and PR you are on, whether memory loaded, why the laptop fan is screaming, how much plan you have left, what changed in the latest release, and which worktrees you have open. We built ten of them as one pack. Eight draw a numbered row above the prompt, and two add a clickable chip to an existing row. Type the digit, and a pane slides open with the details.

Think of it as a car dashboard for Claude Code. You do not need to pop the hood to know you are low on fuel. You just glance down.

These are the mods we use every day while building this site. Below is each one, the problem it solves, and a short code sketch of how it hooks in. The sketches are trimmed on purpose: they show the trick, not the full source.

Key takeaways

* Giving each concern its own row is easier to read than squeezing everything into one status line. The full detail sits one keypress away, so each row only needs to show one number.
* You can click the rows: the arrows on row 2 raise or lower the effort level, and the pane behind row 8 switches models, so there is no slash command to type.
* Most of the mods only read what is already on your machine (Claude Code events, git, gh, the OS), so they are free to run. Explain is the one that asks a model, and only when you press its key. Changelog is the only one that goes online.
* The most useful mod is one you never see: the repo mod tells Claude which branch, issue and PR you are on, so you are both looking at the same thing.

![Eight numbered status rows above the Claude Code prompt, with effort on auto beside a clickable up arrow, and a Model pane listing ten models, each with a Use button.](/images/claude-code-mods/model-picker.webp)

The first eight mods sit as numbered rows above the prompt. Click the arrow on row 2 to change effort, or press Use on the right to switch models. Tap the magnifier for a closer look.

## What is a Claude Code mod?

A mod is a [plugin that changes how Claude Code looks and behaves](https://code.claude.com/docs/en/plugins/mods/overview): a few JavaScript or TypeScript functions that run right inside Claude Code. Anthropic [launched mods on 1 October 2026](https://claude.com/blog/claude-code-mods) and describes them as “small TypeScript functions that change how Claude Code works.” According to the documentation, you need Claude Code v2.1.287 or newer, and mods are on out of the box.

Claude Code already had [settings hooks, skills and MCP servers](/blog/claude-code-harness-guide/), so why bother? Mods add two things. They can remember what happened earlier in the session, and they can **draw** on screen: a pane next to the chat, or rows above the prompt. Everything in this post relies on that second one.

Looking for mods someone else built? Our guide to [the best Claude Code mods and where to find them](/blog/best-claude-code-mods/) compares the directories and explains how to check a mod before you install it. This post covers building your own.

## Why numbered rows instead of one status line?

Claude Code’s [status line](https://code.claude.com/docs/en/statusline) is a single line of text produced by a script. We tried fitting the model, context, branch, CI and cost onto it, and it became too crowded to read at a glance.

So each concern got its own row:

* **One row per job, numbered 1 to 8.** Each row shows the one number you would act on, such as context 61%, CI 4/5 or 5h 15%.
* **The number opens the detail.** Type it into an empty prompt (or click the row) and that mod’s pane opens, with tabs for the detail.
* **Small extras ride on a row that already exists.** Mods 9 and 10 have no row of their own. Each adds a clickable chip to the end of a related row, so the stack does not grow.
* **Controls are clickable.** Arrows, toggles and model names respond to a click, so common changes take one tap.
* **Every pane also has a slash command,** such as `/ctx` or `/limits`, for keyboard users and the Desktop app.
* **No tokens by default.** Drawing a row is just code running on your machine. Claude never sees it.

Rows that change often (activity, context) are at the top. Rows you check now and then (system, usage) are at the bottom.

## How is a mod put together?

Each mod is three small files, plus a shared UI kit:

```
activity/
├── .claude-plugin/plugin.json   # name, version, description
└── hooks/
    ├── hooks.json               # { "modules": ["./register.tsx"] }
    └── register.tsx             # the hooks
```

The mods live in the repository under `.claude/skills/`. Once you trust the workspace, Claude Code loads every folder there that holds a `.claude-plugin/plugin.json`, so nobody has to install anything. Each mod also checks which project it is running in and does nothing anywhere else.

Every hook has the same shape: `on(event, matcher, ($, e, next) => ...)`. `$` is the [mods API](https://code.claude.com/docs/en/plugins/mods/api), `e` is the event, and `next(e)` passes the event on to Claude Code. One rule applies to every mod here: **watch and draw, but never block.**

## 1\. Live activity: what is Claude doing right now?

**The problem:** a long turn looks the same whether Claude is on its first tool call or stuck on its fortieth.

**The row:** a spinner and a count of what is running, or `idle` with how long the last turn took.

**The pane (`/running`):** three tabs. **Now** lists running tool calls with a timer on each, plus subagents and background shells. **MCP** counts calls per server and lists every connected server. **Session** counts skills and tool calls by name.

![The Live pane's MCP tab showing calls by server and eighteen connected servers.](/images/claude-code-mods/live-mcp.webp)

The Live pane on its MCP tab: calls by server, and every connected server with its tool count.

It works by timing every tool call. `next(e)` returns when the tool finishes, so the time in between is how long the tool ran:

```
on('tool.call', async ($, e, next) => {
  const id = start(e.tool); // record name + start time in module state
  $.ui.invalidate('ui.render'); // redraw the row now
  try {
    return await next(e); // let the tool run as usual
  } finally {
    finish(id); // move it to history with its duration
  }
});
```

**What we learned:** the MCP tab turned out to be the most useful. It showed connectors with dozens of tools that a blog repository never uses, and every one of those tools is sent with every request. The pane opens by itself when a session starts, but only if the terminal is wide enough to fit it.

## 2\. Context meter: how full is the window, and how much effort is Claude using?

**The problem:** you usually find out the context is full when Claude compacts it, and by then you can no longer choose what to drop.

**The row:** context used as a percentage, a sparkline of the last 12 turns, and the effort level with an arrow on each side it can move: **▼ medium ▲**, or just **auto ▲** at the bottom of the ladder. Click ▼ to lower effort for quick tasks, or ▲ to raise it for harder ones. No need to type `/effort`.

**The pane (`/ctx`):** the window split by category (tools, memory files, skills, system prompt, MCP), the trend per turn, the full effort ladder, and how much the on-demand MCP tool schemas would add if loaded.

![Context pane showing 6 percent of a one million token window used, with system tools the largest category.](/images/claude-code-mods/context.webp)

The Context pane: 56.1k of a 1M window used, split by category, with the effort ladder below.

Claude Code sends fresh usage numbers as an event, so the mod never has to estimate:

```
on('session.measure', async ($, e, next) => {
  const r = await next(e);
  const usage = await $.session.usage(); // context use and plan limits
  pushTurn(contextPercent(usage)); // feeds the 12-turn sparkline
  $.ui.invalidate('ui.render');
  return r;
});
```

**What we learned:** the breakdown is more useful than the total. Before the first prompt, our window already held 56,000 tokens, and almost half of that was tool definitions. That is why we now trim MCP servers per project. It is the same cost problem covered in [how many tokens AI coding agents burn](/blog/ai-coding-agent-token-usage/).

## 3\. Repo state: which branch, issue and PR am I on?

**The problem:** you and Claude can lose track of where you are. You have switched to the feature branch, but Claude is still working as if you were on main.

**The row:** the branch, the issue it refers to (our branch names start with the issue number), the PR if there is one, and whether the dev server is running.

**The pane (`/stack`):** a **Stack** tab that starts and stops `pnpm dev` with one key, and a **Branch** tab with the issue and PR titles.

![Repo pane showing branch main, no linked issue and no pull request.](/images/claude-code-mods/repo.webp)

The Repo pane on main: no issue, no PR of its own, dev server down.

This is the part worth copying. The mod does not only show you the facts, it also passes them to Claude:

```
on('prompt.compose', async ($, e, next) => {
  const facts = await repoFacts($); // branch, issue, PR, dev server, cached
  if (!facts) return next(e);
  return next(withSection(e, `Repo state: ${facts}`)); // append, never replace
});
```

**What we learned:** a few dozen tokens of facts in the system prompt is the cheapest way we know to avoid “which branch is this?” mistakes. Local state is refreshed every 30 seconds and after any Bash call. Anything that needs GitHub is refreshed every five minutes, because `gh` is slow and rate-limited.

## 4\. PR and CI: are the checks green, and who is waiting on me?

**The problem:** you switch to the browser to watch the checks, and miss a review comment that arrives in the meantime.

**The row:** something like `PR #42 draft · CI 4/5 · 2 threads`.

**The pane (`/ci`, `/threads`):** a **Checks** tab with each check’s state, duration and log link, and a **Threads** tab listing unresolved review threads where someone else replied last.

![PR pane with Checks and Threads tabs, loading the unresolved review threads for the branch.](/images/claude-code-mods/pr-threads.webp)

The PR pane's Threads tab, reading review threads for the checked-out branch.

The data comes from the GitHub CLI, run through the mods API, which uses no shell and does not throw on a failed exit code:

```
const r = await $.process.run(['gh', 'pr', 'checks', '--json', 'name,state,bucket']);
const checks = r.exitCode === 0 ? JSON.parse(r.stdout) : [];
$.ui.status(`CI ${passed(checks)}/${checks.length}`);
```

**What we learned:** two details made this one worth keeping. `/ci` switches to faster polling and shows a notification as each check finishes, so you do not have to keep watching. And the mod registers the threads as a [tool Claude can call](https://code.claude.com/docs/en/plugins/mods/api), so asking Claude to “fix the open review comments” just works. A failed `gh` call shows up as a line of text in the pane rather than a crash, because a monitoring mod that breaks your session is worse than none.

## 5\. Explain: what just happened, in plain words?

**The problem:** a long turn ends with a wall of tool calls, when all you want is a short summary of what changed and what to check.

**The row:** `explain`, a reminder that it gives a plain-language recap.

**The pane (`/explain`):** a recap of the last turn or the whole session under four fixed headings (what you asked, what was done, what to check next, one-sentence summary), the list of changed files from git, and one-key follow-ups such as “explain that more simply.”

![Explain pane offering a recap of this turn or the whole session, empty before any work.](/images/claude-code-mods/explain.webp)

The Explain pane before the first prompt: nothing to recap yet.

This is the only mod that calls a model. It uses `$.model.fork`, which according to the [mods API documentation](https://code.claude.com/docs/en/plugins/mods/api) asks a side question about the current conversation with the same model and system prompt, so most of it is served from the prompt cache. The main chat never sees the question or the answer:

```
const r = await $.model.fork({
  prompt:
    'Recap the last turn for someone who never saw the code. ' +
    'Headings: What you asked / What I did / What to check next / In one sentence. ' +
    'Do not call tools.',
});
if (r.isAnswered) saveRecap(r.text);
```

**What we learned:** a fork is much cheaper than a fresh model call, which would need the whole transcript sent again at full price. Pressing 5 only shows the latest recap. A new one is written only when you ask for it, so looking costs nothing.

## 6\. Memory: is my memory index actually loading?

**The problem:** auto memory loads only the first 200 lines or 25KB of `MEMORY.md`, whichever comes first, according to [Claude Code’s memory documentation](https://code.claude.com/docs/en/memory). Anything past that is dropped without a warning, and Claude simply stops remembering it.

**The row:** the number of memory files and the index size against the 25KB limit.

**The pane (`/memory-files`):** an **Index** tab that marks each line as loaded or cut, a **Files** tab grouped by type, and a **Read** tab for viewing one file.

![Memory pane showing the index at 22.2 of 25 kilobytes and 110 of 200 lines, all loaded.](/images/claude-code-mods/memory-index.webp)

The Memory pane: the index at 22.2 KB of 25.0 KB and 110 of 200 lines, all loaded.

The check itself is one file read and two comparisons:

```
const text = await $.fs.read(indexPath); // ~/.claude/projects/<project>/memory/MEMORY.md
const bytes = new TextEncoder().encode(text).length;
const lines = text.split('\n').length;
const cut = bytes > 25_000 || lines > 200;
```

**What we learned:** this mod alone justified building the set. Our index was at 22.2KB of 25KB. Without this row, the first sign of trouble would have been Claude ignoring a rule it had followed for months, and we would probably have blamed the model. The mod is read-only on purpose: it shows the problem and leaves the cleanup to you.

## 7\. System: is my machine the bottleneck?

**The problem:** with five Claude sessions, two dev servers and a browser open on a 16GB laptop, everything slows down, and you want to know which process to close.

**The row:** RAM used out of total, free memory, temperature, and how many ports are listening.

**The pane (`/system`, `/ports`):** a **RAM** tab with the same breakdown Activity Monitor shows and the biggest processes, and a **Ports** tab listing every TCP listener and the folder it runs from. Every row has a Kill button.

![System pane showing memory split into app, wired, compressed and cached, with Claude sessions as the biggest processes.](/images/claude-code-mods/system-ram.webp)

The System pane's RAM tab: 13.9 GB of 17.2 GB used, with the largest processes and a Kill button on each.

![Ports tab listing sixteen TCP listeners, with this repository's dev server and sessions grouped first.](/images/claude-code-mods/system-ports.webp)

The Ports tab groups this repository's listeners (the Astro dev server on :4321 and the Claude sessions) apart from everything else.

The readings come from standard OS commands (`vm_stat`, `ps`, `lsof` on macOS), run every five seconds, with the heavier ones every sixth pass. Most of the care goes into the Kill button, so a mod can never stop its own session:

```
async function kill($, pid) {
  if (isOwnSessionOrAncestor(pid) || isSystem(pid)) return; // never offered
  if (!(await confirm($, `Stop ${name(pid)}?`))) return;
  await $.process.run(['kill', '-TERM', String(pid)]);
  await $.clock.sleep(3000);
  if (alive(pid) && (await confirm($, 'Still running. Force it?')))
    await $.process.run(['kill', '-KILL', String(pid)]);
}
```

**What we learned:** five of the eight largest processes on the machine were Claude sessions, at 300 to 380MB each. The Ports tab gets used the most, though. When `:4321` is “already in use,” it is almost always a dev server that an earlier session left running.

## 8\. Usage and model: how much plan is left, and which model am I on?

**The problem:** you only notice the 5-hour and 7-day limits when you hit them, and the day’s cost is spread across several sessions.

**The row:** the current model, the 5h and 7d window percentages, and today’s cost.

**The pane (`/models`, `/limits`):** two tabs. The **Model** tab (pictured at the top of this post) lists every model with a **Use** button. Clicking it runs the built-in `/model` for you, so the choice holds for the session and shows everywhere `/model` does. The **Usage** tab has a meter per window, its reset time, today’s cost and a 14-day sparkline.

![Usage pane with rate-limit meters at 6 and 31 percent and a fourteen-day cost sparkline totalling 26.71 dollars.](/images/claude-code-mods/usage.webp)

The Usage pane: 6% of the 5-hour window and 31% of the 7-day window used, $7.41 today and $26.71 over 14 days at list price.

Each session saves its own cost under a per-day key, so today’s total adds up across every open session:

```
const day = dayKey(await $.clock.now()); // local date, so midnight rolls over cleanly
await saveCost($, `cost:${day}:${sessionId}`, sessionCost); // $.store, one key per session
const todayTotal = await sumCosts($, day); // every session's key for today
```

**What we learned:** two things. First, the command is `/limits` rather than `/usage`, because a mod [cannot register a built-in command name](https://code.claude.com/docs/en/plugins/mods/api): the registration throws, and the rest of that hook never runs. Second, the Model tab warns you before switching once the context passes 50,000 tokens. A new model cannot reuse the previous model’s prompt cache, so the next request sends the whole context again at full price. The costs are list-price estimates, the same numbers `/cost` shows, and our [explainer on the /cost command](/blog/claude-code-cost-command-explained/) covers what they mean on a subscription.

## 9\. Changelog: what changed in the Claude Code version I am running?

**The problem:** Claude Code ships a release most days. A fix you were waiting for, or a change to how mods load, can land without you noticing, and the changelog lives on GitHub, outside the terminal.

**The chip:** `☰ changelog` at the end of the usage row, with a `new` badge and the version number when Claude Code updated since your last session.

**The pane (`/changelog`):** two tabs. **Release** opens the version you run, marked “you run this,” with keys to step to older and newer releases. **Releases** lists the last 80, flagging the one you run, the ones new since your last session and the ones you have not installed yet.

![Changelog pane showing Claude Code release 2.1.289 with 27 changes, tagged you run this, beside the numbered status rows with a changelog chip on the last row.](/images/claude-code-mods/changelog-pane.webp)

The Changelog pane on v2.1.289, the release this session runs: 27 changes, one of the last 80 releases. The chip that opens it sits at the end of the bottom row.

It reads Anthropic’s own [Claude Code changelog on GitHub](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md), caches it for an hour, and compares the version you run with the one the last session saw:

```
const cached = await $.store.get('releases'); // { releases, fetchedAt }
if (!cached || now - cached.fetchedAt > HOUR) {
  const res = await $.http.fetch(CHANGELOG_URL); // raw CHANGELOG.md from GitHub
  await $.store.set('releases', { releases: parse(await res.text()), fetchedAt: now });
}
const isUpdated = compareVersions(installed, lastSeen) > 0; // drives the "new" badge
```

**What we learned:** this is the one mod in the pack that goes online, so it fails soft. If the fetch fails, the pane says so and keeps showing the last good copy. The “you run this” marker turned out to matter more than the list: it answers “do I have that fix yet?” without opening a browser.

## 10\. Worktrees: which checkouts do I have, and can I open one?

**The problem:** with [git worktrees](https://git-scm.com/docs/git-worktree) you can run one Claude session per branch side by side. After a week you have several checkouts and no memory of which are stale, which have changes and which branch was already merged.

**The chip:** `⑂ 2 worktrees` at the end of the repo row, with a `here` badge when the session runs inside a linked worktree and a busy badge while one is being set up.

**The pane (`/worktrees`):** every checkout as a card, with **Open** and **Remove** buttons. An Add field takes a branch name or PR number and creates the worktree with its install and build. **Open** starts `claude` in a new terminal window in that folder. **Remove** asks first, and **p** prunes the branches already merged.

![Worktrees pane listing two checkouts with Open and Remove buttons and Add, Prune and Refresh controls, beside status rows ending in a 2 worktrees chip.](/images/claude-code-mods/worktrees-pane.webp)

The Worktrees pane: two checkouts, each with Open and Remove, and an Add field that takes a branch or PR number. Branch names are blurred.

The list comes straight from git, and it is read again after every turn, because Claude may have added or removed a worktree from the shell:

```
on('turn.complete', async ($, e, next) => {
  const r = await next(e);
  const out = await $.process.run(['git', 'worktree', 'list', '--porcelain']);
  await save($, parsePorcelain(out.stdout)); // path, branch, HEAD per checkout
  $.ui.invalidate('ui.render'); // refresh the chip count
  return r;
});
```

**What we learned:** a chip is harder than a row. Neither of these two mods owns the row it sits on, so the chip has to land at the end of another mod’s line whichever loads first, and the row’s owner has to leave space for it. The repo row cuts its PR title short to fit the worktree chip. A second lesson: the row strip above the prompt is one shared tree that every mod adds to, and if one mod passes a single invalid value (an unknown colour name, for example), Claude Code throws the whole tree away and every row disappears at once. Every row and pane now has a test that mounts it.

## What should you know before building your own?

Five rules from building these mods

Watch, do not block: a monitoring mod should call `next(e)` every time, and anything that rewrites or denies belongs in a separate guard mod. Poll in tiers: cheap local reads every few seconds, `gh` and network calls every few minutes, plus an immediate refresh after a Bash call that changed something. Show failures in the pane: wrap every process call and display errors as text, because a hook that throws is skipped along with everything after it. Check command names against the built-ins: `/usage`, `/context` and `/memory` are already taken. Keep each row to one number: if a row needs explaining, the explanation belongs in its pane.

Two more things to know. The mods API is the only way a mod can reach the outside world: there is no Node, no `setTimeout` and no direct file access, so timers go through `$.clock` and files through `$.fs`. And `$` cannot be passed through an import, so shared helpers take it as an argument. We copied one small UI kit (tabs, cards, meters, sparklines, buttons) into each mod so every pane looks consistent.

## How do you start your own monitoring mod?

Start small, with a single row above the prompt. Create the three files from the layout above, then begin `register.tsx` like this:

```
let calls = 0;

export const register = (on) => {
  on('tool.call', async ($, e, next) => {
    calls += 1;
    $.ui.invalidate('ui.render');
    return next(e);
  });
  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
    const below = await next(e);
    return [row(`tools this session: ${calls}`), below]; // row(): your own drawing helper
  });
};
```

Load it for a single session with `claude --plugin-dir ./your-mod`, run `claude plugin validate ./your-mod` to see which events and calls it declares, and build from there. For example code, Anthropic’s [sample mods](https://github.com/anthropics/claude-code-playground/tree/main/claude-code/mods) (`token-weather` draws a context forecast above the prompt) and the [built-in mods’ source](https://github.com/anthropics/claude-code/tree/main/mods) are the best references. You can also describe the mod you want in a session and let Claude write it with the bundled plugin-authoring skill.

Not sure whether a feature should be a mod, a settings hook, a skill or an MCP server? Start with our [Claude Code harness guide](/blog/claude-code-harness-guide/), then see how the pieces fit together in [our own Claude Code harness](/our-claude-code-harness/).

## Frequently asked questions

Do Claude Code monitoring mods cost tokens?

Not unless they call a model. A mod runs as code inside Claude Code, so reading events, running git or gh, and drawing a pane use no tokens. Of the mods in this post, only Explain calls a model, through $.model.fork, and only when you press its key.

Can a mod change the effort level or model with a click?

Yes. In these mods the context row shows the effort level with arrows that step it up or down with a click, and the Model tab behind row 8 switches models by running the built-in /model command for you.

What is the difference between a Claude Code mod and a status line?

A status line is one line of text produced by a script you set in settings.json. A mod runs inside Claude Code, keeps state between events and can draw rows above the prompt and full panes with tabs and buttons. A mod can also set a status line of its own with $.ui.status.

Which Claude Code version do mods need?

Claude Code v2.1.287 or later, where mods are on by default. The screenshots in this post were taken on v2.1.288 and v2.1.289.

Can a mod see how full the context window is?

Yes. Claude Code fires a session.measure event with fresh usage, and $.session.usage() returns context window use and plan limits. The context meter and usage mods both read those numbers rather than estimating them.

Are the mods in this post open source?

Not at the moment. This post explains how each one works and shows simplified sketches of the patterns, which are enough to build your own version with the mods API.

## Sources

* Anthropic (2026). _Customize Claude Code with mods in TypeScript_. Claude blog. <https://claude.com/blog/claude-code-mods>
* Anthropic (2026). _Mods overview_. Claude Code documentation. <https://code.claude.com/docs/en/plugins/mods/overview>
* Anthropic (2026). _CHANGELOG.md_. Claude Code repository, GitHub. <https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md>
* Anthropic (2026). _Use the mods API_. Claude Code documentation. <https://code.claude.com/docs/en/plugins/mods/api>
* Anthropic (2026). _How Claude remembers your project_. Claude Code documentation. <https://code.claude.com/docs/en/memory>
* Anthropic (2026). _Customize your status line_. Claude Code documentation. <https://code.claude.com/docs/en/statusline>
* Anthropic (2026). _claude-code-playground: sample mods_. GitHub. <https://github.com/anthropics/claude-code-playground/tree/main/claude-code/mods>
