~/blog/claude-code-mods-resource-dashboard

AI Workflow · part 23

[Claude Code] Build a Resource Dashboard Inside Claude Code With Six Prompts

2026-10-04updated 2026-10-0615 min read#claude-code#mods#plugins#dashboard中文版
❯ cat --toc

TL;DR

Claude Code mods (v2.1.287+) let you customize the terminal or desktop app you already work in, so you can see what's going on at a glance. I used them to build a pane that shows which machines have capacity and which subscriptions still have quota. Is it hard? No. Ask Claude Code for a simple version built around whatever you check most often, then nitpick it like a demanding boss until you're happy. With the prompts in this post, the first pane took 2 min 51 s. Caveat: mods run with Claude Code's permissions and no sandbox.

Video version (5 min, Mandarin voiceover with English subtitles): the six prompts that built this dashboard.

Update (2026-10-06): I packaged this approach as an open-source repo, cc-dash-kit. If you'd rather not paste six prompts, paste this into Claude Code and it installs the dashboard: Install this Claude Code dashboard for me: https://github.com/coolthor/cc-dash-kit. Then ask Claude to customize it for your setup.

Crayon-style cover on off-white paper. Title: “Six prompts to your own resource dashboard”, subtitle “Claude Code mods · first pane in 2 min 51 s”. A smiling chibi terminal holds up a small panel with three progress bars, next to two smiling server boxes. Four colored cards on the right: GPU + disk (every 10 s), Claude quota (5-hour · weekly), Local models (listed if reachable), Before dispatch (Claude checks first). Bottom line: “No code by hand. Each prompt just loops on validate.”

Claude kept dispatching work to machines that couldn't take it

A sign at the parking garage entrance says level 1 is full and level 2 has 3 spots. You skip level 1 and head straight down.

My setup had no sign like that. I run models on a few machines and pay for a few AI subscriptions. When I asked Claude to dispatch a job, it would sometimes write up the whole task, send it, and only then find out the quota was used up, the machine was busy, or the machine was down. Then it had to dispatch all over again. That's the software version of circling every level.

Claude Code mods, supported since v2.1.287 (announcement), fix this. A mod can show the numbers in a pane for me, and Claude can fetch the same numbers with a tool call. I started by asking Claude Code which things I kept checking over and over, and let it suggest what to build. I split the result into six prompts here so each step is easy to follow. Paste them in order, nitpick the looks a bit, and you end up with a pane like this:

The my-dash pane after six prompts plus a styling prompt: four rounded cards with colored titles, blue for Claude quota, green for GPU memory, yellow for home disk, purple for local models. Quota bars are green, the disk bar is yellow, percentages sit in a right-aligned column, and a dim last-updated line and a Refresh now button run along the bottom.

I'm not going to ask you to copy my code. It's wired to my machines and would be useless on yours. The prompts in this post get Claude to build the same kind of pane for your own machine. I ran each one in a fresh session (claude -p) to make sure every prompt stands on its own; you can just as well paste all six into one session.

What a mod is, and why it has no sandbox

Think of a mod as an add-on for Claude Code: once it's loaded, your own panes and buttons show up right in the session. Claude writes all the code, so you don't need to know TypeScript. A mod can:

  • open a pane with text, progress bars, and buttons
  • give Claude a new tool it can call to look things up, the same way it runs commands
  • look at each action Claude is about to take, and block it if needed
  • add slash commands, such as /my-dash

Per the official docs, mods can also rewrite the prompts you send, intercept or retry tool calls, switch models per request, and change how Claude Code renders its own messages.

One caveat: a mod runs with the same permissions as Claude Code. There is no sandbox. It can read your environment variables and the entire conversation. It can also approve tools for you, burn through your subscription quota or run up charges on your API account, and start processes outside the Bash sandbox. Before installing someone else's mod, run claude plugin validate to see which events it hooks, which capabilities it calls, and which env vars it reads or writes. Then read the code: the official docs recommend installing mods only from authors and marketplaces you trust. The docs cover the full API.

