Example mod that copies in the tui-kit and its adapters, and draws every kit component in a pane and the status badge in a band.

Claude Code marketplace hosting the wf core plugin and its capability packs:
wf:* skill chain for spec → plan → implement → verify → QA → commit/PR, carrying zero stack, domain, or project knowledge.qa-execution engine provider) that core's /wf:qa-auto dispatches to.implement-phase test-authoring guidance).NO_COLOR/FORCE_COLOR handling, control-sequence sanitising, and a status badge rendered to plain text, ANSI and markdown, shipped as a copy-in template.verify lenses plus an optional composite retrospective, co-located with the pre-commit self-review lens that reuses the audit correctness rubric.In Claude Code:
/plugin marketplace add pavel-rp/wf-plugin
/plugin install wf
(Replace pavel-rp/wf-plugin with the repo's actual owner/name once pushed, or use a local path / full git URL.)
Then, in any git repo you want to work in:
/wf:init
/wf:init is per-repo: it scaffolds _local/ for per-task artifacts, adds gitignore entries, and writes the Node test runner. No per-machine setup is needed — Claude Code auto-discovers the plugin's skills and agents on install, and nested subagent delegation (e.g. wf:run→wf:phase-runner) works out of the box.
Each plugin's skills are invoked under its own namespace: core wf skills as /wf:<skill> (/wf:spec, /wf:plan, /wf:qa-auto, …), and each capability pack's skills as /<pack>:<skill> (skill names are bare; the namespace prefix comes from the plugin).
See plugins/wf/README.md for the full, authoritative core skill and subagent reference. In brief, the core skills cover triage → spec → plan → implement → verify → QA → commit/PR, plus a /wf:run dispatcher that drives the safe front of the chain hands-off. See each capability pack's README for the stack/domain capabilities that attach to the spine.
See CLAUDE.md for conventions when adding or editing skills and agents in this plugin.
hooks/register.mjs 121 lines1// Example mod: the tui-kit and its adapters copied into lib/, drawing every
2// kit component. `/tui-kit-demo` opens one pane per component; the band above
3// the prompt shows the status badge. On the terminal the checks list is
4// selectable; elsewhere it is display-only and says so.
5//
6// The host lets `$` reach only functions declared in this file, so every host
7// call is made here and the adapters get plain values: the element table from
8// `$.ui.resolve(e)`, the surface, and the profile built from the host reads.
9
10import { atom, read, update } from 'claude-code';
11
12import { defineBadge, definePanel, defineList, defineTable, defineMeter } from '../lib/kit/index.mjs';
13import { drawView, planPane, profileFromHost, shouldMount } from '../lib/adapters/index.mjs';
14
15const picked = atom({ plugin: 'tui-kit-example', key: 'picked' }, -1);
16
17const CHECKS = ['lint', 'tests', 'docs'];
18
19const PANES = Object.freeze({
20 'tui-kit-badge': { title: 'Badge', view: () => ({ kind: 'badge', def: defineBadge({ status: 'warn' }) }) },
21 'tui-kit-panel': {
22 title: 'Panel',
23 view: () => ({ kind: 'panel', def: definePanel({ title: 'Build', status: 'error', body: ['3 tests failed', 'see the log'] }) }),
24 },
25 'tui-kit-list': {
26 title: 'Checks',
27 view: () => ({
28 kind: 'list',
29 def: defineList({ items: [{ text: CHECKS[0], status: 'ok' }, { text: CHECKS[1], status: 'error' }, CHECKS[2]] }),
30 }),
31 },
32 'tui-kit-table': {
33 title: 'Table',
34 view: () => ({
35 kind: 'table',
36 def: defineTable({
37 columns: [{ key: 'check', header: 'Check' }, { key: 'result', header: 'Result' }, { key: 'ms', header: 'Time', align: 'right' }],
38 rows: [{ check: 'lint', result: { status: 'ok' }, ms: 12 }, { check: 'tests', result: { status: 'error' }, ms: 340 }],
39 }),
40 }),
41 },
42 'tui-kit-meter': {
43 title: 'Meter',
44 view: () => ({ kind: 'meter', def: defineMeter({ label: 'Disk', value: 8, max: 10, status: 'warn' }) }),
45 },
46});
47
48/**
49 * Read the host's accessibility values; a read that fails counts as unreachable.
50 *
51 * @param {any} $
52 * @param {{ screenReaderOff: boolean }} settings
53 */
54async function hostProfile($, settings) {
55 let configRows;
56 let hostSettings;
57 let screenReaderEnv;
58 try {
59 configRows = await $.config.list();
60 } catch {
61 configRows = undefined;
62 }
63 try {
64 hostSettings = await $.settings.read();
65 } catch {
66 hostSettings = undefined;
67 }
68 try {
69 screenReaderEnv = await $.env.get('CLAUDE_AX_SCREEN_READER');
70 } catch {
71 screenReaderEnv = undefined;
72 }
73 return profileFromHost({ configRows, settings: hostSettings, screenReaderEnv }, settings);
74}
75
76/** @type {import('claude-code').Register} */
77export const register = (on, options) => {
78 const settings = {
79 screenReaderOff: options.screenReaderOff === true,
80 panesDrawForScreenReader: options.panesDrawForScreenReader === true,
81 };
82
83 on('command.run', { command: 'tui-kit-demo' }, async ($) => {
84 const profile = await hostProfile($, settings);
85 const skipped = [];
86 for (const id of Object.keys(PANES)) {
87 const pane = PANES[id];
88 const plan = planPane(pane.view(), { ...settings, profile });
89 if (plan.isMounted) {
90 await $.ui.open({ id, title: pane.title });
91 } else {
92 for (const line of plan.lines) $.ui.log(line);
93 skipped.push(pane.title);
94 }
95 }
96 return { text: skipped.length === 0 ? 'Kit panes opened.' : `Kit panes skipped for the screen reader: ${skipped.join(', ')}.` };
97 });
98
99 on('ui.render', { component: 'Pane' }, async ($, e, next) => {
100 if (!Object.hasOwn(PANES, e.requestId)) return next(e);
101 const profile = await hostProfile($, settings);
102 const el = $.ui.resolve(e);
103 const view = PANES[e.requestId].view();
104 if (view.kind !== 'list') return drawView(el, e.surface, view, { profile });
105 const index = await read($, picked);
106 return el.Box({
107 flexDirection: 'column',
108 children: [
109 drawView(el, e.surface, { ...view, onSelect: (i) => update($, picked, () => i) }, { profile }),
110 el.Text({ dimColor: true, children: `Picked: ${index >= 0 && index < CHECKS.length ? CHECKS[index] : 'none'}` }),
111 ],
112 });
113 });
114
115 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
116 const profile = await hostProfile($, settings);
117 if (e.props.hasSurvey || !shouldMount(profile, settings)) return next(e);
118 return drawView($.ui.resolve(e), e.surface, PANES['tui-kit-badge'].view(), { profile });
119 });
120};
121types/index.d.ts 8 lines1export type TuiKitExamplePicked = number
2
3declare module 'claude-code' {
4 interface PluginState {
5 'tui-kit-example': { picked: TuiKitExamplePicked }
6 }
7}
8