npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fdcn/workhorse

v0.1.17

Published

Terminal UI for Workhorse - AI-powered issue resolver

Downloads

2,056

Readme

workhorse-tui

Terminal UI for Workhorse — a reactive interface for managing AI coding agents.

Installation

Requires Bun runtime (v1.0+)

# Install bun if you haven't already
curl -fsSL https://bun.sh/install | bash

# Install workhorse globally
bun i -g @fdcn/workhorse

Note: Workhorse requires Bun as its runtime due to dependencies on Bun-specific features in the terminal UI framework.

What This Package Does

This package provides the interactive terminal interface for Workhorse:

  • Multi-screen navigation — Overview, agent detail, help
  • Issue/agent management — Spawn, monitor, stop agents
  • Live activity feed — Watch agents work in real-time
  • Chat interface — Send messages to running agents
  • OAuth flows — Guided setup for Jira/GitHub authentication

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                            TUI                                   │
├─────────────────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                      Screens                             │    │
│  │  Overview (issues | agents) │ Agent Detail │ Help       │    │
│  └───────────────────────────────────┬─────────────────────┘    │
│                                      │                           │
│  ┌───────────────────────────────────┼─────────────────────┐    │
│  │                     Components    │                      │    │
│  │  IssueList │ AgentList │ ActivityFeed │ ChatBox         │    │
│  │  SpawnModal │ ModelSelectorModal │ DeleteModal          │    │
│  └───────────────────────────────────┼─────────────────────┘    │
│                                      │                           │
│  ┌───────────────────────────────────┼─────────────────────┐    │
│  │                   Primitives      ▼                      │    │
│  │  createAgents │ createIssues │ createChat │ createActivity│   │
│  │  createMonitors │ createFileChanges                      │    │
│  └───────────────────────────────────┬─────────────────────┘    │
│                                      │                           │
│  ┌───────────────────────────────────┼─────────────────────┐    │
│  │                  Renderers        │                      │    │
│  │  Plugin-extensible activity rendering                    │    │
│  │  RendererRegistry → ActivityInput → RenderedActivity     │    │
│  └───────────────────────────────────┴─────────────────────┘    │
├─────────────────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                 WorkhorseProvider                        │    │
│  │  config, hooks, tracker, orchestrator, memory, monitors  │    │
│  └─────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                       workhorse-core                             │
│  bootstrap() → { hooks, tracker, orchestrator, ... }            │
└─────────────────────────────────────────────────────────────────┘

Key Abstractions

Screens

type Screen = "overview" | "agent" | "help";

| Screen | Layout | Purpose | |--------|--------|---------| | Overview | Two-pane: issues | agents | Main dashboard, spawn agents | | Agent | Activity feed + sidebar | Monitor agent, send messages | | Help | Keyboard shortcuts | Reference |

Reactive Primitives

Custom Solid.js hooks that bridge core services to reactive UI:

// Tracks all agents with live state updates
const agents = createAgents();
// → Map<issueId, { adapter, state, model, ... }>

// Fetches issues with optional repo filter
const issues = createIssues({ repo: "owner/repo" });
// → Issue[]

// Chat state for an agent
const chat = createChat(issueId);
// → { messages, send, isLoading }

// Activity feed for an agent
const activity = createActivity(issueId);
// → ActivityItem[]

// Monitor status
const monitors = createMonitors(issueId);
// → Map<monitorId, MonitorStatus>

// Git changes in worktree
const changes = createFileChanges(worktreePath);
// → { staged: FileChange[], unstaged: FileChange[] }

How reactivity works:

function createAgents() {
  const [version, setVersion] = createSignal(0);
  const bump = () => setVersion(v => v + 1);

  // Subscribe to relevant hooks
  const { hooks, orchestrator } = useWorkhorseContext();
  hooks.on("agent.create.post", bump);
  hooks.on("agent.start.post", bump);
  hooks.on("agent.stop.post", bump);

  // Memo recomputes when version changes
  return createMemo(() => {
    version();  // Subscribe to version
    return orchestrator.getAll();
  });
}

Activity Renderers

Plugin-extensible system for rendering notifications:

interface RenderedActivity {
  icon: string;
  title: string;
  subtitle?: string;
  body?: string;
  style: "box" | "inline";
  color?: "info" | "success" | "warning" | "error" | "dim" | "accent";
}

type ActivityRenderer = (input: ActivityInput) => RenderedActivity | null;

Plugin registration:

// Plugins register via hook
hooks.emit("tui.register_renderer", {
  id: "playwright",
  renderer: playwrightRenderer,
  priority: 50,
});