Before you start: version 2.1.287 or later and an empty folder

Check your version:

claude --version

Mine printed 2.1.289. Any version from 2.1.287 on works. If yours is older:

claude update

Then make an empty folder and start Claude Code in it:

mkdir ~/my-dash-work && cd ~/my-dash-work
claude

One note on the prompts: I wrote them in Chinese first. The English versions below are the ones I ran for this post. They're more detailed than they need to be, so that each step reproduces what I got. When you build your own, you don't have to copy them word for word. Say what you want and let Claude fill in the details; over-specifying tends to cause more mistakes, not fewer.

Prompt 1: a GPU and disk pane in 2 min 51 s

Build a Claude Code mod that opens a pane showing two things:
1. GPU memory usage. If the machine has an NVIDIA GPU, read it with nvidia-smi. Otherwise show "No NVIDIA GPU".
2. Free space on the disk that holds my home directory.
Draw a usage bar for each. Refresh every 10 seconds. Put the mod in ./my-dash/.
Run `claude plugin validate` and keep fixing until it passes.
When you're done, tell me how to load it.

Claude took 2 min 51 s and produced this:

my-dash/
├── .claude-plugin/
│   └── plugin.json
├── hooks/
│   ├── hooks.json
│   └── register.tsx    # the pane, the /my-dash command, 10 s refresh
├── types/
│   └── index.d.ts
└── tests/
    └── dash.test.tsx

I didn't ask for tests. Claude wrote them anyway and tested them in both the terminal and the Desktop app. Claude Code's built-in mod-authoring instructions tell it to. When you change the mod later, run claude plugin test. It runs the mod's test files, which can fire real hooks and mount the pane on both surfaces; validate only checks statically. Neither verifies the visual layout, so open a live session for that.

Load the mod:

claude --plugin-dir ~/my-dash-work/my-dash

My MacBook has no NVIDIA GPU, so the pane showed "No NVIDIA GPU", plus about 235 GiB free of 926 GiB on the disk. If the pane doesn't open on its own, type /my-dash. Claude's summary says the pane auto-opens at launch only when the terminal is at least 144 columns wide.

Prompt 2: a Claude quota card in 134 s

Add a "Claude quota" card to the my-dash mod. Show 5-hour and weekly usage as percentages, plus the time until each resets. The mod can read the session's usage info.
Make every card title collapse or expand its card when clicked.
Run `claude plugin validate` until it passes.

The quota numbers come from the same usage data the status line uses. They show up only after you send your first message on a subscription account. Until then the card says "No quota reading yet", and on an API-key account it stays that way.

Prompt 3: a local model card in 154 s

This one just asks Claude to connect to your local model server and refresh every 10 seconds.

Add a "Local models" card to the my-dash mod. Every 10 seconds, poll http://localhost:8080/v1/models (an OpenAI-compatible API) and list the models it returns. If the server can't be reached, show "not running".
Put the URL in a constant at the top of the file.
Run `claude plugin validate` until it passes.

llama.cpp, vLLM, LM Studio, and Ollama all expose an OpenAI-compatible /v1/models endpoint, each on a different default port. Change the constant to match yours.

Claude only tested the card against a fake server. I had a model server on another machine forwarded to localhost:8080, and the mod picked up qwen38-27b-ud from it once loaded. It also handled cases I hadn't spelled out: a server that doesn't answer within 5 s counts as "not running", an error status shows up in red (for example "HTTP 500") so a broken server doesn't look like a stopped one, and an empty model list says "No models loaded".

Prompt 4: a tool Claude can call in 131 s

The pane looks great now, and you can see every resource at a glance. Claude Code still can't. So the next prompt has Claude build a small tool it can call to see what's free before dispatching.

Register a tool in the my-dash mod that Claude can call, named my_resources. It should return a very short summary of what's usable right now:
- Is the Claude quota OK?
- How much GPU and disk is left?
- Is the local model server up?
Run `claude plugin validate` until it passes.
Then tell me the tool's full name and how to make Claude check it before dispatching work.

