Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,13 @@
"prose",
"skill"
]
},
{
"name": "user-interface",
"source": "./plugins/user-interface",
"category": "presentation",
"defaultEnabled": false,
"tags": ["user-interface", "ui", "terminal", "cli", "tui", "design-system", "accessibility", "skill"]
}
]
}
1 change: 1 addition & 0 deletions docs/catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ plugin manifests and kept in sync by CI. Never hand-edit it; the category vocabu
- [`playgrounds`](../plugins/playgrounds): One-step access to Anthropic's first-party playground plugin: declares the cross-marketplace dependency, routes playground-shaped requests to the upstream skill when it is installed, emits the install commands when it is not, and carries field-tested prompt recipes, cloud-session delivery guidance, and consumer cautions. Generates nothing itself.
- [`visualization`](../plugins/visualization): Visualization skills. visualize picks the form (mermaid diagram, markdown table, SVG/CSS chart, ASCII/Unicode art, or a rendered page) and the medium (terminal, local HTML file, or published Artifact) for what is in the conversation, asking only when the target is ambiguous. present builds a slide deck from the account's claude.ai Slides Artifact type behind a publish gate, with a markdown outline as the record. Chart craft and artifact design route to those capabilities when installed.
- [`writing`](../plugins/writing): Prose a scanning reader can use. /writing:be-concise puts the bottom line first, cuts words the meaning does not need, and keeps structure scannable and tone factual. Bare, it sets a standing posture; given a target, it reshapes it and reports word counts. For tickets, PR descriptions, changelogs, READMEs, and status updates. Never drops a decision, number, ask, error, or warning. Paraphrases NN/g, GOV.UK, US plain-language guidelines, Google and Microsoft style guides, and BLUF.
- [`user-interface`](../plugins/user-interface): Front door for interface design: terminal and CLI output, TUIs, prompt themes, Claude Code mods, web and app UI. design detects the project's own design system and the design tools installed, routes each concern to the best present source with the project first, and supplies its own guidance where nothing covers the concern. Terminal guidance is its own contribution.

## Project Management

Expand Down
3 changes: 3 additions & 0 deletions docs/conventions/mod-authoring/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ mod, one mod per plugin, the 2.1.287 floor, and what is committed.

- **The API itself** (events, `$` methods, render sites, limits): the built-in `plugin-authoring`
skill and the upstream pages below. This doc restates none of it.
- **How a mod's display looks** (which surface carries which message, rows, width, color,
wording, the off switch): `/user-interface:design`, whose mod guidance builds on its terminal
guidance (`NO_COLOR`, non-TTY output, plain-text fallbacks).
- **Settings hooks** (`hooks` in `hooks.json`): the `hook-*` conventions, starting with
[hook-budget](../hook-budget/README.md).
- **Three `hook-*` conventions also bind a mod**: what it tells Claude, by the frequency and
Expand Down
4 changes: 2 additions & 2 deletions docs/conventions/rendered-views/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,8 @@ helpers of the loop-closure reference and fails on a dropped case that lints cle
## Accessibility floor

The floor (contrast pairings, focus visibility, color-scheme and reduced-motion behavior,
keyboard reach) lives in the shared chrome reference and is provisional until the
design-system vertical revisits it cross-genre. Accessibility is a named, sanctioned
keyboard reach) lives in the shared chrome reference, as the web instance of the
medium-neutral floor `/user-interface:design` applies to every interface. Accessibility is a named, sanctioned
reason to prefer markdown over a rendered view: when a reader's tooling or needs make the
markdown record the better deliverable, flipping back is conformant, not a deviation.

