Manage your tasks with bartenders
This repository has been archived on 2026-05-22. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
  • Rust 78.7%
  • HTML 21%
  • Handlebars 0.2%
Find a file
2026-05-06 20:52:50 +02:00
docs feat: Added fish as a first class terminal 2026-05-06 20:01:23 +02:00
examples docs: Updated M003 workflow docs and parse-only GitHub examples for Mar… 2026-05-05 19:59:21 +02:00
src feat: Added fish as a first class terminal 2026-05-06 20:01:23 +02:00
tests feat: Added fish as a first class terminal 2026-05-06 20:01:23 +02:00
.envrc Init 2026-05-02 22:20:17 +02:00
.gitignore chore: barback file in gitignore 2026-05-04 08:34:44 +02:00
about.hbs chore: about for 3pl 2026-05-06 20:52:50 +02:00
about.toml chore: about for 3pl 2026-05-06 20:52:50 +02:00
AGENTS.md chore(agents): tdd 2026-05-03 10:08:32 +02:00
Cargo.lock test: Added a public strict MiniJinja selected-item template seam with… 2026-05-05 15:59:26 +02:00
Cargo.toml test: Added a public strict MiniJinja selected-item template seam with… 2026-05-05 15:59:26 +02:00
devenv.lock Init 2026-05-02 22:20:17 +02:00
devenv.nix Init 2026-05-02 22:20:17 +02:00
devenv.yaml Init 2026-05-02 22:20:17 +02:00
LICENSE chore: license 2026-05-06 20:33:04 +02:00
README.md feat: Added fish as a first class terminal 2026-05-06 20:01:23 +02:00
THIRD_PARTY_LICENSES.html chore: about for 3pl 2026-05-06 20:52:50 +02:00

Barback

Barback is a source-agnostic terminal workbench for Stations.

A Station is a named work source that Barback can show in the terminal UI. The project is still early: Barback can discover and load Station config at startup, execute configured Station list commands, parse JSON Lines output, map arbitrary JSON object fields into stable display roles, render a header Station selector with browseable Items and Details panes in the TUI, and surface visible warning/error states without compiling in source-specific logic.

Current status

Barback currently provides:

  • a terminal UI with a Catppuccin Mocha-styled header Station selector (< {Station name} >) and circle indicators, two-pane Items/Details browse area, compact bottom status/help bar, diagnostics, selection, and muted/help text through an internal render-only theme seam
  • startup loading for the initially selected Station before the first terminal draw
  • keyboard navigation for header Station selection (h/l or left/right arrow keys), item selection (j/k or up/down arrow keys), detail scrolling (u/d, PageUp/PageDown, g/G), lowercase r list refresh, uppercase R selected-detail refresh, and quit keys
  • visible empty, no-usable-row, warning, error, and missing-detail states in the TUI
  • a public typed config loader for Station TOML
  • deterministic global/local Station merge behavior
  • strict TOML schema parsing for known config fields
  • CLI startup discovery and loading before the terminal UI opens
  • a public Station list runtime API that executes configured shell command strings
  • JSON Lines parsing for object rows with malformed-row warnings and empty/no-usable-row states
  • source-agnostic field mapping from arbitrary JSON object keys into stable id, title, subtitle, and detail roles
  • selected-item detail resolution from configured row fields or an optional fixture-testable view command
  • bounded Station-scoped diagnostics for successful stderr, malformed rows, detail warnings, and non-zero command exits
  • selected-Station list refresh that reruns the configured list command in the background, preserves the last successful rows/selection/detail when refresh fails, surfaces the refresh failure as a visible error banner, shows successful stderr as a warning, and clears prior refresh errors after a successful refresh
  • selected-item detail loading, detail-pane scrolling, and uppercase R detail refresh that run configured view commands in the background, hydrate from an in-memory detail cache when possible, keep stale detail text visible while refreshing, and keep bounded detail diagnostics visible without blanking the Details pane
  • selected-item template/action preview opened with p, navigated with j/k or arrow keys, confirmed with Enter for action entries, cancelled with c while an action is active, acknowledged with Enter/Esc after results, and closed with Esc when no result popup is visible
  • a source-agnostic non-interactive action command contract that renders configured commands from selected-item templates/outputs, supports stdin = "none" | "template" | "outputs.<key>", executes inside the background Runtime seam with a singleton active-action lock, cancellation, and the default action timeout, and returns typed process status plus bounded stdout/stderr captures