// Registry tries renderers in priority order
function renderActivity(input: ActivityInput): RenderedActivity {
  for (const renderer of sortedRenderers) {
    const result = renderer(input);
    if (result) return result;
  }
  return defaultRenderer(input);
}

Built-in renderers:

  • agent — Agent lifecycle events (start, stop, idle)
  • Plugins add their own (Pi tools, Playwright, etc.)

Global UI State

Centralized state using Solid signals:

const ui = {
  // Current state
  screen: Accessor<Screen>,
  modal: Accessor<Modal | null>,
  selectedAgentId: Accessor<string | null>,
  inputMode: Accessor<boolean>,
  focusedComponent: Accessor<"issues" | "agents" | "chat">,

  // Actions
  enterAgentView(agentId: string),
  openSpawnModal(issue: Issue),
  openModelSelector(),
  focusNext(),  // Tab navigation
  showError(message: string),
};

Keyboard Bindings

Focus-aware shortcuts:

| Context | Key | Action | |---------|-----|--------| | Global | q | Quit | | Global | ? | Show help | | Global | Ctrl+X M | Model selector | | Overview | Tab | Cycle focus (issues → agents → chat) | | Overview | ↑/↓ | Navigate list | | Overview | Enter | Select/spawn | | Agent | s | Stop agent | | Agent | Esc | Back to overview | | Chat | Enter | Send message |

Startup Flow

1. parseCliArgs()
   └─▶ Handle --help, --list-models, --version

2. resolveConfigPaths()
   └─▶ Find ~/.workhorse.toml or create defaults

3. getPluginsNeedingSetup()
   └─▶ If any: show Setup wizard
       └─▶ Guide user through config creation

4. getPluginsNeedingAuth()
   └─▶ If any: show Auth screens
       └─▶ OAuth flows for Jira, GitHub, etc.

5. bootstrap({ plugins })
   └─▶ Initialize workhorse-core with all plugins

6. render(<App />)
   └─▶ Start terminal UI

TUI as Plugin

The TUI registers itself as a plugin:

const tuiPlugin = definePlugin({
  manifest: { name: "tui", version: "0.1.0" },

  setup() {
    const { hooks } = useWorkhorse();

    // Register built-in renderer
    registerRenderer("agent", agentRenderer);

    // Accept plugin renderers
    hooks.on("tui.register_renderer", (payload) => {
      registerRenderer(payload.id, payload.renderer, payload.priority);
    });
  },
});

Components

IssueList

Displays parsed issues with status indicators:

┌─ Issues ──────────────────────┐
│ ● PROJ-123  Implement login   │
│ ○ PROJ-124  Add dark mode     │
│ ◐ owner/repo#45  Fix crash    │
└───────────────────────────────┘
  • = Agent running
  • = Agent starting/stopping
  • = No agent

AgentList

Shows active agents with state:

┌─ Agents ──────────────────────┐
│ 🥧 PROJ-123  running  claude  │
│ 🤖 PROJ-124  stopped  gpt-4   │
└───────────────────────────────┘

ActivityFeed

Live stream of agent activities:

┌─ Activity ────────────────────┐
│ 📝 Read src/auth.ts           │
│ ✏️  Edit src/auth.ts:15-20    │
│ 🔨 npm test                   │
│ ✅ All tests pass             │
│ 📸 Screenshot: login.png      │
└───────────────────────────────┘

ChatBox

Send messages to agent:

┌─ Chat ────────────────────────┐
│ You: Please also handle the   │
│      error case               │
│                               │
│ Agent: I'll add error         │
│        handling...            │
├───────────────────────────────┤
│ > Type message...             │
└───────────────────────────────┘

SpawnModal

Configure and spawn agent:

┌─ Spawn Agent ─────────────────┐
│ Issue: PROJ-123               │
│ Harness: [pi-coding-agent ▾]  │
│ Model: [claude-sonnet-4 ▾]    │
│                               │
│ [Cancel]  [Spawn]             │
└───────────────────────────────┘

Dependencies on Core

| Import | Usage | |--------|-------| | bootstrap | Initialize core | | WorkhorseContext | Service access | | HookEmitter | Event subscription | | Tracker | Issue management | | HarnessOrchestrator | Agent lifecycle | | MemoryService | Chat/notifications | | MonitorService | Monitor status | | AgentAdapter | Agent state/control |

Why This Architecture

  1. Reactive primitives — Clean separation between data fetching and rendering
  2. Hook-based updates — UI stays in sync without polling
  3. Plugin renderers — New tools can customize their activity display
  4. Focus management — Vim-like navigation without mouse
  5. Setup-first flow — Users don't hit auth errors mid-session
  6. Provider pattern — Core services available throughout component tree