plugin-gentleman
v1.3.3
Published
OpenCode TUI plugin featuring Mustachi - an animated ASCII mascot with eyes, mustache, and optional motivational phrases during busy states
Maintainers
Readme
Plugin Gentleman
For the Gentleman Programming community — Bringing Mustachi, our beloved mascot, into your OpenCode terminal.
An OpenCode TUI plugin crafted for the Gentleman Programming community. Mustachi, the official mascot of Gentleman Programming, now accompanies you through your coding sessions with visual flair and environment awareness:
- 🎭 Prominent ASCII mustache on the home screen
- 👤 Full Mustachi face with eyes in the sidebar
- 👀 Subtle eye animations (optional, low-frequency)
- 💬 Motivational phrases during busy states (Rioplatense Spanish style)
- 🎨 Gentleman theme installed and applied automatically
- 🖥️ Environment detection — displays your OS and LLM providers
- ⚙️ Fully configurable via
opencode.json
Installation
Quick Start
Install the plugin globally with OpenCode:
opencode plugin plugin-gentleman --globalOpenCode will download the package and update your TUI config automatically.
Restart OpenCode to see:
- Prominent ASCII mustache on the home screen
OpenCodebranding textDetected: <OS> · <providers>below the prompt area- Full Mustachi face in the sidebar
Updating
If you already have Plugin Gentleman installed, force OpenCode to refresh the cached npm package:
opencode plugin plugin-gentleman@latest --global --force--force matters: when a plugin is already configured, OpenCode may otherwise keep the existing cached package/config entry. Restart OpenCode after updating.
Alternative: Manual Configuration
If you prefer managing config yourself, add this to ~/.config/opencode/opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["plugin-gentleman@latest"]
}OpenCode will install npm plugins automatically on startup.
Mustachi phrases
Mustachi currently uses the bundled local phrase library only. Experimental model-generated phrases, the Mascot Model command, and the CLI model configuration flow are disabled for stability.
For Developers
Local Testing with npm link:
npm linkThen add to ~/.config/opencode/opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["plugin-gentleman"]
}Restart OpenCode to see changes.
Local Testing with Tarball:
npm pack
opencode plugin ./plugin-gentleman-<version>.tgz --globalIf your OpenCode version doesn't accept a tarball path, use the published package flow instead.
Local Cache Smoke Check:
./mini-local-check.shThis repository script packs the current workspace, clears the local OpenCode cache for plugin-gentleman, installs the tarball into OpenCode's active local package slot, and prints a short manual runtime checklist. It is intended for developer smoke checks before publishing; it is not a general end-user installer.
Features
Visual Components
Home Screen:
- Prominent ASCII mustache (no face, just the mustache)
- "OpenCode" branding text
- Environment detection showing
OS · Providersbelow the prompt area
Sidebar:
- Full Mustachi face with eyes and mustache
- Animated blink, random look-around in 8 directions (when animations enabled)
- Tongue and motivational phrases during busy states (when animations enabled)
- Periodic expressive cycle every 45-60s to showcase animations (when animations enabled)
The Mustachi Mascot
Mustachi is the official mascot of the Gentleman Programming community — not something invented just for this plugin, but a character beloved by the community and now integrated into your coding environment.
The ASCII representation features:
- Eyes that blink and look in 8 directions (center, up, down, left, right, and 4 diagonals) (sidebar only)
- Mustache rendered in grayscale gradient on home screen, semantic zone colors in sidebar
- Tongue that appears during busy states or periodic expressive cycles (sidebar only)
- Motivational phrases in Rioplatense Spanish style — rotating while expressive (36+ phrase library) (sidebar only)
Example phrases during busy states:
- "Ponete las pilas, hermano..."
- "Dale que va, dale que va..."
- "Ya casi, ya casi..."
- "Ahí vamos, loco..."
- "Un toque más y listo..."
- "Aguantá que estoy pensando..."
Animations
Low complexity, low frequency — subtle and non-intrusive:
Blink (when animations: true)
- Natural eyelid motion: open → half → closed → half → open
- Progressive top-to-bottom animation (80-100ms per frame)
- ~35% chance every 2s (average ~5-6 seconds between blinks)
Eye Direction (when animations: true)
- Every ~3 seconds, eyes randomly look in one of 8 directions or stay center
- 60% chance to stay centered, 40% chance to look around
- Supports: up, down, left, right, and 4 diagonal positions
- Returns to center frequently for natural feel
Busy/Expressive State (when animations: true)
- Tongue appears when OpenCode is processing
- Eyes squint during expressive state
- Motivational phrases rotate every 3.5 seconds while Mustachi is in expressive/busy state (36+ phrase library)
- Active during detected busy states OR periodic expressive cycles
Expressive Cycle Fallback (when animations: true)
- First cycle: 45-60s after load
- Subsequent cycles: every 45-60s
- Duration: 8 seconds per cycle
- Ensures tongue + phrases are visible even if runtime busy detection is unreliable
Disabled (when animations: false)
- Mustachi stays in neutral position
- No blink, no eye movement, no tongue, no phrases
- Completely static face
Gentleman Theme
A refined dark color palette:
- Background: Deep navy (
#06080f) - Primary: Soft blue (
#7FB4CA) - Accent: Warm gold (
#E0C15A) - Text: Clean white (
#F3F6F9)
Home screen mustache: 3-tone grayscale gradient for better readability
- Top: Light gray (
#C0C0C0) - Middle: Mid gray (
#808080) - Bottom: Dark gray (
#505050)
Sidebar Mustachi: Semantic zone colors for visual clarity
- Monocle: Soft silver (
#B8B8B8) - Eyes: Mid gray (
#808080) - Mustache: Dark gray (
#606060) - Tongue: Pink/red (
#FF4466)
Environment Detection
Displays a "Detected" line with:
- OS Detection: Reads distro name on Linux, shows "macOS" or "Windows" on other platforms
- Provider Detection: Lists active LLM providers (OpenAI, Copilot, Google, etc.)
Both are fully configurable and can be hidden.
Configuration
Options can be configured via plugin tuple syntax in OpenCode config files.
Quick tip: To disable animations, add { "animations": false } to your plugin config (see examples below).
Default Settings
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
[
"plugin-gentleman",
{
"enabled": true,
"theme": "gentleman",
"set_theme": false,
"show_detected": true,
"show_os": true,
"show_providers": true,
"show_metrics": true,
"show_face": true,
"animations": true,
"personality_enabled": true
}
]
]
}Granular override keys (
show_branch,show_tokens,show_cost,show_mcp) are intentionally absent frompackage.jsonexported defaults. If OpenCode merged those defaults into the options object, the runtime could not distinguish between a defaulttrueand a genuinely explicit user override — breaking the precedence system below.
Available Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| enabled | boolean | true | Enable/disable the plugin entirely |
| theme | string | "gentleman" | Name of the bundled theme to install |
| set_theme | boolean | false | Automatically activate the theme on load |
| show_detected | boolean | true | Show the "Detected" environment info line |
| show_os | boolean | true | Show detected operating system name |
| show_providers | boolean | true | Show detected LLM providers |
| show_metrics | boolean | true | Legacy master switch: show/hide tokens, cost, and MCP as a group. Does NOT affect branch visibility (legacy guarantee). |
| show_face | boolean | true | Show Mustachi face in the sidebar |
| show_branch | boolean | true | Show current branch name below the face |
| show_tokens | boolean | true | Show token usage bar |
| show_cost | boolean | true | Show cost bar |
| show_mcp | boolean | true | Show MCP status row |
| animations | boolean | true | Enable Mustachi animations (eyes, busy state) |
| personality_enabled | boolean | true | Enable personality phrases and tongue expression |
Personality settings
personality_enabled(defaulttrue): turns Mustachi personality UX (tongue + local phrases) on/off.- Legacy
personality_modeandpersonality_modelvalues may still exist in older configs, but model-generated speech is disabled and these values are ignored by the phrase renderer.
Examples
Disable Animations:
If you prefer a completely static Mustachi (no eye movement, no busy-state tongue/phrases):
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
["plugin-gentleman", { "animations": false }]
]
}This sets animations: false, which:
- Disables blink animation
- Keeps eyes in neutral center position (no looking around)
- Hides tongue and motivational phrases during busy states
- Disables periodic expressive cycles
- Mustachi remains completely static
Logo Only (No Detection):
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
["plugin-gentleman", { "show_detected": false }]
]
}Shows only Mustachi and OpenCode branding, no OS/provider info.
Show Only OS:
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
[
"plugin-gentleman",
{
"show_detected": true,
"show_os": true,
"show_providers": false
}
]
]
}Granular Metric Visibility:
Instead of the legacy show_metrics toggle, you can control each sidebar section independently.
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
[
"plugin-gentleman",
{
"show_face": true,
"show_branch": false,
"show_tokens": true,
"show_cost": false,
"show_mcp": true
}
]
]
}Visibility Precedence (show_metrics + granular flags)
show_metrics is preserved for backward compatibility and NEVER controlled branch visibility. The precedence rules (highest to lowest):
- Explicit granular flag — if the user set
show_branch,show_tokens,show_cost, orshow_mcp, that specific flag overrides everything below. show_metrics(legacy) — if explicitly set, it acts as the base for tokens, cost, and MCP together. Does not affect branch.- Package defaults — visibility flags default to
truein code. Only granular metric keys (show_branch,show_tokens,show_cost,show_mcp) are excluded frompackage.jsonexports;show_faceremains exported with the rest of the non-granular defaults.
Why granular keys are excluded from package.json exports: OpenCode may merge a plugin's package.json exports["*"].config defaults into the options object at runtime. If granular keys like show_branch or show_tokens were in those exports, every options object would include boolean defaults — making it impossible for the plugin to distinguish between a merged default (true) and a genuinely explicit user override. The raw boolean override check would always match, breaking the precedence system. The fix is to keep only non-granular defaults in package.json and rely on cfg() defaults in code.
Examples:
| User Config | Branch | Tokens | Cost | MCP |
|---|---|---|---|---|
| (none) | visible | visible | visible | visible |
| { show_metrics: false } | visible | hidden | hidden | hidden |
| { show_branch: false } | hidden | visible | visible | visible |
| { show_metrics: false, show_tokens: true } | visible | visible | hidden | hidden |
| { show_branch: false, show_tokens: false } | hidden | hidden | visible | visible |
How granular vs legacy mode is detected: The plugin checks whether each granular key is present in the raw options object with a boolean value. Boolean granular values are explicit overrides. Malformed values are treated as absent and fall back to show_metrics (for tokens/cost/mcp) or the hardcoded default true (for branch). This only works correctly when the options object does NOT contain injected defaults for those keys — which is why granular keys are deliberately excluded from package.json exports.config. To use granular overrides, add the exact boolean key(s) you want to control to your plugin config tuple.
How It Works
The plugin integrates with OpenCode's TUI system through slot registration:
- Theme Installation: Installs
gentleman.jsoninto OpenCode themes on load - Theme Activation: Switches to the gentleman theme (if
set_theme: true) - Home Logo Slot: Registers
home_logowith mustache-only ASCII art (grayscale gradient) - Environment Detection Slot: Registers
home_bottomwith OS/provider info below the prompt - Sidebar Slot: Registers
sidebar_contentwith full Mustachi face and animations - Animation Loops: Starts independent interval timers for:
- Blink animation (~5-6s average interval)
- Random eye look-around (~3s interval)
- Phrase rotation during expressive states (3.5s interval)
- Periodic expressive cycles (45-60s interval)
(All sidebar-only, when
animations: true)
- Busy State Detection: Attempts to read
api.state.session.runningfor reactive busy state (best-effort; may not be exposed by all OpenCode versions) - Expressive Cycle Fallback: If busy detection is unreliable, periodic cycles ensure animations are visible
Technical Stack
- No Build Step: Plain TSX transpiled at runtime by OpenCode
- Solid.js Reactivity: Uses
createSignalandcreateEffectfor all animations - Safe Detection: All OS/provider detection wrapped in try-catch blocks
- Cleanup: Uses
onCleanupto clear all intervals when components unmount - Multi-file Architecture: Separated concerns for maintainability (see Architecture below)
Architecture
The plugin is structured as a multi-file module for maintainability and clarity:
tui.tsx— Plugin entry point. Handles initialization, theme setup, slot registration, and busy state detection.ui/— Solid.js UI components (HomeLogo,SidebarMustachi,DetectedEnv) plus sidebar-specific helpers/effects.ascii-frames.ts— All ASCII art assets: 9 eye position frames, 3 blink frames, squinted eyes, mustache designs, 3 tongue frames, and zone color definitions.phrases.ts— Library of 36+ motivational phrases (Rioplatense Spanish style) shown during expressive states.config.ts— Configuration parsing with type-safe defaults and validation helpers.utils/— OS/provider/stack detection, animation utilities, MCP helpers, and message/cost formatting.runtime/— Plugin API helpers, busy detection, model resolution compatibility helpers, Mustachi context summarization, and personality layer orchestration.
Supported Providers
Friendly name mapping for LLM providers:
| Provider ID | Display Name |
|-------------|--------------|
| openai | OpenAI |
| google | Google |
| github-copilot | Copilot |
| opencode-go | OpenCode GO |
| anthropic | Claude |
| deepseek | DeepSeek |
| openrouter | OpenRouter |
| mistral | Mistral |
| groq | Groq |
| cohere | Cohere |
| together | Together |
| perplexity | Perplexity |
Unknown provider IDs display the configured name or raw ID.
Important Notes
TUI Plugin vs System Plugin
This is a TUI plugin for npm installation.
If you copy .ts files to ~/.config/opencode/plugins/ (system plugin):
- ❌ NO visual changes — system plugins cannot modify the TUI
- ❌ NO Mustachi — only TUI plugins can register slot components
- ❌ NO animations — JSX/Solid.js components only work in TUI plugins
For full features, use npm installation (recommended global method above).
Package Contents
Files included in npm package (via files field in package.json):
tui.tsx— plugin entry point and slot registrationui/— UI components and sidebar effects/helpersruntime/— runtime helpers, model resolver/client seam, and personality layerutils/— detection, animation, MCP, and message utilitiesbin/— disabledplugin-gentlemanCLI compatibility entrypointascii-frames.ts— all ASCII art frames, eye positions, and color definitionsphrases.ts— motivational phrases library for busy statesconfig.ts— configuration parsing helpers and type definitionstypes.ts— shared type definitions- root compatibility re-exports:
detection.ts,animation-utils.ts,message-utils.ts,mcp-utils.ts progress-components.tsx— sidebar metrics/MCP rendering componentsgentleman.json— theme definitionpackage.json— auto-included by npmREADME.md— auto-included by npm
Repository-only files (excluded from npm package):
mini-local-check.sh— developer-only local cache smoke-check script.atl/— local Agent Teams/SDD artifact metadata when present
Known Limitations
Busy State Detection: The plugin attempts to detect busy states via
api.state.session.running, but this may not be exposed in all OpenCode versions. If unavailable, a periodic expressive cycle fallback (every 45-60s) ensures animations remain visible.Animation Frequency: Current timing intervals:
- Blink: ~5-6 seconds average (35% chance every 2s)
- Look-around: 3 seconds (60% center, 40% random direction)
- Phrase rotation: every 3.5 seconds during expressive state
- Expressive cycle: first at 45-60s, then every 45-60s
To adjust, modify the intervals in
ui/sidebar/expression-effects.ts.Slot Usage: The plugin uses these OpenCode TUI slots:
home_logo— mustache-only ASCII art (grayscale gradient)home_bottom— environment detection (OS + providers)sidebar_content— full Mustachi face with animations
The plugin requests
mode: "replace"as a best-effort strategy, but host-native sections such as Context / MCP / LSP / Modified Files are controlled by OpenCode and may still appear.Theme Compatibility: The plugin installs and optionally activates the Gentleman theme. If you prefer a custom theme, set
set_theme: false.
Development
Modifying the Plugin
The plugin is organized into focused modules for easy customization:
Adding new content:
- Motivational phrases: Edit
phrases.ts— add new phrases to thebusyPhrasesarray (currently 36+ phrases) - ASCII art frames: Edit
ascii-frames.ts— modify eye positions (9 variants), blink frames (3 stages), mustache designs, or tongue states (binary on/off) - UI logic: Edit
ui/— adjust components, sidebar effects, and layout - Configuration: Edit
config.ts— add new config options with type-safe defaults - Detection logic: Edit
utils/detection.tsand relatedutils/helpers — add OS/provider/stack patterns or marker behavior - Plugin behavior: Edit
tui.tsx— modify slot registration, initialization flow, or busy detection
Testing locally:
- Make your changes in the appropriate file(s)
- Test with
npm link,npm pack, or./mini-local-check.sh(see "For Developers" section above) - No build step needed — OpenCode transpiles TSX at runtime
- Restart OpenCode to see changes
Animation timing customization:
Animation intervals are handled in ui/sidebar/expression-effects.ts:
- Look-around interval: Currently 3000ms (3s)
- Blink interval: Currently 2000ms with 35% chance (~5-6s average)
- Blink frame timing: Currently 80-100ms per frame progression
- Phrase rotation: Every 3500ms while expressive/busy
- Expressive cycle timing: First cycle at 45-60s, then every 45-60s
- Expressive cycle duration: Currently 8000ms (8s)
Color customization:
- Zone colors (sidebar): Edit
zoneColorsobject inascii-frames.ts(monocle, eyes, mustache, tongue) - Home grayscale gradient: Edit
HomeLogocomponent inui/home-logo.tsx(top/middle/bottom color values) - Theme colors: Edit
gentleman.jsonfor the full color palette
License
ISC
