tui-builder

tui-builder is a skill for Claude Code from OpenScribbler/syllago. It costs 45 tokens per session (1,331 once invoked), scanned A, original, Apache-2.0.

A guide for building and debugging Syllago's terminal user interface, or TUI—a text-based application controlled with a keyboard and mouse.

In plain words
What is it for?
Use it when changing files in the TUI to handle resizing, borders, text limits, mouse clicks, keyboard input, asynchronous work, and component updates.
Why use it?
It records the project's layout rules, component communication agreements, and known mistakes so interface changes behave consistently.

Skill for Claude Code

Written for Claude Code: installed under .claude/.

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add skills/openscribbler/syllago/tui-builder
Any agent
npx skills add OpenScribbler/syllago --skill tui-builder
Clone the repo
git clone --depth 1 https://github.com/OpenScribbler/syllago

Made for: Claude Code.

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for tui-builder

README.md
[![agentmods](https://agentmods.dev/badge/skills/openscribbler/syllago/tui-builder.svg)](https://agentmods.dev/skills/openscribbler/syllago/tui-builder)
Your own site
<a href="https://agentmods.dev/skills/openscribbler/syllago/tui-builder"><img src="https://agentmods.dev/badge/skills/openscribbler/syllago/tui-builder.svg" alt="Measured on agentmods" height="20"></a>
Per session 45 Skills are progressive disclosure: only the name and description are preloaded; the body loads when the skill is used.
When invoked 1,331 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 0 findings. Scan, not verified.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5.1 $0.00045 $0.01331
Opus 5 $0.00023 $0.00665
Sonnet 5 $0.00009 $0.00266
Haiku 4.5 $0.00005 $0.00133

Measured 6d ago against content hash fdaa6b24732e, method: parsed. Prices are Anthropic first-party input rates as of 2026-09-05, from the pricing page.

Security

Grade A, and why

tui-builder scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 6d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Nothing flagged

None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.

.claude/skills/tui-builder/SKILL.md · 59 lines

How it starts

The opening of the file, as written. The whole thing — 59 lines — stays where its author put it; the contents beside it link to each section on GitHub.

TUI Builder — Golden Rules + Quick Reference

Context gate: On first invocation in a session, run: touch /tmp/syllago-tui-builder-${PPID} This signals the tui-context-gate hook that the skill was loaded.

Golden Rules Checklist

Before any layout or component work, verify:

  1. Mouse + keyboard parity on EVERY interactive elementzone.Mark() in View, zone.Get().InBounds() in updateMouse. No exceptions. If it responds to a keypress, it must respond to a click.
  2. Subtract 2 from height for borders, 2 from width for borders
  3. Never rely on auto-wrap — truncate explicitly (maxTextWidth = panelWidth - 4)
  4. Use MaxWidth() not Width() — Width word-wraps
  5. Use borderedPanel() for all bordered panels
  6. Parent owns child sizes — call SetSize(), never let children calculate
  7. All async work via tea.Cmd — never goroutines
  8. All state changes returned from Update() — never mutate elsewhere
  9. Always propagate Update() to active sub-models
  10. Handle tea.WindowSizeMsg — store and recalculate

Component Message Contracts

Component Receives Sends
topbar tea.WindowSizeMsg, tea.KeyMsg, tea.MouseMsg tabChangedMsg, actionPressedMsg, helpToggleMsg
items tea.WindowSizeMsg, tea.KeyMsg itemSelectedMsg
explorer tea.WindowSizeMsg, tea.KeyMsg, content items explorerDrillMsg, explorerCloseMsg
gallery tea.WindowSizeMsg, tea.KeyMsg, tea.MouseMsg cardSelectedMsg, cardDrillMsg
modal tea.KeyMsg, tea.MouseMsg editSavedMsg, editCancelledMsg
install tea.KeyMsg, tea.MouseMsg, step data installResultMsg, installDoneMsg, installCloseMsg
toast tea.KeyMsg, toastTickMsg (dismisses via Dismiss() cmd)

Gotchas

  • AdaptiveColor mutates renderer state. Fix: warmup render in TestMain().
  • bubbletea v1 uses tea.KeyMsg, not tea.KeyPressMsg (v2 API). Check version before using spec code.
  • Golden files with raw ANSI are fragile. Strip ANSI before storing.
  • lipgloss.Width() is ANSI-aware, len() is not. Always use lipgloss.Width() for rendered strings.
  • Children that never receive WindowSizeMsg render at zero size.
  • goimports strips "unused" imports between edits. Add import and usage in a single Edit call.
  • Cursor initialization with Reset(): When active = -1, Open() must default cursor to 0, not -1.
  • lipgloss Width() wraps, MaxWidth() truncates. For bordered panels, always use both Width+MaxWidth and Height+MaxHeight. Without MaxHeight, content overflow pushes the header offscreen.
  • Manual string splitting destroys zone markers. Never split rendered strings containing zone.Mark() on newlines or truncate by rune — the invisible zero-width markers get broken. Use lipgloss styling for dimension control instead.
  • Sort indicators overflow short columns. "Files ▲" is 7 visual chars but the Files column is 5. Use headerCell() which truncates the label to make room for the indicator within the column width.
  • App-level keys intercept search input. When the library search input is active, keys like 'a' (add), 'q' (quit), '1' (group switch) must be passed through to the search handler instead of triggering app shortcuts. Check table.searching before handling global letter keys.
  • Help bar separator is middle dot (·) not asterisk (*). Cleaner look.
  • Metadata bar steals table height. When adding a fixed-height panel below a variable-height component (like table + metadata bar), always reduce the variable component's height in both SetSize() AND View(). If only one is updated, the table renders at the wrong height on resize vs initial draw.
  • Modal lipgloss.Place() centering. Use lipgloss.Place(width, height, lipgloss.Center, lipgloss.Center, rendered) for modal centering — not manual padding math. Handles terminal resize automatically.
  • YAML folded scalars add trailing newlines. Descriptions from loadout.yaml using > have \n at the end. Always sanitize catalog text fields before rendering in the TUI.
  • Unified frame requires manual border construction. borderedPanel() can't create shared borders between sections. Build frames manually: / for corners, ├──┤ for internal separators, ├──┬──┤/╰──┴──╯ for split pane junctions.
  • Metadata panel height is constant (metaBarLines=3 + 1 separator). Type-specific line 3 is blank for simple types, not omitted. This prevents the frame from shifting when scrolling between hooks and skills.
  • Explorer needs providers for metadata. Pass providers + repoRoot to newExplorerModel() so it can compute installed status via computeMetaPanelData().
  • Keyboard-only components are bugs. If a View renders interactive elements without zone.Mark(), mouse users can't interact with them. Every radio option, checkbox row, list item, button, and text input needs a zone mark in View and a corresponding zone.Get().InBounds() check in updateMouse. The checkboxList component automates this via zonePrefix — set it and call HandleClick() in the parent's mouse handler.

Read the full file on GitHub · 59 lines

Files

What ships with it

2 files beside SKILL.md in the same directory: the scripts, references and assets a skill reads on demand. Not counted in the per-session cost; read them before you install if any of them is executable.

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 6d ago First seen · 59 lines · 45 tokens per session scan A fdaa6b24732e

Subscribe to this mod's changes

tui-builder is a skill published in the GitHub repository OpenScribbler/syllago (15 stars, last pushed 3d ago), licensed Apache-2.0. It adds 45 tokens to every session and 1,331 once invoked, about $0.0002 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.

Related

Other skills, from other repositories

go-safe-move-refactor

Safely move Go source files between packages with zero compilation downtime. Handles package declarations, import updates, symbol exports, type renames, test migration, and forwarding stubs. Validates compilation after every atomic step.

jmrplens/gitlab-mcp-server · 50 tokens

go-source-documentation

Add idiomatic, godoc-compliant documentation to Go source and test files. Use when user asks to document Go files, add doc comments, improve Go documentation, or mentions /document-go. Covers file headers, package comments, types, functions, interfaces, constants, variables, tests (with detailed explanations)…

jmrplens/gitlab-mcp-server · 75 tokens

increase-test-coverage

Increase Go test coverage to 90%+ using a Research → Plan → Implement pipeline. Analyzes coverage gaps, generates table-driven tests with httptest mocks, and validates results with go test -coverprofile. Designed for Go MCP server projects using the official go-sdk and gitlab.com/gitlab-org/api/client-go/v2.

jmrplens/gitlab-mcp-server · 72 tokens

modularize-go-package

Modularize a monolithic Go package into domain-specific sub-packages. Extracts shared utilities, moves domain files, updates imports, renames types, creates registration functions, and validates compilation+tests at every step. Designed for large-scale refactoring of 50-100+ file packages.

jmrplens/gitlab-mcp-server · 65 tokens

create-mcp-tool

Create a new MCP tool end-to-end: sub-package, input/output structs, handler, ActionSpec metadata, markdown formatter, tests, catalog projection, and documentation. Use when adding a new GitLab API endpoint as an MCP tool.

jmrplens/gitlab-mcp-server · 52 tokens

generate-project-documentation

Generate comprehensive project documentation including architecture overview, package/component docs, MCP tools/resources/prompts reference, developer onboarding guide, and configuration/deployment guide. Uses Diátaxis framework and Mermaid diagrams.

jmrplens/gitlab-mcp-server · 43 tokens