<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>The Accessible Engineer: Derek Riemer (Posts about productivity)</title><link>https://derekriemer.com/</link><description></description><atom:link href="https://derekriemer.com/categories/productivity.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:derek@derekriemer.com"&gt;Derek Riemer&lt;/a&gt; </copyright><lastBuildDate>Thu, 28 May 2026 00:17:02 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Designing a Terminal for an Audio-First Workflow</title><link>https://derekriemer.com/posts/2026/03/15/designing-a-terminal-for-an-audio-first-workflow/</link><dc:creator>Derek Riemer</dc:creator><description>&lt;h1 id="designing-a-terminal-for-an-audio-first-workflow"&gt;Designing a Terminal for an Audio-First Workflow&lt;/h1&gt;
&lt;p&gt;Most shell environments assume a visual interface. Prompts are colorful, information-dense, and optimized for quick scanning. My workflow is different. I'm blind, and I work with a screen reader, which means my terminal is fundamentally an audio interface. Instead of scanning the screen, I'm listening to the environment as I work. That changes what matters. &lt;/p&gt;
&lt;p&gt;Most terminal workflows carry a lot of unnecessary noise such as long paths, redundant context, and visual-only cues. Designing for audio forced me to remove that noise entirely.&lt;/p&gt;
&lt;p&gt;The result is a shell environment that’s faster to navigate, easier to parse, and often better even if you can see the screen. If you like what I describe here, take a look at my dotfiles repo for your own inspiration. If you are interested in what path substitution built directly into shell configuration might look like, and maintain shell code, I'd love to talk.&lt;/p&gt;
&lt;h2 id="where-this-started"&gt;Where this started&lt;/h2&gt;
&lt;p&gt;While I was working at Google, I spent a lot of time inside the monorepo. (If you haven't encountered that model before: imagine nearly the entire company's code living inside one enormous repository). The ideas that eventually led to this system started there, but the implementation in this repository was written later from scratch and is entirely my own code.&lt;/p&gt;
&lt;p&gt;The paths were extremely long. In some cases it could take several seconds for my screen reader, even reading at 700 WPM, just to tell me where I was during a large refactor. A typical path might look something like this (simplified):&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;/mounted/path/to/the/monorepo/perforce_client/root/javascript/namespace_part/namespace_subpart/namespace_subpartsubpart/app_root/subsystem/main/tests/audioInformation_test.ts
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For a sighted developer, this is mostly a visual annoyance. You glance at the prompt, pick out the important segment, and move on. For me, the terminal had to speak the entire thing, or I had to interrupt my work and read it piece by piece. Every time the prompt appeared, my screen reader would start reading the path, and after a while I realized I often had no idea where I was unless I waited several seconds for the prompt to finish speaking. That was not going to work.&lt;/p&gt;
&lt;p&gt;So I started building tools to compensate. First came "teleport" functions. These were small helpers that jumped directly to important parts of the repository:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;function jsdf {
  cd &amp;lt;path_to_google_drive_javascript_code&amp;gt;
}

