ワンクリックで
api-reviewer
// Guidelines for reviewing API design in the Hex1b codebase. Use when evaluating public APIs, reviewing accessibility modifiers, or assessing whether new APIs follow project conventions.
// Guidelines for reviewing API design in the Hex1b codebase. Use when evaluating public APIs, reviewing accessibility modifiers, or assessing whether new APIs follow project conventions.
Step-by-step guide for creating new widgets in the Hex1b TUI library. Use when implementing new widgets from scratch, including widget records, nodes, extension methods, theming, reconciliation, and tests.
Agent for diagnosing and fixing flaky terminal UI tests in the Hex1b test suite. Use when tests pass locally but fail in CI, or when tests exhibit timing-sensitive behavior.
Guidelines for producing accurate and maintainable documentation for the Hex1b TUI library. Use when writing XML API documentation comments, creating end-user guides, or updating existing documentation.
Guidelines for writing unit tests in the Hex1b TUI library. Use when creating new tests for widgets, nodes, or terminal functionality.
Agent for validating Hex1b documentation against actual library behavior. Use when auditing documentation accuracy, testing interactive examples, or identifying discrepancies between documentation and implementation.
Guidelines for running and interpreting Surface API performance benchmarks. Use when modifying code in src/Hex1b/Surfaces/ to ensure performance is not regressed.
| name | api-reviewer |
| description | Guidelines for reviewing API design in the Hex1b codebase. Use when evaluating public APIs, reviewing accessibility modifiers, or assessing whether new APIs follow project conventions. |
This skill captures API design preferences for the Hex1b codebase. Use it when reviewing public APIs, evaluating whether code should be public or internal, or assessing API design decisions made by AI agents.
Hex1b is structured into distinct layers, each with different API design considerations:
The foundational layer maintaining an in-memory representation of terminal state.
| Component | Purpose | Accessibility |
|---|---|---|
Hex1bTerminal | Core terminal state management | Public |
Hex1bTerminalBuilder | Fluent terminal construction | Public |
| Presentation adapters | Output to real terminals, tests, web | Public interfaces |
| Workload adapters | Input/output abstraction | Public interfaces |
Surface, SurfaceCell | Low-level cell storage | Public (low-level API) |
Design note: Hex1bTerminal emerged from the need to properly test TUI components. The adapter pattern enables use in real terminals, unit tests, or projecting state across networks.
The widget/node tree layer, inspired by React/Ink but not beholden to it.
| Component | Purpose | Accessibility |
|---|---|---|
Hex1bApp | Root of TUI applications | Public |
*Widget records | Declarative UI configuration | Public |
*Node classes | DOM-like render elements | Generally public (for extensibility) |
| Reconciliation logic | Widget→Node synchronization | Internal |
| Layout algorithms | Measure/arrange implementation | Internal |
Testing and automation infrastructure.
| Component | Purpose | Accessibility |
|---|---|---|
Hex1bTerminalInputSequenceBuilder | Fluent input automation | Public |
CellPatternSearcher | Visual assertion matching | Public |
Hex1bTerminalSnapshot | Captured terminal state | Public |
Design note: Automation APIs are explicitly for external consumers, not just internal testing.
When reviewing accessibility modifiers, apply this framework:
When reviewing agent-generated code, ask:
With...() PatternFor Hex1bTerminalBuilder and similar construction scenarios:
// ✅ PREFERRED: Classic builder pattern
var terminal = Hex1bTerminal.CreateBuilder()
.WithWorkload(workload)
.WithPresentation(presentation)
.WithDimensions(80, 24)
.WithHeadless()
.Build();
For widget APIs, use minimal constructors with essential arguments only, then fluent extension methods:
// ✅ PREFERRED: Minimal constructor + fluent methods
ctx.Table(columns, rows)
.SelectedRow(selectedIndex)
.OnSelectionChanged(HandleSelection)
// ✅ PREFERRED: Cross-cutting concerns as extension methods
ctx.Progress(value, max).Fill()
// ❌ AVOID: Overloaded constructors with many parameters
new TableWidget(columns, rows, selectedIndex, onSelectionChanged, sortColumn, sortDirection, ...)
Guideline: Only include the most essential arguments in the constructor. Make usage "bleeding obvious." Use extension methods to splice in extra behavior.
IStatefulWidget<TSelf, TState>When a widget owns non-trivial mutable user-facing state (a textbox's buffer + cursor, an editor's document, a navigator's selection, a checkbox's checked value), expose that state to callers so composites can drive the widget from outside.
The framework-wide contract is IStatefulWidget<TSelf, TState>:
public sealed record TextBoxWidget(...)
: Hex1bWidget, IStatefulWidget<TextBoxWidget, TextBoxState>
{
internal TextBoxState? InjectedState { get; init; }
public TextBoxWidget State(TextBoxState state) => this with { InjectedState = state };
}
Callers then own the state via ctx.UseState(...) and bind it:
var state = ctx.UseState(() => new TextBoxState());
return ctx.TextBox().State(state);
When to apply:
UseState needed.Rules:
where TState : class) so external mutations are observed..State(s) must route the same instance into the node every reconcile..State(...), conflicting use must throw InvalidOperationException on reconcile (no precedence rules to memorise).State(...) — the analyzer (HEX1B0001) forbids With* on widget extension methods.See TextBoxWidget/TextBoxState, EditorWidget/EditorState, NavigatorWidget/NavigatorState, CheckboxWidget/CheckboxState for the canonical patterns.
When you start creating many overloads, use an options type:
// ✅ PREFERRED: Options type for complex configuration
public class Hex1bAppOptions
{
public Hex1bTheme? Theme { get; init; }
public IHex1bTerminalWorkloadAdapter? WorkloadAdapter { get; init; }
// ... extensible without breaking changes
}
// ❌ AVOID: Many overloads
public Hex1bApp(Func<...> builder) { }
public Hex1bApp(Func<...> builder, Hex1bTheme theme) { }
public Hex1bApp(Func<...> builder, Hex1bTheme theme, IWorkloadAdapter adapter) { }
But: Don't have options classes everywhere. Be conservative with what you require.
// ✅ PREFERRED: Task as default
public Task<Hex1bWidget> BuildAsync(WidgetContext ctx);
// ✅ Use ValueTask when there's a performance reason (hot paths, avoiding allocations)
public ValueTask HandleInputAsync(Hex1bKeyEvent key);
Provide both sync and async overloads for event handlers. Implement everything assuming async is possible, then wrap sync handlers:
// ✅ PREFERRED: Both overloads, sync wraps to async
public ButtonWidget OnClick(Action<ButtonClickedEventArgs> handler)
=> this with { ClickHandler = args => { handler(args); return Task.CompletedTask; } };
public ButtonWidget OnClick(Func<ButtonClickedEventArgs, Task> handler)
=> this with { ClickHandler = handler };
Note: Samples often favor sync versions for simplicity.
// ✅ PREFERRED: Strongly typed
public void SetColor(Hex1bColor color);
// ❌ AVOID: Magic strings
public void SetColor(string colorName);
// ❌ AVOID: Forcing callers to pass null
public void Configure(string? requiredArg, string? optionalArg);
Configure("value", null); // Awkward
// ✅ PREFERRED: Overloads or optional parameters
public void Configure(string requiredArg);
public void Configure(string requiredArg, string optionalArg);
Be wary of using too many primitive types, as it makes creating overloads harder in the future:
// Consider whether a type is warranted
public void SetPosition(int x, int y); // OK for simple cases
public void SetPosition(Point position); // Better if Point is meaningful elsewhere
Use extension methods when:
.Fill(), .FixedWidth()Use instance methods when:
Minimize the namespaces developers need to know:
// ✅ PREFERRED: Most types in root namespace
using Hex1b;
// Specialized namespaces for specific concerns
using Hex1b.Theming;
using Hex1b.Input;
Guideline: Ideally, most developers just need using Hex1b; and discover other types through methods on types in that namespace.
📘 See the
doc-writerskill for comprehensive documentation guidelines including XML API docs and end-user guides.
All public APIs must have XML documentation:
/// <summary>
/// Creates a button widget with the specified label.
/// </summary>
/// <remarks>
/// Buttons are focusable widgets that respond to Enter key or click events.
/// Use <see cref="OnClick"/> to register a handler for button activation.
///
/// Buttons automatically display a focus indicator when focused and can be
/// styled using <see cref="ButtonTheme"/> elements.
/// </remarks>
/// <param name="label">The text displayed on the button.</param>
/// <example>
/// <description>A simple quit button that stops the application:</description>
/// <code>
/// using Hex1b;
///
/// await using var terminal = Hex1bTerminal.CreateBuilder()
/// .WithHex1bApp((app, options) => ctx =>
/// ctx.Button("Quit").OnClick(e => e.Context.RequestStop()))
/// .Build();
///
/// await terminal.RunAsync();
/// </code>
/// </example>
public sealed record ButtonWidget(string Label) : Hex1bWidget
| Element | Guideline |
|---|---|
<summary> | Concise and accurate - what it does, not how |
<remarks> | Detailed, useful from end-user perspective, no irrelevant internals |
<param> | Required for all parameters |
<returns> | Required for non-void methods |
<example> | Complete, runnable mini-apps when possible |
<code> | Cut-and-paste ready, not just method invocation |
Anti-pattern: Examples that just show invoking the method without context. Always provide complete, runnable examples.
Throw exceptions when something is truly irrecoverable:
// ✅ Use existing exception types when they fit
throw new ArgumentNullException(nameof(widget));
throw new InvalidOperationException("Cannot render before Measure()");
// ✅ Custom exceptions when debugging info is needed
throw new Hex1bRenderException(widget, node, phase, "Render failed", innerException);
Guideline: Think about what information developers need to debug. Attach widget/node/phase information when it helps.
Note: The RescueWidget exists to handle and display errors gracefully in the UI.
Each type should be in its own file. This makes it easier to:
// ❌ AVOID: Static state (causes testability bugs)
private static readonly Dictionary<string, Widget> _cache = new();
// ✅ PREFERRED: Instance state
private readonly Dictionary<string, Widget> _cache = new();
Rationale: AI agents tend to use statics, which introduce subtle testability bugs.
// ✅ OK: Constants
public const int DefaultWidth = 80;
public const int DefaultHeight = 24;
When reviewing APIs, check:
With...() pattern? (and ONLY builders — flagged by HEX1B0001 if a widget extension or instance method starts with With)Widget and are declared as record (HEX1B0002 / HEX1B0004)Node and are declared as class (HEX1B0003 / HEX1B0005)With* methods on widgets — use bare verb-noun names like Title("..."), MaxFloating(3), Disabled(true) (HEX1B0001)These rules are enforced at build time by the Hex1b.Analyzers Roslyn analyzer wired into every project via the root Directory.Build.props. See AGENTS.md § "Code Analysis Rules" for the full table. If a flagged case is intentional and cannot follow the rule, suppress with #pragma warning disable HEX1B000x rather than disabling the analyzer wholesale.
AI agents often make things public by default. Flag for review:
// ❌ REVIEW: Should this be internal?
public void ReconcileChildNodes(List<Hex1bNode> children) { }
// ❌ REVIEW: Implementation detail exposed
public int CalculateInternalLayoutOffset() { }
When you see many overloads, suggest an options type:
// ❌ REVIEW: Consider options type
public Table(columns);
public Table(columns, selectedRow);
public Table(columns, selectedRow, sortColumn);
public Table(columns, selectedRow, sortColumn, sortDirection);
Flag string parameters that should be types:
// ❌ REVIEW: Should be enum or type
public void SetAlignment(string alignment); // "left", "center", "right"
// ✅ Better
public void SetAlignment(Alignment alignment);
Flag public APIs without proper documentation:
// ❌ REVIEW: Missing documentation
public Hex1bWidget CreateWidget(WidgetContext ctx);
// ❌ REVIEW: Example not runnable
/// <example>
/// <code>
/// widget.OnClick(handler);
/// </code>
/// </example>