Claude reports the tool's full name when it's done: mcp__my-dash__my_resources. That's the internal name Claude Code uses for the tool, always in the form mcp__<mod name>__<tool name>, and it's the name you use when writing instructions for Claude. Here is a real call from a fresh session:

Claude quota: OK (5-hour 52%, Weekly 7%)
GPU: none (no NVIDIA GPU on this machine)
Disk: 232.0 GiB free of 926.4 GiB
Local model server: UP (qwen38-27b-ud)

To make Claude use it, put one line in your project's CLAUDE.md, the instructions file Claude Code reads at the start of every session:

Before dispatching work, call mcp__my-dash__my_resources; don't dispatch to anything it reports as unavailable.

One mod reads the data once and feeds two outputs: a pane for you and a tool for Claude

The tool only has to answer one question: can I dispatch here? No quota, don't dispatch. Machine busy, don't dispatch. Anything more detailed just burns tokens. My own tool started out stuffing every detail into Claude's context; I later cut it down to three short yes/no sections: quotas, local models, and the ComfyUI image server I also run.

Prompt 5: a Pause dispatch button in 139 s

Why buttons at all? Mine do two jobs:

  1. Subscriptions: show which model Claude dispatches to by default, and let me switch it by hand.
  2. Local services (model servers, ComfyUI): when I'm using a machine to test a new model or tune settings, I need a flag that tells Claude to leave it alone.

This prompt builds the second one, plus a Refresh button:

On the local-models card of the my-dash mod, add a "Pause dispatch" button. Clicking it creates a `paused` flag file under my-dash/ and shows "Paused" next to the card title; clicking again deletes the flag and goes back to normal. When the flag exists, the my_resources tool must report the local models as "paused, do not dispatch".
At the bottom of the pane, add a "Refresh now" button that re-reads all the data immediately.
Run `claude plugin validate` until it passes.

With the flag set, I asked Claude in a fresh session to call the tool, and the local-models line came back as paused, do not dispatch. A button runs an action on your machine, so instead of writing a flag it could just as well open a page or run a script.

Prompt 6: SVG bars for the Desktop app in 172 s

The same mod renders in the Desktop app's Claude Code tab mostly unchanged. The one thing that breaks is text-based progress bars. In the terminal, every █ takes exactly one cell. The Desktop app uses a proportional font, so the same row of characters comes out wider and covers the numbers next to it. I wrote this prompt after watching my own pane break that way.

Make the my-dash mod look right in the Claude Desktop app. Desktop uses a proportional font, so text-character bars overflow and cover the numbers.
On Desktop, draw the bars as SVG. Leave the terminal version unchanged.
Run `claude plugin validate` until it passes.

Claude hit a snag here and fixed it on its own. It first checked whether the element table included Svg. In the test environment the terminal's table has one too, so the terminal tests broke. Claude switched to checking whether the surface is terminal instead. The official element table lists Svg as Desktop-only.

Bonus prompt: make it look good in 224 s

After six prompts everything works, but every card is the same gray. This prompt is optional. Paste it if you want the pane from the top of this post:

Make the my-dash pane look polished. Give each card a rounded border and its own title color: blue for quota, green for GPU, yellow for disk, purple for local models.
Color each usage bar by how full it is: green under 70%, yellow from 70 to 90%, red above 90%. Let the bars stretch with the pane width, and right-align the percentages.
Dim the last-updated line at the bottom.
Apply this in both the terminal and the Desktop app.
Run `claude plugin validate` until it passes.

The run took 224 s. claude plugin validate passed, and so did all 34 tests Claude wrote. In a 150-column terminal the bars stretched to fill each card with the percentages lined up on the right, as in the screenshot at the top of this post. I didn't check the Desktop version this time.

One change it made on its own: since Prompt 2 you collapse a card by clicking its title, but buttons can't have colored text. So Claude split the header: the collapse arrow became its own small button, with the colored title as plain text beside it. You now collapse a card by clicking the arrow.