Expand Down
1 change: 1 addition & 0 deletions docs/skill-cheat-sheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ owned by [docs/catalog-taxonomy.md](catalog-taxonomy.md).
| [`/testing:check`](../plugins/testing/skills/check/SKILL.md) | `testing` | Report whether node and jq resolve for the testing hooks. Never installs. |
| [`/toolchain:check-prerequisites`](../plugins/toolchain/skills/check-prerequisites/SKILL.md) | `toolchain` | Report whether the tools toolchain declares resolve. Never installs. |
| [`/typos-format:check`](../plugins/typos-format/skills/check/SKILL.md) | `typos-format` | Report whether typos and node are installed. Never installs. |
| [`/user-interface:design`](../plugins/user-interface/skills/design/SKILL.md) | `user-interface` | Design interfaces from the project's system and installed tools; terminal guidance built in |
| [`/visualization:present`](../plugins/visualization/skills/present/SKILL.md) | `visualization` | Slide deck through the claude.ai Slides Artifact type, outline markdown as the record |
| [`/visualization:visualize`](../plugins/visualization/skills/visualize/SKILL.md) | `visualization` | Pick the best visual form for what is in the conversation and render it |
| [`/wizard:generate`](../plugins/wizard/skills/generate/SKILL.md) | `wizard` | Author a hardened interactive bash wizard for human-only setup, credential, and cutover steps |
Expand Down
29 changes: 29 additions & 0 deletions plugins/user-interface/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "user-interface",
"version": "0.1.0",
"description": "Front door for interface design: terminal and CLI output, TUIs, prompt themes, Claude Code mods, web and app UI. design detects the project's own design system and the design tools installed, routes each concern to the best present source with the project first, and supplies its own guidance where nothing covers the concern. Terminal guidance is its own contribution.",
"author": {
"name": "Melodic Software",
"email": "info@melodicsoftware.com"
},
"license": "MIT",
"keywords": [
"user-interface",
"ui",
"terminal",
"cli",
"tui",
"design-system",
"accessibility",
"routing",
"skill"
],
"userConfig": {
"account_tools_enabled": {
"type": "boolean",
"title": "Account-bound tools",
"description": "Route to tools that need an account, login or API key (Claude Design, Figma, axe) when installed and reachable; false uses only account-free tools. Paid tools only when nothing free fits. No effect yet: every account-bound route is still deferred until it is tested.",
"default": true
}
}
}
24 changes: 24 additions & 0 deletions plugins/user-interface/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Changelog

All notable changes to the `user-interface` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.1.0] - 2026-10-04

### Added

- `design` skill: detects the project's design system and installed design tools, then routes each
interface concern to the best present source, project first. It keeps to the project's system
when a request conflicts with it, names the target platform with a fallback when the request
leaves it open, applies theme-safe color and display-width rules, and hands a user-only skill
over as its slash command.
- `reference/routing.json` and its schema: 13 interface concerns, this repository's skills ranked
first, Mac-only and account-bound routes deferred. Rows tested by install and a routed prompt
are marked `confirmed`. No route follows instructions fetched from a mutable branch.
- `scripts/detect.mjs`: the project's design signals, the installed routes (local-scope installs
for this project included), and which are reachable.
- Guidance: `reference/principles.md` (working order, heuristics, accessibility floor) and
`reference/types/` for terminal, mods, web and app. `types/web.md` reads the Web Interface
Guidelines checklist at a pinned commit, as data.
- `account_tools_enabled` option, "Account-bound tools", default on.
- Evals: 10 cases covering terminal rules, project-first routing, and platform targeting.
109 changes: 109 additions & 0 deletions plugins/user-interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# user-interface

Front door for interface design. An agent building anything a person or agent interacts with (CLI
output, a TUI, a prompt theme, a Claude Code mod, web or app UI) gets guidance that keeps the
project's existing look, reuses the design tools already installed, and fills only the gaps.

| Skill | What it does |
|---|---|
| `/user-interface:design` | Detect the project's design system and the installed design tools, route each concern to the best present source, and supply the plugin's own guidance where nothing covers it |

## How it routes

For each concern the order is:

1. The project's own design system, conventions and MCP servers.
2. This repository's enabled plugins.
3. Official publishers.
4. Community tools, with adoption breaking ties.
5. This plugin's own guidance.

The routes live in [`reference/routing.json`](reference/routing.json), validated by
[`reference/routing.schema.json`](reference/routing.schema.json).
[`scripts/detect.mjs`](scripts/detect.mjs) reports what the project and machine have, and which
installed routes are reachable.

## Guidance