Barback's Catppuccin Mocha styling is currently built in through an internal render seam. M002 does not expose custom or user-configurable themes. The final M003 selected-item workflow is covered by deterministic fixture-backed Product Flow tests, and the Manual UAT runbook provides real-terminal validation without live services.

Barback intentionally still keeps command execution simple: configured list, view, and non-interactive action commands run through the local shell as single command strings, with no retries, pagination controls, or non-shell execution yet. Slow list/detail/action commands no longer block normal terminal drawing while they are running. Selection changes do not cancel in-flight list or detail work; when successful background work completes, Barback stores the result in process-local cache. Returning to a Station or item whose request is still pending reuses that pending request instead of starting duplicate command work. Action entries can be launched from the preview popup, one action may run at a time, active actions can be cancelled, action commands use the runtime default timeout, and completed action outcomes remain visible in a persistent result popup until acknowledged with Enter or Esc.

Product Flow and browser controls

Reader action after this section: create or reuse Station TOML, run cargo run, navigate the Station browser, and understand what the UI is proving when it shows loading, cache, stale, warning, or error states.

The running browser is a Catppuccin Mocha-styled two-pane Station view. The header shows the selected Station as < {Station name} > with adjacent circle indicators. The left Items pane shows JSONL rows mapped through the Station field config. The right Details pane shows the selected item detail, either from mapped row detail fields or from the configured view command. The compact footer carries help, loading, stale cache, warning, and error text so the main panes do not disappear during background work.

Keyboard controls:

Area Keys Effect
Station selector h/l, left/right arrows Move to the previous or next Station and load its Items.
Items j/k, up/down arrows Move the selected item and request its Details.
Details scroll u/d, PageUp/PageDown, g/G Scroll the visible Details text by line, page, top, or bottom.
Refresh r reload Rerun the selected Station list command in the background.
Refresh R detail Rerun the selected item's detail command in the background, even when cached detail text exists.
Preview p Open the selected items Templates / Actions preview popup.
Preview j/k, up/down arrows Move between preview entries while the popup is open without moving the underlying item selection.
Preview Enter Run the highlighted action entry when its referenced template renders successfully; template-only entries remain non-runnable.
Action c Request cancellation for the active action.
Action Result Enter, Esc Acknowledge the persistent action result popup and return to the underlying browser/preview state.
Quit q, Ctrl-C Leave the terminal UI.

The preview popup renders the selected Station items configured template body and named outputs using the same selected-item context as detail view-command rendering, and shows bounded render errors in the popup when rendering fails. Template-only entries are non-runnable display entries: pressing Enter on them remains fail-closed and does not execute a command. Action entries render their referenced template in the same popup; pressing Enter on a runnable action starts the action through the Runtime seam, disables duplicate launches while it is active, and leaves normal browser navigation responsive. While an action is active, press c to request cancellation. When the action completes, fails, times out, is cancelled, or is rejected because another action is active, Barback shows a topmost persistent Action Result popup with typed status details and bounded stdout/stderr where available. Acknowledge that result with Enter or Esc to return to the underlying preview/browser state.

Barback caches only in memory inside the current process. A successful list load stores that Station's Items snapshot, even if the user selected another Station before it finished. A successful detail load stores that item's Details snapshot, even if the user selected another item before it finished. These caches are not written to disk, shared across runs, or invalidated by external source changes. Returning to an already completed Station or item hydrates from cache without duplicate command work; returning while the original request is still pending reuses the pending request. During lowercase r list refresh and uppercase R detail refresh, stale cached content remains visible and scrollable while the newer command runs. If refresh fails, Barback keeps the stale Items or Details on screen and surfaces bounded, redacted diagnostics in the footer instead of exposing full shell command strings, oversized output, credentials, or local paths.

Automated product proof is fixture-backed rather than live-service-backed. The focused M003 Product Flow checks build temporary Station config against tracked local fixture scripts, prove JSONL remains the Station item contract, exercise startup loading, Station/item navigation, detail scrolling, stale cache preservation during failed list/detail refreshes, inline and Markdown-backed template previews, template-only no-op confirmation, non-interactive runtime actions, singleton suppression, cancellation, timeout, persistent Action Result acknowledgement, bounded diagnostics, compact footer behavior, and Catppuccin styling. They do not call GitHub, Jira, network services, credentials, or developer-local config.