If something looks off when you open it, describe what you see in plain words and paste that as the next prompt, for example: "The usage bars are too long and the last few cells wrap onto the next line. Keep each bar and its percentage on one line at any window width." When I ran the same test with Chinese prompts, the bars did wrap, and a follow-up like that fixed it in 81 s.

Keep it loaded: one env var in settings.json

The easiest way is to tell Claude Code "make my-dash load automatically every time I start Claude Code" and let it edit the config for you. If you'd rather see what changes, or do it by hand, this is what goes into ~/.claude/settings.json:

{
  "env": {
    "CLAUDE_CODE_PLUGIN_DIRS": "/Users/you/my-dash-work/my-dash"
  }
}

Separate several mods with colons. The Desktop app can't take --plugin-dir, so it relies on this env var too.

After a code change, an open terminal session picks it up as soon as you save. Desktop sessions don't watch the folder: open a new one, or add "CLAUDE_CODE_PLUGIN_DIR_WATCH": "1" to the same env block.

Deep dive: what my own dashboard reads, and what broke while building it

You can skip this section; the guide above is complete without it. It covers the bugs I hit along the way.

Seven things that broke

  1. CJK text misaligned. I padded columns with padEnd, but a CJK character takes two cells. Fix: a fixed-width Box per column.
  2. viewport.columns is the width of the whole window, not the pane. Fix: drop the explicit width and let flex fit the content. The pane then follows whatever width you drag it to. If you need the exact width, the docs recommend e.props.bodyColumns, the width of the pane's content area.
  3. claude plugin validate rejected three things. Two unfiltered hooks on the same event (merge them into one). Functions that use $ must be top-level. Atom state must be declared in the types file, or validate reports it as "is not declared".
  4. A shadowed h killed the whole pane. In the machines card, hosts.map(h => …) shadowed the JSX h. Whenever a machine snapshot was present, the entire pane threw and rendered nothing. The tests passed because they never fed in a snapshot. I only found it in a real terminal.
  5. Character bars overflowed on Desktop. Fix: choose by e.surface, and switch to SVG when the surface is not terminal.
  6. Three sessions edited one file. One session made all card borders gray, a second kept editing on top of that, and the third switched them back to color. There is one register.tsx and the last save wins. Edit a mod from one session, and commit.
  7. Claude Code updated itself mid-build. The mods API is marked early access, so an update can change it under you. Setting autoUpdates: false in ~/.claude.json didn't stop an update from 2.1.288 to 2.1.289. Adding this to settings.json did:
{
  "env": {
    "DISABLE_AUTOUPDATER": "1"
  }
}

With that set, I update manually with claude update.

FAQ

Which version of Claude Code supports mods?
Mods are supported from Claude Code v2.1.287 and are on by default. Check with claude --version and run claude update if yours is older. The Desktop app ships its own bundled engine and updates along with the app.
Can I build a mod without knowing TypeScript?
Yes. Describe the pane you want, and Claude writes the code (TypeScript or JavaScript), runs claude plugin validate until it passes, and tells you how to load it. The six prompts in this guide were each tested in a fresh session and took between 83 and 172 seconds each.
How do I make a mod load automatically?
Use --plugin-dir to try it. To keep it, add CLAUDE_CODE_PLUGIN_DIRS to the env block of ~/.claude/settings.json; Desktop sessions read it too. An open terminal session watches the mod folder and reloads on save. An open Desktop session doesn't: start a new one, or set CLAUDE_CODE_PLUGIN_DIR_WATCH=1 in the same env block.
Are Claude Code mods safe to install?
A mod runs with the same permissions as Claude Code and there is no sandbox, so it can read environment variables and the whole conversation. Run claude plugin validate before installing someone else's mod; it lists the events the mod hooks, the capabilities it calls, and the env vars it reads or writes. That's a static check, not a runtime safety audit, so still read the code and install mods only from sources you trust.

Read next

Don't miss the next one

Subscribe, and you won't.

One-click unsubscribe anytime.