Higher-Level Primitives
Published on:
Higher-Level Primitives
Primitives are an interesting concept.
In our programming languages we have char, int, float, boolean. Perhaps some others I'm forgetting.
We take these primitives and we compose them into structs. We take those structs and compose them again. We take statements: if, for, while, and compose them into functions and we take functions and compose them into systems.
Each of these layers is itself a primitive. Functions are primitives, especially in languages that let us pass them as arguments.
Each layer is a primitive, and I need primitives at a higher level.
My professional workflow involves Jira, Asana, GitHub, and a few other enterprise tools. You are probably, like me, too familiar with these.
CLIs
Some higher-level primitives I've found are CLIs. They allow me, and more importantly AI, to interact with each of these systems. But I've found most CLIs to be both, paradoxically, too powerful and too complex.
Just as an API can have hundreds of endpoints, so too can a CLI have hundreds of commands... 👀 kubectl in particular comes to mind.
I use the open-source Jira CLI. This tool does everything I need, and I can toss it to Claude or Codex to manage my tickets. But sometimes I want to do what needs to be done quickly and easily. I may need to assign a bunch of tickets to myself, add a label, move them from 'To Do' to 'In Progress', and throw in a comment or two. Remembering all the commands, flags, ticket numbers, and everything else is a feat I have no interest in mastering. I don't have it in me, nor do I want to.
I need a higher level primitive.
The higher-level primitive I want is not simply a CLI or a TUI, or a web app. It is a discoverable interface for taking repeatable actions against a batch of entities.
A CLI is a great start because it provides a programmatic interface into the system. An API does the same. Both get us the data we need to process and render how we want.
What I want instead is some sort of UI with the speed and precision of a CLI, but with enough visual support to understand the actions I can take and the entities against which I can take them.
TUIs
My background is as a frontend developer. Frontend is fascinating. We interact with the same sorts of problems as other software domains, but on a smaller scale. Fundamentally, though, the problems are the same: race conditions, managing concurrency, coordinating state updates, cache invalidation, and performance.
Frontend is more than web apps. It's more than mobile apps. Frontend is also the CLI and the TUI. On the web, a frontend engineer's best friend is React. At its core, React is a rendering system with scheduling capabilities: when state changes, React determines what parts of the UI have changed and schedules them to re-render.
React is great. It, and the web platform, provide a phenomenal developer experience: hot module reloading, great developer tools for introspection, testing frameworks, an ecosystem of exceptional dependencies, and everything else that comes with a great language and runtime. And all for, if not free, an extremely low cost.
Wouldn't it be cool to update a TUI's codebase and see the running instance display the change immediately and automatically? It would feel surreal until it became expected.
React, and generally TypeScript is not just for browser, these capabilities are generic. TypeScript can run everywhere.
TUIs are also frontend. If we wanted to use React to build them we would need a good component library to compose rich and performant user interfaces... enter Ink! Ink is a React component library for terminal environments. It's a TUI component library!
Another lesson learned from web apps is the importance of storing server state separately from client state. We also want metadata about our server state and a way to track the progress of actions against it. Luckily, the frontend ecosystem gave us TanStack Query, and all our work became easier.
These are a wonderful set of primitives to build with, but they are not yet what I actually want.
Jira TUI
I want a Jira TUI.
We can take the Jira CLI tool, create some TypeScript functions to programmatically invoke it, capture the response, which is kindly made available as JSON, chuck that JSON into our TanStack Query cache, compose Ink components for our UI, manage frontend state with React, and build a fully fledged TUI with all the niceties of a modern web app.
We can also leverage the environment and delegate functions to other CLI tools, like FZF for fuzzy finding in a menu or ripgrep for searching.
A flow I want to easily support is something like: select a Jira board, filter to the tickets assigned to me, select a few of those, and update their status from 'To Do' to 'In Progress'. I want to be able to do this quickly, and I don't want to memorize a bunch of commands or ticket numbers. I want a previous step to carry forward into the next, reducing to some final selection of entities against which I then take an action.
Another flow might be to select an epic and create a bunch of tickets, with all the required fields prefilled, in this selected epic. I don't want to create the tickets one by one. I may have 12 tickets I want to get added and I want to create and add them just by writing down each ticket's title on a new line. For some I may even want to include a description.
Wrapping Up
These are the capabilities I want in the primitives of my workflow: discoverable, batch-oriented actions. And I think this is what most users want from their tools.
So finally we have arrived at the primitive I want. It's a very high-level one, built on many smaller primitives, each composed with its neighbors. Because, really, what is an int but a composition of a handful of bits?
The point is not to turn every application into a TUI. It is to build tools that make powerful actions fast, discoverable, and predictable for the humans using them.