- [`reference/principles.md`](reference/principles.md): the working order, the usability heuristics,
and the accessibility floor every medium keeps.
- [`reference/types/`](reference/types/): one file per interface type. The skill reads the file
that matches, so a new type is a new file.
- [`terminal.md`](reference/types/terminal.md): CLI output, TUIs, prompt themes, PowerShell,
banners; `NO_COLOR`, non-TTY output and plain-text fallbacks.
- [`mods.md`](reference/types/mods.md): Claude Code mod displays, building on terminal.md.
- [`web.md`](reference/types/web.md) and [`app.md`](reference/types/app.md): project-first rules
and pointers to platform guidelines.

## Options

<!-- BEGIN GENERATED: plugin options. Edit plugin.json, then run scripts/sync-plugin-options-docs.py -->

### Options reference

Generated from this plugin's `.claude-plugin/plugin.json`. Every option Claude Code
will prompt for when the plugin is enabled, with the environment variable each hook
reads it from.

| Option | Type | Default | Environment variable | Description |
| --- | --- | --- | --- | --- |
| `account_tools_enabled` | boolean | `true` | `CLAUDE_PLUGIN_OPTION_ACCOUNT_TOOLS_ENABLED` | Route to tools that need an account, login or API key (Claude Design, Figma, axe) when installed and reachable; false uses only account-free tools. Paid tools only when nothing free fits. No effect yet: every account-bound route is still deferred until it is tested. |

### How to set these

Three supported routes, in the order most people want them:

1. **Interactively.** Claude Code prompts for declared options when you enable the
plugin. To change them later: `/plugin configure user-interface@<marketplace>`.
2. **Headless.** Repeat `--config` for each option. Replace
`<marketplace>` with the marketplace you installed this plugin from:

```shell
claude plugin install user-interface@<marketplace> -s <scope> --config account_tools_enabled=<value>
```

The same command reconfigures a plugin that is **already installed**: it prints
`already installed` and still writes the value. The short-circuit message is
about the install, not the config write. Do **not** `claude plugin uninstall` to
reconfigure: uninstalling drops this plugin's whole stored `pluginConfigs` entry,
resetting every option in the table above to its default. `-s` defaults to `user`,
so pass the scope `claude plugin list` reports for this plugin. The verified-version
record lives in the [plugin-reconfiguration convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/conventions/plugin-reconfiguration/README.md).

The value is stored immediately; the session you are in does not change. Hooks are
handed their `CLAUDE_PLUGIN_OPTION_*` when the session starts, so start a fresh
Claude Code session before expecting new behavior. A check run in the old session
still reports the old value, and that is not a failed write.

3. **By hand, in settings.** Add the value under `pluginConfigs` in your **user**
settings (`~/.claude/settings.json`):

```json
{
"pluginConfigs": {
"user-interface@<marketplace>": {
"options": {
"account_tools_enabled": <value>
}
}
}
}
```

Plugin option values are read from **user**, `--settings`, and managed settings
only, **not** from a project's `.claude/settings.json`. To vary behavior per
repository, enable or disable the plugin in that project's `enabledPlugins`
instead of setting an option there.

Do not set the `CLAUDE_PLUGIN_OPTION_*` variables yourself. They are how Claude Code
hands a configured value to a hook process; the value comes from the routes above.

### Upstream documentation

