Vibe yourself a better ghcid

ghcid is a tool for getting fast type checker feedback when writing Haskell programs. I vibe coded new features to ghcid which make it much better for use by a coding agent. I can’t share the code, unfortunately, because my workplace’s open source release process is too bureaucratic to make that worthwhile. I can, however, describe what those features are, in case any one else wants to vibe or hand code their own equivalent, and maybe share the result with us all!

The features are to allow communication with ghcid via a socket and a client script (ghcid-client), to be able to add and remove files from consideration, to patch ghcid bugs around creating and deleting files, and to terminate gracefully.

Vibe coding the new features will take perhaps an hour of wall clock time, and require only gentle oversight of the coding agent.

Features

Agent socket API: Add a command line flag (for example --agent) that makes ghcid create a Unix socket1 for communication. Incoming messages should be formatted in JSON. ghcid should process messages in the order they are received and reply in JSON.

Client script: Add a client called ghcid-client which can be called with --start to launch ghcid --agent. Subsequently, ghcid-client can be used to send and receive messages on the socket to invoke ghcid functionality. This functionality is exposed to users (including agents) through ghcid-check command line flags (see below for details of these new features and flags). ghcid-client could be a shell script written using socat (for socket interaction) and jq (for composing and parsing JSON messages), or could be written in Haskell using a socket library and JSON library.

Reload trigger: Add a socket command that causes ghcid to reload its source files, type check them and return the type checker output over the socket. This can be exposed through ghcid-client --reload.

Add and remove files at run time: ghcid --agent starts up with no files loaded, and files can be added to and removed from ghcid’s working set by sending socket commands. Those socket commands can be exposed, for example, through ghcid-client --add and --remove. The benefit of this feature is that startup is quick and you can restrict ghcid’s attention to only the files that you care about, saving substantial memory in large projects.

Synchronous operation: Reload, add and remove requests must be run through ghcid’s normal processing to allow it to updated diagnostics and watched files before replying. It should not send any status updates asynchronously, i.e. not “opportunistically” or “unsolicited”.

Deletion recovery: After files or directories are deleted, ghcid can lose track of what to watch. My agent patched it to retain watches after failed loads and detect when deleted paths return.

Completion-marker fix: ghcid has a bug where using -Werror causes problems (#429). It’s easy for an agent to patch.

Clean shutdown: Firstly, ghcid should handle SIGTERM properly. SIGTERM is a common signal to send to a process to trigger it to shut down gracefully but, by default, Haskell does not install a signal handler for it. Consequently, SIGTERM will terminate ghcid without giving it the opportunity to remove its socket. For SIGINT, the signal that is normally sent on Ctrl-C, there is a default signal handler that throws UserInterrupt to the main thread. We can adjust our ghcid to install a signal handler for SIGTERM that throws UserInterrupt to the main thread too. Secondly, add a ghcid-client --shutdown flag to trigger shutdown. (Thirdly, an additional feature I haven’t yet implemented: ghcid should shut itself down if its socket is deleted.)

If you vibe yourself a ghcid along these lines please share it as open source and publicise it!


  1. And maybe on Windows it could create a named pipe?↩︎