function jdf {
  cd &amp;lt;path_to_java_drive_code&amp;gt;
}
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I can't show the real internal layout because I don't want to reveal how Google's internal monorepo is organized, but &lt;code&gt;jsdf&lt;/code&gt; simply meant "JavaScript Drive frontend code." This saved a ton of time in typing along, a simple &lt;code&gt;jsdf;cd infra&lt;/code&gt; would get me somewhere I needed to be to do something on a specific file, but it wasn't enough. These worked, but they were crude, and  I was still constantly hearing huge paths in the prompt. Eventually I added a small rewriting pipeline that shortened common segments before displaying the path. The first version was extremely simple:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;sed -e 's/path_1/short_segment/' \
    -e 's/path_2/another_short/'
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It was ugly, but it proved something important: shortening paths dramatically reduced cognitive load. To make that concrete, here is the kind of transformation I was trying to achieve.&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;/mnt/c/users/driem/programs/python/ai_image_describer/main/tests/audioInformation_test.ts
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;py/ai_image_describer/main/tests/audioInformation_test.ts
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Saving keystrokes was important, but not as important as saving attention. Hearing a short, predictable path segment is far easier than listening through a long directory hierarchy every time the prompt appears. At Google, the savings was well over two thirds the path length, and left me more mental capacity to actually think about the problem at hand. That idea eventually evolved into the alias and namespace system I use today.&lt;/p&gt;
&lt;h2 id="making-terminal-sessions-resilient"&gt;Making terminal sessions resilient&lt;/h2&gt;
&lt;p&gt;My workflow makes this problem harder to ignore. At Google, and still today, I am often working from home, a café, my desk, or even while camping in the middle of the desert, so most of my development happens inside a persistent tmux session on a remote machine. I jump between windows, reconnect from different devices, and keep long-running work alive in the background. That means I'm constantly re-orienting myself: switching panes, reattaching sessions, and resuming work. Every time I do, the first thing my terminal speaks is the full working directory. If that path is long, I'm back to waiting several seconds just to answer a simple question: "Where am I?"&lt;/p&gt;
&lt;h2 id="path-aliases"&gt;Path aliases&lt;/h2&gt;
&lt;p&gt;The first step was simple: shorten frequently used paths. For example, instead of typing &lt;code&gt;cd /mnt/c/users/driem&lt;/code&gt;, I can define an alias:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;driem /mnt/c/users/driem
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and then navigate with:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;p driem
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;p&lt;/code&gt; command is a thin wrapper around &lt;code&gt;cd&lt;/code&gt;, backed by the alias map. I also use &lt;code&gt;pd&lt;/code&gt; for &lt;code&gt;pushd&lt;/code&gt; when I want a quick stack to bounce between locations. Both commands support tab completion over the alias names, so navigation stays fast even as the alias set grows. This alone reduces both typing and how much the prompt has to speak.&lt;/p&gt;
&lt;p&gt;Each alias also exports an environment variable. For example, &lt;code&gt;P_driem&lt;/code&gt; - which makes it easy to reuse paths in scripts:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;tail -f $P_driem/log.txt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This helped, but it didn't scale well once directory structures became deeper and shortcuts naturally nested. That led to the namespace system.&lt;/p&gt;
&lt;h2 id="namespaced-paths"&gt;Namespaced paths&lt;/h2&gt;
&lt;p&gt;Earlier versions of my environment experimented with something I called &lt;strong&gt;namespaced paths&lt;/strong&gt;. The idea was to treat filesystem paths more like hierarchical identifiers than raw strings. After the &lt;code&gt;sed&lt;/code&gt; rewriting pipeline, I briefly invented a space separated list of simple substitutions, applied in order. This was much better, but I started desiring something that wasnn't just a prefix match, and didn't strictly do a longest match either. This lead me to a new, more elegant, solution.&lt;/p&gt;
&lt;p&gt;The new system is fundamentally a key-value store based on aliases. Each alias has two roles: a real filesystem expansion used for navigation, and a display representation used when rendering the prompt. That distinction allows the prompt to either preserve hierarchy for orientation or collapse it to reduce noise, depending on how an alias is defined. The prompt uses a display path rather than a literal filesystem path, optimized for readability rather than exact reproduction. In the rare case I need the real path, I'll just run &lt;code&gt;pwd&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Example aliases:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;driem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mnt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;driem&lt;/span&gt;
&lt;span class="n"&gt;gmscripts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;driem&lt;/span&gt;&lt;span class="o"&gt;]/&lt;/span&gt;&lt;span class="n"&gt;mydrive&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;software&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gm_scripts&lt;/span&gt;
&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;driem&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;programs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="n"&gt;easy_ui&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;easy_ui&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="literal-paths"&gt;Literal paths&lt;/h3&gt;
&lt;p&gt;If an alias target starts with &lt;code&gt;/&lt;/code&gt;, it is treated as a normal filesystem path:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;driem /mnt/c/users/driem
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="visible-composition-with-alias"&gt;Visible composition with &lt;code&gt;[alias]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;If the target starts with &lt;code&gt;[alias]&lt;/code&gt;, the referenced alias is expanded for the real path while remaining visible in the prompt:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;gmscripts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;driem&lt;/span&gt;&lt;span class="o"&gt;]/&lt;/span&gt;&lt;span class="n"&gt;mydrive&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;software&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gm_scripts&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This keeps the parent namespace visible when it carries useful meaning. When working inside gm_scripts, the prompt can display:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;driem&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;gm_scripts&lt;/span&gt;&lt;span class="p"&gt;$&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id="hidden-prefix-composition-with-alias"&gt;Hidden-prefix composition with &lt;code&gt;alias/...&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;If the target starts with &lt;code&gt;alias/...&lt;/code&gt;, the alias is expanded for the real path but its ancestry can be collapsed in the prompt:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;py driem/programs/python
easy_ui py/easy_ui
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When working inside &lt;code&gt;easy_ui&lt;/code&gt;, the prompt can simply display:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nv"&gt;easy_ui&lt;/span&gt;&lt;span class="p"&gt;$&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;while the filesystem path remains long.&lt;/p&gt;
&lt;h3 id="mental-model-of-the-algorithm"&gt;Mental model of the algorithm&lt;/h3&gt;
&lt;p&gt;Conceptually the system resolves aliases while tracking two outputs: the expanded filesystem path, and the compressed display path.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;starts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;real_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;starts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nl"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;expand&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;real&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;path&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;path&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;starts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nl"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;expand&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;real&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;path&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;allow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;collapse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;prefix&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When rendering the prompt, the system finds the best matching expansion and substitutes the corresponding display form.&lt;/p&gt;
&lt;h3 id="concrete-example"&gt;Concrete example&lt;/h3&gt;
&lt;p&gt;Real filesystem path:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;/mnt/c/users/driem/programs/python/easy_ui
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Prompt display:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;easy_ui
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The filesystem stays long and stable, but the spoken prompt stays short.&lt;/p&gt;
&lt;h2 id="hacks-to-make-reading-code-faster"&gt;Hacks to make reading code faster&lt;/h2&gt;
&lt;p&gt;Another trick that speeds up spoken output is shortening how punctuation is pronounced. Instead of hearing every punctuation symbol spoken in full, I use shorthand pronunciations:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;symbol&lt;/th&gt;
&lt;th&gt;pronunciation&lt;/th&gt;
&lt;th&gt;shorthand&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;(&lt;/td&gt;
&lt;td&gt;left paren&lt;/td&gt;
&lt;td&gt;par&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;)&lt;/td&gt;
&lt;td&gt;right paren&lt;/td&gt;
&lt;td&gt;ren&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[&lt;/td&gt;
&lt;td&gt;left bracket&lt;/td&gt;
&lt;td&gt;brà&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;]&lt;/td&gt;
&lt;td&gt;right bracket&lt;/td&gt;
&lt;td&gt;ket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{&lt;/td&gt;
&lt;td&gt;left brace&lt;/td&gt;
&lt;td&gt;curl&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;}&lt;/td&gt;
&lt;td&gt;right brace&lt;/td&gt;
&lt;td&gt;lea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:&lt;/td&gt;
&lt;td&gt;colon&lt;/td&gt;
&lt;td&gt;coal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;;&lt;/td&gt;
&lt;td&gt;semicolon&lt;/td&gt;
&lt;td&gt;dah&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For example, a typical C-style loop would normally be spoken like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;paren&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;equals&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;zero&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;semi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;less&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ten&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;semi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;plus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;plus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;paren&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;brace&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With shorthand it becomes:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;par&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;zero&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dah&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;less&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ten&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dah&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;plus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;plus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ren&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;curl&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This dramatically reduces how long it takes to listen to code. The learning curve was a little weird at first, but I adapted to it within a week.&lt;/p&gt;
&lt;h2 id="small-shell-tweaks-that-reduce-friction"&gt;Small shell tweaks that reduce friction&lt;/h2&gt;
&lt;p&gt;Most of the rest of the repository consists of small adjustments to default shell behavior.&lt;/p&gt;
&lt;h3 id="nice-to-have-immediate-history-syncing"&gt;Nice to have: Immediate history syncing&lt;/h3&gt;
&lt;p&gt;Normally Bash writes command history when a shell exits, which means that if you have multiple terminals open, commands from one session may not appear in another until much later. This configuration appends history immediately after each command so history search remains consistent across terminals, and survives reboots.&lt;/p&gt;
&lt;h3 id="per-machine-overrides"&gt;Per-machine overrides&lt;/h3&gt;
&lt;p&gt;The repository includes a &lt;code&gt;.bash_local&lt;/code&gt; file that allows machine-specific configuration. Local settings are stored separately so the main configuration remains portable across machines. Something as trivial as the hostname adds words to my prompt, so the host is one such local knob, and only remote systems have that knob turned on.&lt;/p&gt;
&lt;h2 id="bootstrapping-and-configuration-drift"&gt;Bootstrapping and configuration drift&lt;/h2&gt;
&lt;p&gt;Installers sometimes modify shell startup files without asking. To reduce ambiguity, this repository bootstraps itself by symlinking dotfiles into &lt;code&gt;$HOME&lt;/code&gt;, so there is always one canonical copy. If &lt;code&gt;.bashrc&lt;/code&gt; changes unexpectedly, the difference becomes immediately visible and I know exactly who to blame for messing with my config without asking.&lt;/p&gt;
&lt;h2 id="wsl-as-a-practical-compromise"&gt;WSL as a practical compromise&lt;/h2&gt;
&lt;p&gt;I split my time between WSL and native Linux. Strong accessibility tooling on Windows provides a great accessibility environment for me, while Linux offers the developer tooling I prefer. WSL lets me keep a Linux shell while still accessing the Windows GUI when necessary, Linux GUI accessibility is ... complicated.&lt;/p&gt;
&lt;h2 id="a-tiny-vim-tweak-that-matters"&gt;A tiny Vim tweak that matters&lt;/h2&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;set noru
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This disables Vim's ruler display. For a screen reader, the ruler constantly announces line and column numbers, and turning it off removes unnecessary speech. In general, the only time I want changing text on screen is if that text matters right now.&lt;/p&gt;
&lt;h2 id="designing-terminals-for-listening-instead-of-looking"&gt;Designing terminals for listening instead of looking&lt;/h2&gt;
&lt;p&gt;Most terminal environments assume the user is visually scanning the screen. When the interface is audio, different tradeoffs emerge:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Visual shell design&lt;/th&gt;
&lt;th&gt;Audio-first shell design&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;show lots of context&lt;/td&gt;
&lt;td&gt;minimize repeated information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;color cues&lt;/td&gt;
&lt;td&gt;text cues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;long paths are fine&lt;/td&gt;
&lt;td&gt;long paths create audio noise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;visual scanning&lt;/td&gt;
&lt;td&gt;stable spoken landmarks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;highlight changes&lt;/td&gt;
&lt;td&gt;highlight importance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Individually, these changes are small. Together, they turn the terminal into something I can navigate at the speed I can think. Interestingly, many of these ideas are useful even for sighted users. Shorter prompts, clearer cues, and stable navigation primitives improve terminal workflows regardless of how you interact with them.&lt;/p&gt;
&lt;p&gt;The full configuration is available here:
&lt;a href="https://codeberg.org/derekriemer/dotfiles-public"&gt;https://codeberg.org/derekriemer/dotfiles-public&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="disclaimers"&gt;disclaimers:&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;This repo is a cleaned-up, minimal version of my actual setup. I’ve removed machine-specific config (SSH, hostnames, private paths), but the structure and workflows are the same as what I use day to day. &lt;/li&gt;
&lt;li&gt;My crude bash implementations are far from elegant, and do not even attempt to maximize performance. If the pipeline ever becomes noticeably slow, I'll likely rewrite the core matcher as a simple rust program that uses the current directory to walk a tree of substitutions or something like that. However, I'm not optimizing something that works well enough with no noticeable overhead to me, the intended user.&lt;/li&gt;
&lt;/ol&gt;</description><category>blindness</category><category>productivity</category><category>software</category><category>tech</category><guid>https://derekriemer.com/posts/2026/03/15/designing-a-terminal-for-an-audio-first-workflow/</guid><pubDate>Sun, 15 Mar 2026 11:13:39 GMT</pubDate></item></channel></rss>