CLUI: Building a Graphical Command Line (2020)
blog.replit.com
blog.replit.com
The most backwards compatible format to achieving this would be a interface like abstraction on top of regular binaries (similar to the concept of TS interfaces adding type completion to JS) which describe a list of parameters/flags and their valid values/data types with an associated description. I think a system similar to this gives an intuitive and extensible suggestion system without requiring inconceivable redesigns to existing systems whilst maintaining the familiarity and ease of use of the regular command line.
We defined a declarative standard that lets you describe the structure of any CLI tool. Fig then uses these to provide VSCode-style autocomplete in the terminal!
Currently specs are manually contributed by the community, but we are planning to integrate with CLI libraries (cobra, commander, click, etc) so they can be generated automatically!
You can see some examples here: https://github.com/withfig/autocomplete
- You need to press tab to get any completion to display
- You don't know what completion will appear when you press tab, or at most one completion displays
- The completions are not at all aware of context, at best just doing some untyped fuzzy matching. Compare this to Fig, which at least seems to know what sorts of arguments each command accepts and displays a list of them
- They only provide completion, not inline documentation, which is one of the big points of the article
- It autosuggests on the command line without typing TAB
- The completion is programmable, so git completion knows where a branch is expected or a path is expected, etc.
- When you hit TAB, you get descriptions of each completion
- You can get the completions to display as you type (see zsh-autosuggestions, zsh-autocomplete).
- Completions are completely aware of their context, probably more so than what fig can infer. You do need to actually load the completion functions of your binaries though, which are traditionally named ‘_command’ (so, an example would be ‘_git’).
- They can provide documentation; fzf-tab does so.
You only need to do that if you want to bring focus to the completion selection. A lot of interfaces will display completions without the need to press tab. And to be honest, while I'm all for making things simpler, I don't think pressing tab is a hard barrier to expect people to overcome.
> You don't know what completion will appear when you press tab, or at most one completion displays
This doesn't make a whole lot of sense. If you know what completion is going to appear then you're hitting tab to save keystrokes. If you don't know the command then of course you're not going to know what completions will display since the whole point of hitting tab is to explore the valid options.
> The completions are not at all aware of context...
That's not even remotely true of Bash, let alone any modern shells like murex and fish.
Murex even goes one step further and doesn't just display the parameters in context of the command that's being run (eg suggesting names of available branches when running `git checkout <tab>`) but it runs the entire command line that precedes it to understand the data being passed into the STDIN of the current command. This is useful when using tools that inspect JSON properties for example:
murex-dev» open https://api.github.com/repos/lmorg/murex/issues | [[ ]]
(builtin) Outputs an element from a nested structure
/0 /0/active_lock_reason /0/assignee /0/assignees
/0/author_association /0/body /0/closed_at /0/comments
/0/comments_url /0/created_at /0/events_url /0/html_url
/0/id /0/labels /0/labels/0 /0/labels/0/color
/0/labels/0/default /0/labels/0/description /0/labels/0/id /0/labels/0/name
/0/labels/0/node_id /0/labels/0/url /0/labels_url /0/locked
> They only provide completion, not inline documentation, which is one of the big points of the articleNope. In murex if you type `kill <tab>` you'll get a list of process names instead of PIDs and when you select one it is still the PID that is placed.
murex-dev» kill
(/bin/kill) kill - terminate or signal a process
543 /Applications/Visual Studio Code.app/Contents/Frameworks/Code Helper (Renderer).app/Contents/MacOS/Code...
738 /usr/libexec/promotedcontentd
1645 /System/Library/PrivateFrameworks/DifferentialPrivacy.framework/XPCServices/DPSubmissionService.xpc/Con...
17903 /Applications/Slack.app/Contents/Frameworks/Electron Framework.framework/Helpers/chrome_crashpad_handle...
47968 /System/Library/Frameworks/Metal.framework/Versions/A/XPCServices/MTLCompilerService.xpc/Contents/MacOS...
496 /Applications/iTerm.app/Contents/XPCServices/pidinfo.xpc/Contents/MacOS/pidinfo
Likewise if you type `git <tab>` you will get a list of all the next commands that follow and what they do: murex-dev» git
(/usr/bin/git) git - the stupid content tracker
init Create an empty Git repository or reinitialize an existing one
restore Restore working tree files
revert Revert some existing commits
submodule Initialize, update or inspect submodules
push Update remote refs along with associated objects
The output is colourised and highlighted so it makes more sense in a terminal than it might appear in here. But you get the idea. And all of the suggestions are scrollable with the cursor keys, you can quickly jump by typing more characters, or search for specific completions using regex if you press ctrl+f (this last feature also makes it very quick to traverse large directory structures in `cd`)The biggest issue with traditional shell completions is that they are kind of tricky to build. Everything is defined imperatively and written as a shell script.
We wanted to lower the barrier to building completions and make a representation that works across different shells.
A big problem I came across is that any shell-agnostic solution is likely to be hard/impossible to implement in bash, which is the most popular shell :)
Since Oil already emulates bash, it was easier just to implement bash APIs and get a huge corpus of completions for free, rather than try to develop a new corpus.
It looks like Fig does things in the terminal emulator / OS and not the shell, which is interesting. Does an approach like that work on Linux?
Another problem I ran into is that GNU readline is pretty limited as far as the UI goes. Fish and other shells have a better UIs but they are coupled pretty tightly to the shell.
Fig currently works with bash, zsh and fish. Since we are operating at the OS level rather than in the shell, we can get around the inflexibility of readline/bash.
In theory, this approach would work anywhere. What we do currently on macOS is especially 'involved' since we need to provide the completions UI in a separate GUI app that we don't control. But, in general, integrating at the OS/application level should be possible on Linux and Window as well.
As mentioned elsewhere I wanted to have some invariants for correctness, but maybe not all of them were necessary. I felt it was useful to separate the problem into completing the shell language vs. completing the argv.
Feel free to join the Oil's Zulip channel (link on home page https://www.oilshell.org/)! There are the past discussions on #shell-autocompletion, going back a couple years. It's dormant now, but as mentioned, there were multiple people who wrote code towards this. And there were debates around the issues above.
Another interesting channel that just started is #shell-gui. Short summary which I have yet to blog about: I just implemented "headless mode" for Oil (analogy to headless Chrome). So I can punt the UI for the shell to multiple other projects :) As I mentioned a few times, I realized the scope of the project is too big. I have a collaborator Subhav who has a prototype of a GUI in Go (and shell).
Basically the shell a language- and text-oriented interface, but it does NOT have to be terminal-oriented interface. It took me awhile to realize that we shouldn't conflate those things! And it was pretty easy to tease them apart in Oil. The headless mode provides a simple interface and allows integration that can't be done with bash (or any other shell AFAIK).
It needs feedback from people who want to build GUIs. An easy analogy is to imagine is a browser-like GUI for a shell with the URL bar as the prompt. The URL bar provides autocompletion, history, and shows state, etc. just like shell does.
So I’m not convinced this is still a hard thing to do. (The ui of fig does look slick though.)
Cobra and other CLI libraries, like oclif, can help you generate the skeleton of the CLI, stuff like subcommands, options, etc. (I'm in the process of writing the integration so you can generate a Fig completion spec the same way!)
The difference is that with Fig you can add richer completions as well. For instance, Fig's completion for `npm install` allows you to search across all npm packages: https://twitter.com/fig/status/1385401292193865731
You say that but it's actually an easier problem to solve than it first appears. Both my shell, murex[0], and Fish[1] parse man pages to provide automatic autocompletions for flags.
I also have a secondary parser for going through `--help` (et al) output for programs that don't have a man page. This isn't something that is enabled by default (because of the risk of unintended side effects) but it does work very effectively.
They way I look at it though, is you want to cover as many things automatically as you can but there will always be instances where you might want to manually fine tune the completions. For that, I've also defined a vastly simplified autocompletion description format. It's basically a JSON schema that covers 99% use cases out of the box and supports shell code to be included to cover the last 1% of edge cases.
This means you can write autocompletions to cover common usecases and have the shell automatically fill in any gaps you might have missed.
A couple years ago I wanted to tackle this problem for Oil, and there was some brainstorming here:
https://github.com/oilshell/oil/wiki/Shell-Autocompletion
https://github.com/oilshell/oil/wiki/Shellac-Protocol-Propos...
and on the oilshell Zulip in #shell-autocompletion.
The author of the Ion shell went pretty far with it, but it seems to be dormant now (or correct me if I'm wrong):
https://www.redox-os.org/news/rsoc-ion-ux-2/
https://www.redox-os.org/news/rsoc-ion-ux-3/
https://www.redox-os.org/news/rsoc-ion-ux-4-5/
To me a problem is that most shells like bash and even zsh are too impoverished to implement some kind of common protocol. For one, they don't work well with structured data.
I just looked at Fig, and it seems interesting as well. The approach of using Mac-specific APIs and doing it outside of the shell skirts that problem.
I did want to make Shellac correct and precise. Specifically it should have the invariant that if the tool suggests a completion, it should actually be a prefix to a valid command! And it was also supposed to separate the problem of completing the shell language from completing the argv language -- those are two different things. For example shells complete variables like $P -> $PATH, etc. And they complete their own builtins.
The docs on my schema is https://murex.rocks/docs/commands/autocomplete.html but it was a little rushed so could use more examples and a little more TLC.
Also this XKCD strip feels apt: https://xkcd.com/927/
The goal was to improve on the state of the art, which is hard. The original motivation for Shellac is here and I discussed it with many people. The Ion shell author wrote some code toward it, and so did a zsh dev.
https://github.com/oilshell/oil/wiki/Shellac-Protocol-Propos...
But it didn't really go anywhere for a variety of reasons. One is the "bootstrapping" problem. That is, there's no incentive for anyone to implement shellac if there's no corpus of completions. Likewise there's no incentive for the author of new-popular-tool to use it if no shells implement it.
The other problem is whether the completion is declarative or imperative, which you hinted at. If it's more imperative then you've automatically coupled it to a language like Murex. If not then I think the UI will be limited and it will break some of the invariants I wanted (but are maybe not strictly necessary).
I think that most people on the #shell-autocompletion Zulip wanted it to be declarative. I was one of the few people who said that declarative isn't enough, mainly because the ACTUAL command lines are written in imperative languages. It's sort of an argument around computational power.
To me the fact that all the popular systems are imperative is evidence in favor of that, until reality shows the opposite :)
I also wanted to push the burden of developing the logic on the people who write the tools -- THEY know what its syntax is. This also solves versioning problems. So the idea was that EITHER the shell or the TOOL ITSELF would be a Shellac server. In the latter case, it would never get out of sync, which does happen in bash, zsh, and fish.
(Although to be fair if you dynamically scrape --help, that also solves the versioning problem, but not if you bake it in. bash-completion actually DOES the dynamic execution and scraping of 'ls --help' on TAB, which I didn't know before implementing Oil's autocompletion )
Actually I just remembered I enumerated some "unusual cases" here:
https://github.com/oilshell/oil/wiki/Diversity-in-Command-Li...
Is the escape hatch for Fig in JavaScript? One thing I would like to see "rescued" from Shellac is the idea of allowing the escape hatch to be in any language, not Oil or Murex, or JS. In particular, it could be the language that the tool itself is written in!
I somehow feel that all these boil-the-oceans approaches regarding rich CLIs aren't going to make it very far.
Which is really unfortunate :-(
As for covering every flag for every tool, some shells already parse man pages and —-help output to provide meaningful autocompletions automatically. Murex does this (https://murex.rocks) as does fish (https://fishshell.com)
There is probably something like that for bash too.
Yes, shells can be made more interactive, but that requires setup and prior-experience and know-how. How would someone new know to install zsh and something like ohmyzsh?
We need to go back to first principles.
Just because it should be convenient to type and navigate with a keyboard doesn't mean the command line can't have structure beyond just the characters on it.
I can fill in a webform just the keyboard. Tab, type, tab, type, tab, type, tab to the submit button, hit enter. Done.
So I didn't have to type "--" anywhere. I didn't have to type the field names at all actually. Not saying I want them fixed per se, but "telling them apart" shouldn't be a real question in a graphical terminal UI.
Likewise we should never escape strings or quote strings in a Graphical Terminal IMHO. They should just be text fields integrated seamlessly into the line, with visible boundaries.
If you copy the line and paste it in a text editor, then sure, maybe show all this ghastly encoding happening underneath. But in the GUI Terminal... nah I don't accept it.
EDIT: While we're at it, I don't think we should use monospace fonts in a GUI terminal. Certainly a font with distinct letters like lI1 and 0O, but not monospaced.
which also had semantically driven clickable data structures one could use to build up a command line invocation. In about 1982.
The goal is to layer on a CLUI style interface to all of the CLI commands you already use!
Still hasn't caught fire though, even for me. I respect it but I still reach for bash. I'm not really sure why except perhaps momentum.
1. Humans can understand text too!
2. Text can travel over every transport.
3. Text is benign.
4. Large text is large and small text is small, but all objects look the same size.
https://medium.com/@octskyward/the-woes-of-powershell-8737e5...
1. Powershell isn't a complete greenfield and exists in a context, which explains many of the complaints.
2. any new tech use requires learning.
I could address each point but most galling was the conclusion:
"At this point we realise the truth — PowerShell is not designed to be an actual shell for users" Because wget did not output to a file by default?!? Really?!?
IMO, this article failed to hit the mark back in 2014 when it was written let alone now.
Edit: conciseness
That's learned helplessness.
My mother, who is a distinctly non-technical person, learned to use IRC when she was 60. Why? Because her friends were already using it. She wrote down a series of commands that her friends sent her in email, carried them out, and then spent years happily chatting with them.
Humans are very good at learning complex things when they see something they want at the end of it.
Terminals are something I find lot of people take for granted, and don't really stop to think how they actually work. Very much a black box.
I mean what is a terminal. It's chat with a bot we call "the terminal". We can look at chat apps and see how we can integrate graphics and commands into the chat without it looking like obscure 80s thing.
Non-technical people will use whatever they are being asked to use. If the "CLI" was popular amongst software authors (as it once was in the 1980s), people would use it and get things done the same way they do with "GUIs". I saw this first hand. Computers for many folks are just a means to an end.
The CLI if it were the chosen standard might make today's "tech" company strategies more difficult. How would ads be delivered. How would "dark patterns" avoid discovery. How would vendors justify "upgrades" without resource-hungry GUIs that slow down as they bloat.
> Command line interfaces. Once that was all we had. Then they disappeared, replaced by what we thought was a great advance: GUIs.
The CLI is still incredibly relevant, it’s core to this day to single most important task you can perform with a computer: Programming. There’s only a few applications that can safely be considered more important: the browser, the spreadsheet, the word processor, maybe the email client. After that you start to get to things similar in relevance to the terminal. That’s not “disappearing”, that’s having enduring importance that makes it a monumental achievement deserving of a place in the software pantheon. The CLI only gets punished because it was once even more important, before the GUI. We should all be striving to create software with the enduring importance of the terminal.
The trick is to bring it to mainstream audience in a way that's not awkward.
The closest thing I've seen to mainstream terminal is the JavaScript console in browsers but... that's again for programmers (albeit also casual ones).
In professional apps like premiere, the closest thing to a terminal is a search box that's smarter than just looking up words in the documentation. And that is preeeeeeetty shit.
We can do better.
https://docs.blender.org/manual/en/latest/editors/python_con...
But I still don't find this very mainstream. It's a bit like the JS console in browsers.
Some of the stuff that is immediately useful to mainstream users:
- ncdu
- youtube-dl
- spotdl
- aria2
- Homebrew Casks
People aren’t dumb. If these were marketed properly to them, they would use them.
+ Search engine search bars
+ voice services like Siri and Alexa
+ Excel formulas
+ Slack and MS Teams use of IRC-like /commands in the chat window
Why do you say that? This article is explicitly talking about programming tools, not mainstream tools. "At Repl.it, where our goal is to build a simple yet powerful programming environment"
The implementation seems to be missing the point of the cli, scripting. If there is not a primitive way to combine and chain commands and reduce repetition, then this doesn't look like enough by itself to sway a userbase.
Mangle it with sed, grep, awk, cut, sort, perl, wc, whatever
Get the result you want
Dump the instructions in a script
Optionally stick echo -e "Content-Type: text/plain\n\n" at the top of the script, chmod 755 and host
Beautiful, job done, problem solved, move on.
At the end of the day, I think GUIs & CLIs are targeted for different people and folks should pick which works for them.
Still, this replit "clui" might be pretty good execution of the idea, so I'm not dismissing it completely. Might be interesting experiment to plug it into powershell backend to see how well it works with more real-world stuff.
I posted some videos on Twitter on how we use it in our app. Basically any place where you can image a lot of UI bloat, it's replaced by a CLUI bar: https://twitter.com/amasad/status/1390005374741143554
Offhand, image-et-al preview still feels like a mess, a million ways of doing it and none are that good.
Also, I dream about something like "fzf, but with a very clearly delineated out-of-the-current-shell window,perhaps like rofi" or something?
Best tools with complex logic like CAD software usually combine CLI, CLUI and GUI to be ergonomic.
There's an awesome public domain fork of TempleOS called Shrine[0] that has networking and a normal shell. When is someone going to throw some chrome and polish on top of that and sell it as the OS of the future? I'll back your kickstarter or whatever.
i guess that is a sign of success?
click "edit on replit" at the article and you go to:
https://replit.com/@util/replit-blog?fileName=posts/clui.md
it shows:
> 70 Reactions
> 47 comments