- [User configuration](https://code.claude.com/docs/en/plugins/manifest-reference#user-configuration): the `userConfig` schema and the `CLAUDE_PLUGIN_OPTION_<KEY>` export
- [Plugin install options](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install): the `--config` flag's reference entry
- [Plugins and skills settings](https://code.claude.com/docs/en/settings-reference#plugins-and-skills): `enabledPlugins`, `extraKnownMarketplaces`, `pluginConfigs`
- [Settings files and who they affect](https://code.claude.com/docs/en/settings#settings-files-and-who-they-affect): user vs project vs local precedence
- [Manage installed plugins](https://code.claude.com/docs/en/discover-plugins#manage-installed-plugins): enabling, disabling, `/plugin list`

<!-- END GENERATED: plugin options -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
schema_version: "1.1"
name: conflicting-request-project-wins
description: "Hard (the request itself asks for what the project's design system forbids): the project's rules win, and the conflict is named"
tags: [project-first, hard]
runs: 3
expected_outcome: "The answer names the conflict with docs/design-system.md (no illustration on empty states, no gradients), designs within the system, and offers the user's idea as a proposed change to the system instead of silently applying it"
context:
scaffold_script: scaffold.sh
execution:
prompt: "Add a friendly illustration and a gradient 'Create invoice' button to the Invoices empty state in this app. Give the layout and the exact styling values. This run cannot write files, so put the design in your reply."
allowed_tools: [Read, Glob, Grep, Skill]
max_turns: 25
timeout_seconds: 600
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
flags: i
arm: both
pattern: "design-system\\.md|design system (?:says|rules?|forbids|doesn'?t allow|does not allow|calls for)|(?:no|without an?) illustration|never gradients|no gradients?"
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
type: llm
arm: both
---
PASS only if the design keeps to the project's documented rules (no illustration on the empty state,
no gradient, primary color brand.pine, one muted line and a secondary button) and the answer tells
the user their request conflicts with those rules, offering the illustration or gradient as a
proposed change to the design system for them to decide. FAIL if the design includes the
illustration or the gradient as the delivered design, or never mentions the conflict.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
type: tool_used
tool: Skill
input_match: "\"skill\"\\s*:\\s*\"(?:user-interface:)?design\""
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
flags: i
arm: both
pattern: "0B6E4F|brand\\.pine|\\bpine\\b|tokens\\.json|space\\.gutter|radius\\.card|18px"
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"pass": [
{
"answer": "docs/design-system.md rules out illustrations on empty states, so I left it out.",
"why": "names the file"
},
{
"answer": "Your design system calls for no illustration and never gradients.",
"why": "names the rule"
}
],
"fail": [
{
"answer": "Here is a friendly illustration with a pine-to-teal gradient button.",
"why": "complies silently"
},
{
"answer": "Add the illustration above the text.",
"why": "no conflict named"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"pass": [
{
"answer": [
{
"tool": "Skill",
"input": {
"skill": "user-interface:design"
}
}
],
"why": "the design skill fired"
}
],
"fail": [
{
"answer": [],
"why": "no tool call"
},
{
"answer": [
{
"tool": "Skill",
"input": {
"skill": "frontend-design"
}
}
],
"why": "another skill"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"pass": [
{
"answer": "Pad the card with space.gutter (18px) and use radius.card.",
"why": "token names"
},
{
"answer": "Primary button in brand pine #0B6E4F.",
"why": "token value"
}
],
"fail": [
{
"answer": "Use a soft blue #3B82F6 with a 12px radius.",
"why": "invented values"
},
{
"answer": "Follow Material defaults.",
"why": "no project values"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Seeds the eval workspace with a small app that has its own design system.
set -euo pipefail

mkdir -p design docs src
cat > package.json <<'JSON'
{ "name": "ledgerly", "private": true, "dependencies": { "@mui/material": "^6.1.0", "react": "^19.0.0" } }
JSON
cat > design/tokens.json <<'JSON'
{
"color": { "brand": { "pine": "#0B6E4F" }, "text": { "muted": "#5B6770" }, "surface": { "card": "#F7F5F0" } },
"space": { "gutter": "18px", "stack": "10px" },
"radius": { "card": "3px" },
"font": { "body": "Inter, sans-serif" }
}
JSON
cat > docs/design-system.md <<'MD'
# Ledgerly design system

- Colors, spacing, radius and type come only from `design/tokens.json`. No other values.
- Empty states: one line of text in `color.text.muted`, one secondary button, and no illustration.
- Cards: `surface.card` background, `radius.card` corners, `space.gutter` padding, no shadow.
- Buttons: MUI `Button`; primary actions use `color.brand.pine`, never gradients.
MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
type: regex
arm: both
pattern: "DictReader"
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
type: tool_used
tool: Skill
input_match: "\"skill\"\\s*:\\s*\"user-interface:design\""
min: 0
max: 0
arm: both
---
12 changes: 12 additions & 0 deletions plugins/user-interface/evals/control-no-trigger/prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
schema_version: "1.1"
name: control-no-trigger
description: "A non-interface Python question must not invoke the design skill"
tags: [control]
runs: 3
max_turns: 10
allowed_tools: [Read, Glob, Grep, Skill]
expected_outcome: "The answer names csv.DictReader and no Skill call reaches user-interface:design"
---

In Python, how do I read a CSV file into a list of dicts? Answer in under 80 words.
Loading
Loading