Configuration format

Barback config is TOML. Define Stations with [[stations]] tables. Each Station needs an id, a display name, and a list command string under [stations.list]. Optional [stations.fields] and [stations.view] sections describe how source-shaped JSON rows become Barback display roles and how selected-item details can be resolved lazily.

[[stations]]
id = "pull-requests"
name = "Pull Requests"

[stations.list]
command = "gh pr list --json number,title,author,body --jq '.[] | @json'"

[stations.fields]
id = "number"
title = "title"
subtitle = "author"
detail = ["body"]

[stations.view]
command = "gh pr view {{ item.number }} --json body --jq .body"

[[stations]]
id = "issues"
name = "Issues"

[stations.list]
command = "gh issue list --json number,title,author --jq '.[] | @json'"

Fields

Field Required Meaning
id Yes Stable Station identifier used for merging global and local config.
name Yes Human-readable name shown in Barback UI surfaces.
list.command Yes Shell command string Barback stores and passes to the Station list runtime. The command should emit one JSON object per stdout line.
fields.id No JSON object key used as the stable item id. Defaults to id when omitted.
fields.title No JSON object key used as the item title. Defaults to title when omitted.
fields.subtitle No JSON object key used as optional subtitle text. Omitted unless configured.
fields.detail No Ordered list of JSON object keys joined with blank lines for row-backed selected-item details.
view.command No Shell command string used instead of row-backed detail fields when resolving a selected item.
templates No Strict catalog of prompt/action templates available to callers. Each entry needs id, name, and exactly one of inline body or Markdown file.
actions No Strict catalog of non-interactive actions tied to a template. Each action needs id, name, template, command, and optional stdin/shell.

Unknown fields are rejected. This is intentional: a typo in config should fail visibly instead of being ignored.

Station templates

Stations can define a deterministic template catalog for callers that need rendered prompt/action text from the selected Station item and optional detail text. Templates use the same strict MiniJinja selected-item context as view.command: selected row fields are available at the top level and under item, Station metadata is under station, detail text is detail, and shell_quote is available for shell-safe arguments.

Inline templates live directly in TOML and may declare named outputs below [stations.templates.outputs]:

[[stations.templates]]
id = "pr-summary"
name = "PR Summary"
body = "Review PR #{{ item.number }}: {{ item.title }}\n\n{{ detail }}"

[stations.templates.outputs]
title = "PR #{{ item.number }}: {{ item.title }}"
url = "{{ item.url }}"

Markdown-backed templates use file, resolved relative to the config file that declares it unless the path is absolute. File-backed outputs must be declared in TOML frontmatter inside the Markdown file, not in the Station TOML entry:

[[stations.templates]]
id = "pr-review"
name = "PR Review Markdown"
file = "templates/pr-review.md"
---
[outputs]
title = "PR #{{ item.number }}: {{ item.title }}"
url = "{{ item.url }}"
---
# Review PR #{{ item.number }}

{{ detail }}

Named outputs are returned in deterministic key order. Config-load diagnostics include the config path, Station id, template id/path field, and concise reason, but they do not dump full Markdown/frontmatter/body content. Render diagnostics distinguish template body failures from named output failures without exposing the full template source or rendered output.

Station template entries are visible from the selected items preview popup. Press p in the TUI to open the popup, use j/k or the up/down arrows to move between preview entries, and use Esc to close it when no result popup is visible. The popup renders template bodies plus named outputs for template-only entries, and it renders the referenced template for action entries before execution. Template-only entries remain non-runnable/fail-closed; Enter on a valid action entry launches that action.

Station actions

Stations can define a strict action catalog for non-interactive local command execution by Barback runtime callers. Actions reference a configured template by id, render a command with the selected-item context plus template and outputs.<key> values, and select stdin from the rendered template body, a rendered named output, or no stdin:

[[stations.actions]]
id = "copy-pr-summary"
name = "Copy PR Summary"
template = "pr-summary"
command = "pbcopy"
stdin = "template"

[[stations.actions]]
id = "create-pr-from-review"
name = "Create PR From Review"
template = "pr-review"
command = "gh pr create --body {{ outputs.pr_body|shell_quote }}"
stdin = "none"

[[stations.actions]]
id = "fish-notify"
name = "Fish Notify"
template = "pr-summary"
command = "set subject {{ outputs.title|shell_quote }}; printf '%s\n' $subject"
stdin = "none"
shell = "fish"

stdin defaults to template when omitted. Valid values are template, none, or outputs.<key>. shell defaults to sh; valid values are sh and fish. Prefer stdin for multiline prompt bodies or rendered outputs; reserve command interpolation for short shell arguments and wrap arbitrary values with shell_quote, for example {{ outputs.url|shell_quote }} or {{ outputs.pr_body|shell_quote }}. Command rendering uses the same strict MiniJinja environment and deterministic POSIX shell_quote filter as Station templates, plus template for the rendered body and outputs for rendered named outputs. Missing fields, null selected-item values, malformed command templates, missing output references, or invalid shell names fail before any process spawn. Subprocess results return typed status (success, non-zero exit, spawn failure, stdin-write failure, timeout, or cancellation) with bounded stdout/stderr captures. Diagnostics include action/template context but do not dump full commands, prompts, rendered bodies, outputs, local fixture paths, credentials, or tokens. In the TUI, action entries launch from the selected-item preview popup, the runtime enforces a single active action at a time, active actions can be cancelled with c, the default action timeout applies, and completed or rejected outcomes display a persistent Action Result popup until acknowledged with Enter or Esc.

Template/action examples in this repository are parse-only/local fixtures. Automated tests verify that tracked examples parse and file paths resolve, but they do not execute gh, call live services, or require credentials.

Example configs

A checked-in GitHub CLI Station example lives at examples/gh-stations.toml. Treat this file as a canonical example of external Station config shape: it demonstrates gh-backed list commands, field mapping, and an optional view command without making GitHub a compiled-in Barback source.

The example file is not the same as a repository-local .barback.toml. Barback discovers .barback.toml from your current working directory at startup for local use, while examples/gh-stations.toml is documentation and parse-only test input. Automated tests verify that the example parses as config, but they do not execute its gh commands, call Jira/GitHub, or require live credentials.

Field mapping and details

Station list commands emit arbitrary JSON objects, not source-specific Rust structs. Barback maps configured keys into stable roles:

  • strings render as-is
  • numbers and booleans render with their display text
  • arrays and objects render as compact JSON
  • missing or null mapped display fields are absent

Selected-item details resolve in two modes:

  1. If [stations.view].command is configured, Barback renders it as a strict MiniJinja selected-item template and executes the rendered result as one platform shell command string. Selected row fields are available both at the top level ({{number}}) for backward-compatible configs and under the item namespace ({{ item.number }}) for collision-safe configs. Station metadata is available as {{ station.id }} / {{ station.name }}, and shell_quote provides deterministic POSIX single-quoting for placeholder values that may contain shell metacharacters. Stdout is treated as plain detail text, not JSON. Successful stderr is returned as a bounded warning. Missing or null selected-item values return a structured Station-scoped detail error and skip shell execution instead of panicking.
  2. If no view command is configured, Barback joins configured fields.detail row values in order. Missing or null detail fields produce Station-scoped warnings; non-string JSON values render using the same display conversion as mapped fields.

View-command rendering is intentionally limited to the selected-item MiniJinja context and the shell_quote filter. Barback does not add retries, timeouts, cancellation, pagination, or non-shell execution in this slice. Because rendered view commands run through the local shell, Station authors should quote placeholders appropriately for their command and data shape, for example {{ item.key|shell_quote }} when an arbitrary string becomes one shell argument. Public diagnostics include Station id/name, render/command failure summaries, status, and bounded stdout/stderr snippets, but they do not include the full configured shell command string or template body.

Global and local config

Barback's config model supports two sources:

  1. a global config file for Stations you want available everywhere
  2. a project-local config file for Stations specific to one repository

At startup, Barback discovers and loads both paths before opening the terminal UI:

  • global config: the OS-standard Barback config directory, as resolved by directories::ProjectDirs, with a config.toml file
  • project-local config: .barback.toml in the current working directory

Tests and library callers can still pass explicit paths into the config loader so they do not read developer-local config.

When both sources define Stations, Barback merges them by id:

  • global Stations are loaded first
  • a local Station with the same id replaces the global Station at the same position
  • local Stations with new ids append after the global list in local-file order

Example:

# Global config
[[stations]]
id = "pull-requests"
name = "Pull Requests"

[stations.list]
command = "gh pr list --json number,title --jq '.[] | @json'"

[[stations]]
id = "inbox"
name = "Inbox"

[stations.list]
command = "tool inbox --jsonl"
# Project-local config
[[stations]]
id = "pull-requests"
name = "Repository PRs"

[stations.list]
command = "gh pr list --search 'repo:owner/project' --json number,title --jq '.[] | @json'"

[[stations]]
id = "release-blockers"
name = "Release Blockers"

[stations.list]
command = "tool blockers --jsonl"

Merged result:

  1. pull-requests from the project-local config, in the original global position
  2. inbox from the global config
  3. release-blockers from the project-local config

Diagnostics

Config load errors include the source path that failed. Invalid TOML and unknown fields are returned as errors instead of being silently ignored. When config discovery finds an invalid present config file, Barback returns that error before opening the terminal UI.

Diagnostics are deliberately concise. They should identify the broken source and parser problem without dumping the full config file or exposing secret-bearing command strings.

On lowercase r list refresh, Barback reruns the selected Station's configured list command in the background. A failed list refresh leaves the last successful Items and Details panes visible, keeps the previous selection when possible, and reports the current refresh failure in the compact bottom status/help bar as a bounded error with a stale-cache signal. A successful list refresh replaces stale rows with the latest command output, clears any previous refresh error, and keeps successful stderr visible as a bounded warning in that bar.

On uppercase R selected-detail refresh, Barback reruns the selected item's configured view command in the background even when cached detail content exists. While that refresh is in flight, the Details pane keeps the stale detail text visible with a refresh signal. The same Details pane scroll controls (u/d, PageUp/PageDown, g/G) continue to operate on the visible stale text and diagnostics. A successful detail refresh replaces the stale text and updates the in-memory detail cache; a failed detail refresh keeps the stale text visible and surfaces bounded detail diagnostics instead of blanking the pane. These diagnostics remain source-agnostic: they describe Station context and bounded stdout/stderr snippets without exposing the full configured shell command string.

For selected-item actions, Barback surfaces completion through the persistent Action Result popup rather than a transient footer line. The popup distinguishes success, non-zero exit, spawn failure, stdin-write failure, timeout, cancellation, and singleton rejection; includes Station/item/action/template metadata; and shows bounded stdout/stderr captures for completed process outcomes. It intentionally omits full command strings, rendered prompt/template bodies, rendered outputs, fixture paths, credentials, and tokens. The popup stays topmost until acknowledged with Enter or Esc.

Development

Run the test suite with:

cargo nextest run

Run the config-focused tests with:

cargo nextest run config

Run the Station list runtime tests with:

cargo nextest run station_runtime

Run the Station field/detail tests with:

cargo nextest run --test station_details

Run the checked-in example-config parse proof with:

cargo nextest run --test example_config

Run the fixture-backed App/TUI browse proof with:

cargo nextest run --test e2e_fixture_browse

Run the focused M003 selected-item product-flow proof with:

cargo test --test e2e_fixture_browse m003 -- --nocapture

The config tests use temporary files and public APIs. Runtime and detail tests use tracked fixture scripts under tests/fixtures/bin/ instead of live tools. The fixture-backed E2E tests build temporary global/local TOML files that point at those tracked scripts, proving merge order, command execution, JSONL parsing, field mapping, details, warnings/errors, Station switching, refresh behavior, stale-cache preservation, item/detail scrolling, inline and Markdown-backed template rendering, named outputs, runtime action success and failure statuses, singleton suppression, cancellation, timeout, Action Result acknowledgement, compact footer rendering, and Catppuccin style presence without reading developer-local config. They do not call live GitHub/Jira services or require credentials. The focused M003 product-flow tests are the automated no-live-service regression gate for the current selected-item workflow; the Manual UAT runbook is the human real-terminal validation pass.