<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>emre şahin's digital garden 🍃 - nushell</title>
    <link>https://emresahin.net/tags/nushell/</link>
    <description>Posts in the nushell tag</description>
    <language>en</language>
    <managingEditor>contact@emresahin.net (Emre Şahin)</managingEditor>
    <lastBuildDate>Tue, 15 Sep 2026 19:46:32 +0000</lastBuildDate>
    <atom:link href="https://emresahin.net/tags/nushell/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>bits 21</title>
      <published>2025-09-28T18:00:14+00:00</published>
      <updated>2025-09-28T18:00:14+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 28 Sep 2025 18:00:14 +0000</pubDate>
      <link>https://emresahin.net/bits-21/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-21/</guid>
      <description>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini. Gemini proposed this: git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length What I ended up ...</description>
      <category>bits</category>
      <category>programming</category>
      <category>Gemini</category>
      <category>nushell</category>
      <category>git</category>
      <category>regex</category>
      <category>LLM</category>
      <content:encoded><![CDATA[<p>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini.</p>
<p>Gemini proposed this:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length
</code></pre>
<p>What I ended up doing:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -a "{+" "" | str replace -a "+}" "" | str replace -a ' ' "\n" | lines | length
</code></pre>
<p>I notice LLMs tend to err on the side of complexity. I don’t know if this is due to their training to spit out as many tokens as possible, but using regular expressions when a simple string replacement may suffice is a good sign that they may be adding more complexity than necessary.</p>]]></content:encoded>
    </item>
    <item>
      <title>bits 20</title>
      <published>2025-09-28T17:52:31+00:00</published>
      <updated>2025-09-28T17:52:31+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 28 Sep 2025 17:52:31 +0000</pubDate>
      <link>https://emresahin.net/bits-20/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-20/</guid>
      <description>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini. Gemini proposed this: git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length What I ended up ...</description>
      <category>bits</category>
      <category>programming</category>
      <category>Gemini</category>
      <category>nushell</category>
      <category>git</category>
      <category>regex</category>
      <category>LLM</category>
      <content:encoded><![CDATA[<p>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini.</p>
<p>Gemini proposed this:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length
</code></pre>
<p>What I ended up doing:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -a "{+" "" | str replace -a "+}" "" | str replace -a ' ' "\n" | lines | length
</code></pre>
<p>I notice LLMs tend to err on the side of complexity. I don’t know if this is due to their training to spit out as many tokens as possible, but using regular expressions when a simple string replacement may suffice is a good sign that they may be adding more complexity than necessary.</p>]]></content:encoded>
    </item>
    <item>
      <title>bits 19</title>
      <published>2025-09-28T17:42:11+00:00</published>
      <updated>2025-09-28T17:42:11+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 28 Sep 2025 17:42:11 +0000</pubDate>
      <link>https://emresahin.net/bits-19/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-19/</guid>
      <description>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini. Gemini proposed this: git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length What I ended up ...</description>
      <category>bits</category>
      <category>programming</category>
      <category>Gemini</category>
      <category>nushell</category>
      <category>git</category>
      <category>regex</category>
      <category>LLM</category>
      <content:encoded><![CDATA[<p>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini.</p>
<p>Gemini proposed this:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length
</code></pre>
<p>What I ended up doing:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -a "{+" "" | str replace -a "+}" "" | str replace -a ' ' "\n" | lines | length
</code></pre>
<p>I notice LLMs tend to err on the side of complexity. I don’t know if this is due to their training to spit out as many tokens as possible, but using regular expressions when a simple string replacement may suffice is a good sign that they may be adding more complexity than necessary.</p>]]></content:encoded>
    </item>
    <item>
      <title>bits 18</title>
      <published>2025-09-28T17:22:48+00:00</published>
      <updated>2025-09-28T17:22:48+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 28 Sep 2025 17:22:48 +0000</pubDate>
      <link>https://emresahin.net/bits-18/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-18/</guid>
      <description>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini. Gemini proposed this: git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length What I ended up ...</description>
      <category>bits</category>
      <category>programming</category>
      <category>Gemini</category>
      <category>nushell</category>
      <category>git</category>
      <category>regex</category>
      <category>LLM</category>
      <content:encoded><![CDATA[<p>I wanted to get the number of word changes between commits in my blog. I asked for a git-diff-based Nushell pipeline from Gemini.</p>
<p>Gemini proposed this:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length
</code></pre>
<p>What I ended up doing:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -a "{+" "" | str replace -a "+}" "" | str replace -a ' ' "\n" | lines | length
</code></pre>
<p>I notice LLMs tend to err on the side of complexity. I don’t know if this is due to their training to spit out as many tokens as possible, but using regular expressions when a simple string replacement may suffice is a good sign that they may be adding more complexity than necessary.</p>]]></content:encoded>
    </item>
    <item>
      <title>bits 17</title>
      <published>2025-09-28T17:20:37+00:00</published>
      <updated>2025-09-28T17:20:37+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 28 Sep 2025 17:20:37 +0000</pubDate>
      <link>https://emresahin.net/bits-17/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-17/</guid>
      <description>I wanted to calculate the number of words added between commits in my blog posts. I asked Gemini for a Nushell pipeline based on git diff : It proposed the following: git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length H...</description>
      <category>shell</category>
      <category>productivity</category>
      <category>Gemini</category>
      <category>nushell</category>
      <category>git</category>
      <category>regex</category>
      <category>ai</category>
      <content:encoded><![CDATA[<p>I wanted to calculate the number of words added between commits in my blog posts. I asked Gemini for a Nushell pipeline based on <code>git diff</code>:</p>
<p>It proposed the following:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length
</code></pre>
<p>However, I ended up using this simplified version:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -a "{+" "" | str replace -a "+}" "" | str replace -a ' ' "\n" | lines | length
</code></pre>
<p>I’ve noticed that LLMs often lean toward overly complex solutions. I’m not sure if this is due to being trained to generate as many tokens as possible, but using regular expressions where a simple string replacement would suffice is often a sign of unnecessary complexity.</p>]]></content:encoded>
    </item>
    <item>
      <title>bits 16</title>
      <published>2025-09-28T17:15:52+00:00</published>
      <updated>2025-09-28T17:15:52+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 28 Sep 2025 17:15:52 +0000</pubDate>
      <link>https://emresahin.net/bits-16/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-16/</guid>
      <description>I wanted to get the number of word changes between commits in my blogs. I asked for a git diff based nushell pipeline from Gemini: Gemini proposed this: git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length What I ended up...</description>
      <category>bits</category>
      <category>Gemini</category>
      <category>nushell</category>
      <category>git</category>
      <category>regex</category>
      <category>LLM</category>
      <category>workflow</category>
      <content:encoded><![CDATA[<p>I wanted to get the number of word changes between commits in my blogs. I asked for a <code>git diff</code> based nushell pipeline from Gemini:</p>
<p>Gemini proposed this:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -r '\{\+|\+\}' '' | str join " " | str words | length
</code></pre>
<p>What I ended up doing:</p>
<pre><code class="language-nushell">git diff --word-diff=plain -- "*.md" | rg -o '\{\+(.*?)\+\}' | str replace -a "{+" "" | str replace -a "+}" "" | str replace -a ' ' "\n" | lines | length
</code></pre>
<p>I notice LLMs tend to err on the side of complexity. I don’t know if this is due to their training to spit as many tokens as possible, but using regular expressions when a simple single replace may suffice is a good sign that they may be adding more complexity than necessary.</p>]]></content:encoded>
    </item>
    <item>
      <title>Nushell $env.PATH in tmux</title>
      <published>2025-05-30T16:09:29+00:00</published>
      <updated>2025-05-30T16:09:29+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 30 May 2025 16:09:29 +0000</pubDate>
      <link>https://emresahin.net/nushell--env-path-in-tmux/</link>
      <guid isPermaLink="true">https://emresahin.net/nushell--env-path-in-tmux/</guid>
      <description>I noticed that when I use: set -g default-shell nu in .tmux.conf to specify Nushell as the shell, it doesn’t load $env.PATH . It is necessary to specify the shell with: set -g default-command nu -l (using the full path to nu ) to ensure the shell reads your configuration file.</description>
      <category>Shell</category>
      <category>Nushell</category>
      <category>Tmux</category>
      <category>Terminal</category>
      <category>Configuration</category>
      <content:encoded><![CDATA[<p>I noticed that when I use:</p>
<pre><code class="language-tmux">set -g default-shell nu
</code></pre>
<p>in <code>.tmux.conf</code> to specify Nushell as the shell, it doesn’t load <code>$env.PATH</code>. It is necessary to specify the shell with:</p>
<pre><code class="language-tmux">set -g default-command nu -l
</code></pre>
<p>(using the full path to <code>nu</code>) to ensure the shell reads your configuration file.</p>]]></content:encoded>
    </item>
    <item>
      <title>bits 15</title>
      <published>2025-03-27T09:52:06+00:00</published>
      <updated>2025-03-27T09:52:06+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Thu, 27 Mar 2025 09:52:06 +0000</pubDate>
      <link>https://emresahin.net/bits-15/</link>
      <guid isPermaLink="true">https://emresahin.net/bits-15/</guid>
      <description>How to use a Python virtual environment with Nushell: virtualenv .venv overlay use .venv/bin/activate.nu</description>
      <category>bits</category>
      <category>shell</category>
      <category>nushell</category>
      <category>Python</category>
      <category>venv</category>
      <category>virtualenv</category>
      <category>cli</category>
      <content:encoded><![CDATA[<p>How to use a Python virtual environment with Nushell:</p>
<pre><code class="language-nu">virtualenv .venv
overlay use .venv/bin/activate.nu
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>devlog 22</title>
      <published>2025-02-03T10:24:41+00:00</published>
      <updated>2025-02-03T10:24:41+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Mon, 03 Feb 2025 10:24:41 +0000</pubDate>
      <link>https://emresahin.net/devlog-22/</link>
      <guid isPermaLink="true">https://emresahin.net/devlog-22/</guid>
      <description>🐢 We need to find a theme for the blog. The current one breaks Nushell output tables because the code blocks are too narrow. 🐇 https://www.getzola.org/themes/pico/ is an option, but I don’t like its header. 🦊 Minimal Dark from the same author looks better: https://kuznetsov17.github.io/minimal-da...</description>
      <category>Digital Garden</category>
      <category>Development</category>
      <category>Theme</category>
      <category>Zola</category>
      <category>Anemone</category>
      <category>mdBook</category>
      <category>Nushell</category>
      <category>Netlify</category>
      <category>Syntax Highlighting</category>
      <category>Static Site Generator</category>
      <content:encoded><![CDATA[<p>🐢 We need to find a theme for the blog. The <a href="https://www.getzola.org/themes/anemone/">current
one</a> breaks Nushell output tables
because the code blocks are too narrow.</p>
<p>🐇 https://www.getzola.org/themes/pico/ is an option, but I don’t like its header.</p>
<p>🦊 Minimal Dark from the same author looks better: https://kuznetsov17.github.io/minimal-dark/notes/note1/</p>
<p>🐇 https://www.getzola.org/themes/no-style-please/ is also an option.</p>
<p>🦊 https://halve-z.netlify.app/posts/information/ looks interesting, but there is too much screen estate for the left bar.</p>
<p>🐢 Let’s start by running the site locally first.</p>
<p>🐇 There are errors in the configuration. That’s weird, but let’s fix these.</p>
<p>🐢 Fixed errors. These are probably related to a newer version. Can we take a look at <code>netlify.toml</code> to see if it downloads the same version?</p>
<p>🐇 There are breaking changes in Zola 0.19. Let’s update and push the Netlify config to see the results.</p>
<p>🐢 We need to add language support for Nushell to prevent warnings. Take a look at how to add a syntax file to Zola.</p>
<p>🐇 I added</p>
<pre><code class="language-toml">extra_syntaxes_and_themes = ["syntaxes"]
</code></pre>
<p>to the <code>[markdown]</code> section and added a <code>syntaxes/nushell.sublime-syntax</code> file copied from https://github.com/kurokirasama/nushell_sublime_syntax. I’m getting:</p>
<pre><code>Error: Reason: Error while compiling regex '\b(?x: 7z | ?
...
s-to-gdrive | usage | ver | verify | weather | wget-all | which-cd | wifi-info | wifi-pass | xls2csv | ydx | yt-api | ytcli | ytm | z | zi)\b'
Oniguruma error: target of repeat operator is not specified
</code></pre>
<p>🐢 The syntax highlighter may be a bit buggy. Let’s try to fix this if it’s a one-off.</p>
<p>🐇 Found the bug. There is a <code>?</code> in the regex that causes it to fail. Now it compiles, and Nushell blocks are colored.</p>
<p>🐢 Cool. Let’s fix the other warnings now. <code>shell</code> and <code>console</code> are not recognized, it looks like.</p>
<p>🐇 There is only Bash listed in https://www.getzola.org/documentation/content/syntax-highlighting/.</p>
<p>🦁 What do you think about migrating to mdBook? We already maintain mdBook for XVC; what about just moving the site to mdBook?</p>
<p>🐢 I thought about this before, and the only downside is the lack of an RSS feed.</p>
<p>🦊 I found this: https://github.com/theowenyoung/mdbook-rss</p>
<p>🐢 Now, this changes everything. We can even move the <code>nedriy.at</code> site to mdBook in this case.</p>
<p>🐇 Then, let’s start working on this. The site will be a technical book site in this case.</p>
<p>🐢 Does mdBook support Nushell syntax?</p>
<p>🐇 Nushell is not in the <a href="https://rust-lang.github.io/mdBook/format/theme/syntax-highlighting.html">listed languages</a>. mdBook uses <a href="https://highlightjs.org/">highlight.js</a>, and in its <a href="https://highlightjs.readthedocs.io/en/latest/supported-languages.html">listed languages</a>, we don’t find Nu either.</p>
<p>🐢 We already added Nu support to Zola, and we can just change the theme. This is a blocker in my opinion.</p>
<p>🐇 I searched for Nushell highlight.js support, and nothing appears. I think we can just postpone until Nu has more support on this front.</p>
<p>🐢 Yes. Let’s first try this change in the non-technical blog, and we can come back to this issue. Now, we’ll update <code>console</code> and <code>shell</code> to <code>bash</code>, I think.</p>
<p>🐇 Replaced <code>shell</code> and <code>console</code> with <code>bash</code>.</p>
<p>🐢 There is a file for <code>ggplot</code> that has warnings from earlier incarnations. We also lack syntax highlighters for Vim and Tmux.</p>
<p>🐇 There is a <code>sublime-syntax</code> file for Tmux at https://raw.githubusercontent.com/gerardroche/sublime-tmux/refs/heads/master/Tmux.sublime-syntax, but do we need it for a single file?</p>
<p>🐢 Let’s set it to plain text.</p>
<p>🐇 Now we only have Mermaid warnings left.</p>
<p>🐢 There should be a diagram at https://emresahin.net/developing-a-gitignore-crate/, but it doesn’t show up. We need a shortcode to show these, like the YouTube shortcode. Now let’s get back to theme selection.</p>
<p>🐇 I tested Karzok, but it doesn’t have category and tags support.</p>
<p>🐢 And I tested https://github.com/micahkepe/radion, but the best so far is the <code>apollo</code> theme. I’m struggling to modify the index page, though. I forgot that I modified the theme’s <code>index.html</code> file. I have content in <code>/content/_index.md</code> and a modified <code>index.html</code> in <code>/themes/anemone/templates/index.html</code> to show the content, tags, categories, etc. It should be fixed now.</p>
<p>🐇 Ah, cool. Can we clean the recent duplicate pages now?</p>
<p>🐢 Yeah, let’s take a look.</p>]]></content:encoded>
    </item>
    <item>
      <title>devlog 21</title>
      <published>2025-02-03T09:59:28+00:00</published>
      <updated>2025-02-03T09:59:28+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Mon, 03 Feb 2025 09:59:28 +0000</pubDate>
      <link>https://emresahin.net/devlog-21/</link>
      <guid isPermaLink="true">https://emresahin.net/devlog-21/</guid>
      <description>🐢 Now, the next version will have a --json output for xvc file list . We can start working on it or update the Readme file? 🐇 What about adding at least command completions for Nushell? 🐢 Let’s read a bit about clap_complete_nushell . 🦊 There seems to be a nu-complete command. Let’s check its doc...</description>
      <category>xvc</category>
      <category>Development</category>
      <category>Nushell</category>
      <category>clap_complete_nushell</category>
      <category>carapace</category>
      <category>dynamic completions</category>
      <category>JSON</category>
      <category>Lazygit</category>
      <category>Rust</category>
      <category>XVC</category>
      <category>completions</category>
      <content:encoded><![CDATA[<p>🐢 Now, the next version will have a <code>--json</code> output for <code>xvc file list</code>. We
can start working on it or update the Readme file?</p>
<p>🐇 What about adding at least command completions for Nushell?</p>
<p>🐢 Let’s read a bit about <code>clap_complete_nushell</code>.</p>
<p>🦊 There seems to be a <code>nu-complete</code> command. Let’s check its documentation.</p>
<p>🐇 Nothing was found, and Kagi doesn’t help much either.</p>
<p>🐢 There is a completions document for Nushell: https://www.nushell.sh/book/custom_completions.html</p>
<p>🐇 There is a tool called carapace to provide completions across shells.</p>
<p>🐢 Its <a href="https://carapace-sh.github.io/carapace/carapace.html">documentation</a> is
thin, and I’m not sure if it supports dynamic completions out of the box. I
believe instead of adding a carapace setup, we can just write a Nushell
completion script that will use JSON output from the commands and add some
(maybe hidden) utility commands to support it.</p>
<p>🐇 There are a set of example scripts in the Nushell repo:
https://github.com/nushell/nu_scripts/tree/main/custom-completions</p>
<p>🐢 The reason I want to write custom completions for Nushell is that it will be
an exercise for the scripting language. <a href="https://github.com/nushell/nu_scripts/blob/main/custom-completions/gh/gh-completions.nu"><code>gh</code>
completions</a>
are not as scary as a Bash script.</p>
<p>🐇 <a href="https://github.com/nushell/nu_scripts/blob/main/custom-completions/git/git-completions.nu"><code>git</code>
completions</a>
are a better example for XVC. They simply run <code>git</code> whenever necessary. We can
start from a static completions command and update this with dynamic
completions manually. It will teach a lot.</p>
<p>🐢 I <a href="https://github.com/iesahin/nu_scripts">forked</a> the <code>nu_scripts</code> repo and
will add XVC completions script there.</p>
<p>🐇 Then let’s begin by adding Nushell static completions. Shall we add a
command for this?</p>
<p>🦊 Reviving the <code>completion</code> command we removed in 0.6.13?</p>
<p>🐢 We shouldn’t list it. We can make a <code>_comp</code> subcommand for the time being
and generate and distribute completions in the repository. When
<code>clap_complete_nushell</code> has the feature parity to provide dynamic completions,
we can remove these commands.</p>
<p>🐇 What will we use this for other than generating completions?</p>
<p>🐢 Maybe dynamic completions can call this as well.</p>
<p>🐇 Added Nushell static completions to be output using <code>xvc _comp generate-nushell</code>. Let’s bump up the version to 0.6.15.</p>
<pre><code>cargo set-version 0.6.15-alpha.1
   Upgrading xvc from 0.6.14 to 0.6.15-alpha.1
...
</code></pre>
<p>🐢 I noticed we forgot a line in the CLI command handler that asserts <code>xvc_root_opt.is_some()</code>, and this fails when we run <code>xvc</code> outside of repositories. We need to release this version quickly.</p>
<p>🐇 Oops, now, ok, let’s write a static Nushell generator and just release quickly.</p>
<p>🦊 Generating completions with</p>
<pre><code>xvc comp generate-nushell
</code></pre>
<p>🐢 Completion command is run with <code>comp</code> instead of <code>_comp</code>. Should we rename it?</p>
<p>🐇 Renamed it to <code>_comp</code>. It’s not hidden, but at least we can be sure that it won’t be misunderstood as a common command.</p>
<p>🐢 Bumping the version again. Now let’s source the generated script and test it.</p>
<pre><code>cargo set-version 0.6.15-alpha.2
   Upgrading xvc from 0.6.15-alpha.1 to 0.6.15-alpha.2
...
</code></pre>
<p>🦊 Yep, it works. We now have completions for Nushell.</p>
<p>🐢 Let’s update the completions documentation.</p>
<p>🐇 Done. Now, let’s take a look at CI and see what fails.</p>
<pre><code class="language-nu">ghrl | first
╭──────────────┬─────────────────────────────────────────────────────────╮
│ conclusion   │ success                                                 │
│ displayTitle │ Add Nushell completions                                 │
│ headBranch   │ nushell-completions                                     │
│ url          │ https://github.com/iesahin/xvc/actions/runs/13070200765 │
╰──────────────┴─────────────────────────────────────────────────────────╯
</code></pre>
<p>🐢 It fails because of coverage, not the tests. <a href="https://github.com/iesahin/xvc/pull/266#issuecomment-2626794240">Codecov says</a> the new code isn’t tested.</p>
<p>🐇 The added <code>xvc _comp</code> command isn’t tested. We can add a test running those lines and testing if the command outputs a completion script.</p>
<p>🐢 We have a <a href="https://github.com/iesahin/xvc/blob/main/lib/tests/test_completions.rs#L18">test for completions</a>. We can add a test that runs the lines.</p>
<p>🐇 Added a test and bumping up the version.</p>
<pre><code class="language-nu">cargo set-version 0.6.15-alpha.3
   Upgrading xvc from 0.6.15-alpha.2 to 0.6.15-alpha.3
...
</code></pre>
<p>🦊 We can add some more coverage while waiting for the tests.</p>
<p>🐇 <a href="https://app.codecov.io/gh/iesahin/xvc/blob/main/logging%2Fsrc%2Flib.rs#L285"><code>XvcOutputLine</code> implementation</a> seems to have no tests. It’s weird because we use these everywhere.</p>
<p>🐢 I’m not sure we use this particular implementation; we just use <code>XvcOutputLine::Info(s)</code>, not <code>XvcOutputLine::info(s)</code> anywhere. We can delete these methods actually.</p>
<p>🐇 We’ll add JSON output via this particular struct. Can we refactor these to use formatting for JSON, for example? Or use these to output JSON?</p>
<p>🦊 We can add a formatter to <code>XvcOutputLine</code> to output structures.</p>
<p>🐢 The enum is now defined as:</p>
<pre><code class="language-rust">#[derive(Clone, Debug)]
pub enum XvcOutputLine {
    /// The output that we should be reporting to user
    Output(String),
    /// For informational messages
    Info(String),
    /// For debug output to show the internals of Xvc
    Debug(String),
    /// Warnings that are against some usual workflows
    Warn(String),
    /// Errors that interrupts a workflow but may be recoverable
    Error(String),
    /// Panics that interrupts the workflow and ends the program
    /// Note that this doesn't call panic! automatically
    Panic(String),
    /// Progress bar ticks.
    /// Self::Info is also used for Tick(1)
    Tick(usize),
}</code></pre>
<p>Here, these fields can also have a <code>formatter</code> that will render the string in a particular format. For example, the output can be</p>
<pre><code class="language-rust">XvcOutputLine::Output(XvcJsonFormatter, String)</code></pre>
<p>🐇 I’m not sure this is a good idea. <code>Output</code> already specifies this string as output. We can have a wrapper instead, like,</p>
<pre><code class="language-rust">struct XvcJsonOutput(Format&lt;XvcStructuredOutput&gt;, XvcStructuredOutput)</code></pre>
<p>and we can use the supplied format to render <code>XvcStructuredOutput</code> to an output line with <code>XvcOutputLine::Output</code>. If we don’t provide output as structured, it will be too much error-prone work to convert the current outputs to structured.</p>
<p>🦊 The transition will also be gradual. We may not need structured output for most of the commands. We can start with <code>xvc file list</code> and convert others as we go.</p>
<p>🐢 This is sensible. By the way, coverage still didn’t increase. There may be something going on with Codecov or running the test.</p>
<pre><code class="language-nu">ghrl | first
╭──────────────┬─────────────────────────────────────────────────────────╮
│ conclusion   │ success                                                 │
│ displayTitle │ Add Nushell completions                                 │
│ headBranch   │ nushell-completions                                     │
│ url          │ https://github.com/iesahin/xvc/actions/runs/13087431038 │
╰──────────────┴─────────────────────────────────────────────────────────╯
</code></pre>
<p>🐇 Let’s run the test:</p>
<pre><code class="language-sh">cargo test -p xvc --test test_completions
...
test test_completions ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.80s
</code></pre>
<p>🐢 Can we make sure the output is a Nushell script and not an error message?</p>
<p>🐇 Let’s print it out.</p>
<p>🐢 It looks like when the <code>COMPLETE</code> environment variable is set, it never calls <code>_comp</code> subcommand and never calls those lines.</p>
<pre><code>cargo set-version 0.6.15-alpha.4
   Upgrading xvc from 0.6.15-alpha.3 to 0.6.15-alpha.4
...
</code></pre>
<p>🐢 Let’s make a release for 0.6.15. Coverage is OK now.</p>
<pre><code class="language-nu">cargo set-version 0.6.15
   Upgrading xvc from 0.6.15-alpha.4 to 0.6.15
...
</code></pre>
<pre><code class="language-nu">gh pr merge --squash --body $"(open CHANGELOG.md | lines | skip 2 | take 5)" --subject "Add static nushell completions"
</code></pre>
<p>🐇 Merged the PR.</p>
<p>🐢 Releases should appear in a few minutes.</p>
<p>🐇 We need to tag the merge commit for this.</p>
<p>🐢 Oh, yep. AFAIK Lazygit doesn’t have something for <code>git push --tags</code>. Let’s push from the CLI.</p>
<pre><code class="language-nu">git push --tags
You are on the main branch. Skipping CHANGELOG.md check.
To github.com:iesahin/xvc
 * [new tag]         v0.6.15 -&gt; v0.6.15
</code></pre>
<p>🦊 These commands, especially tables, are not rendered correctly on the web. We need to change the theme, I think.</p>]]></content:encoded>
    </item>
    <item>
      <title>My Nushell aliases for `gh` command</title>
      <published>2025-01-29T08:04:19+00:00</published>
      <updated>2025-01-29T08:04:19+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Wed, 29 Jan 2025 08:04:19 +0000</pubDate>
      <link>https://emresahin.net/my-nushell-aliases-for--gh--command/</link>
      <guid isPermaLink="true">https://emresahin.net/my-nushell-aliases-for--gh--command/</guid>
      <description>Almost every day, I start my work by playing with my configuration. Today, I added aliases for gh (GitHub CLI) to Nushell. # List issues related to me export def ghil [] { (gh issue list --search "involves:iesahin" --json title,url) | from json } # Add a comment to an issue export alias ghic = gh...</description>
      <category>Shell</category>
      <category>Nushell</category>
      <category>nushell</category>
      <category>gh</category>
      <category>GitHub</category>
      <category>CLI</category>
      <content:encoded><![CDATA[<p>Almost every day, I start my work by playing with my configuration. Today, I added aliases for <code>gh</code> (GitHub CLI) to Nushell.</p>
<pre><code class="language-nu"># List issues related to me
export def ghil [] { 
  (gh issue list --search "involves:iesahin" --json title,url) | from json
}

# Add a comment to an issue
export alias ghic = gh issue comment

# Create an issue
export alias ghiC = gh issue create $in

# View an issue
export alias ghiv = gh issue view

# View an issue in the browser
export alias ghiw = gh issue view --web

# List PRs in a table
export def ghpl [] {
  (gh pr list --json "title,url,headRefName" ) | from json
}

# View a PR
export alias ghpv = gh pr view

# View a PR in GitHub
export alias ghpw = gh pr view --web

# Checkout a PR
export alias ghco = gh pr checkout

# Add a comment to a PR
export alias ghpc = gh pr comment

# Show changes in a PR
export alias ghpd = gh pr diff

# Add a review to a PR
export alias ghpR = gh pr review

# Create a PR
export alias ghpC = gh pr create

# Merge a PR to main
export alias ghpM = gh pr merge

# List GH CI runs in a table
export def ghrl [] {
  (gh run list --json conclusion,displayTitle,headBranch,url ) | from json 
}

# View a GH CI run
export alias ghrv = gh run view

# View a GH CI run in the browser
export alias ghrw = gh run view --web 

# View output from a failed run
export alias ghrf = gh run view --log-failed

# Search code and get results in a table
export def ghsc [] { 
  (gh search code $in --json repository,path,textMatches,url) | from json
}
</code></pre>
<p>Aliases don’t allow pipes in Nushell, so I used <code>def</code> for those that require them.</p>
<p>When there is an <code>$in</code> parameter in an alias, it expects the input from a pipe.
An issue can be created like this, for example:</p>
<pre><code class="language-nu">"Your software sucks" | ghiC
</code></pre>
<p>To search for the code copied to the clipboard and get only the URLs, you can use:</p>
<pre><code class="language-nu">pbpaste | ghsc | get url
</code></pre>
<p>To open them in different browser windows:</p>
<pre><code class="language-nu">pbpaste | ghsc | get url | each { |u| start $u }
</code></pre>
<p>I’ll add a shortcut key for this last one. It’s so quick! :D</p>]]></content:encoded>
    </item>
    <item>
      <title>devlog 17</title>
      <published>2025-01-27T16:07:11+00:00</published>
      <updated>2025-01-27T16:07:11+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Mon, 27 Jan 2025 16:07:11 +0000</pubDate>
      <link>https://emresahin.net/devlog-17/</link>
      <guid isPermaLink="true">https://emresahin.net/devlog-17/</guid>
      <description>🐢 We are progressing towards adding all store completions. In the meantime, I switched to nushell from zsh. Completions for nushell require a different crate and it doesn’t have dynamic completions yet. 🐇 We can just complete the completions for this version and add JSON output for all commands t...</description>
      <category>devlog</category>
      <category>xvc</category>
      <category>clap_complete</category>
      <category>clap</category>
      <category>nushell</category>
      <category>dynamic-completions</category>
      <category>completions</category>
      <category>rust</category>
      <content:encoded><![CDATA[<p>🐢 We are progressing towards adding all store completions. In the meantime, I
switched to nushell from zsh. Completions for
<a href="https://github.com/nushell/nushell">nushell</a> require a different crate and it
doesn’t have dynamic completions yet.</p>
<p>🐇 We can just complete the completions for this version and add JSON output
for all commands to use in nushell. In the meantime, dynamic completions support
for nushell is likely to be added.</p>
<p>🐢 Umm, yep. Then we’ll continue to run tests with zsh for the current version.</p>
<p>🐇 <a href="~/github.com/iesahin/xvc/run-tests.zsh"><code>run-tests</code></a> is a zsh file, and
we will continue to use it. We can switch to nushell eventually as we add more
compatibility with it, but let’s not do this now.</p>
<p>🐢 Where were we the last time for completions?</p>
<p>🐇 We added xvc_path_completers. Let’s check the TODO list now.</p>
<p>🐢 We still need tracked_targets completions in a few places.</p>
<p>🐇 Added those.</p>
<p>🐢 Now we have some strum completers. Let’s finish these up as well.</p>
<p>🐇 Ok. They are done as well.</p>
<p>🐢 Now, let’s start adding a storage_identifier completer. It should read all
<code>XvcStorage</code> records and list their names.</p>
<p>🐇 Added a <code>storage_identifier</code> completer. Let’s add it to all places where we use
storage_identifiers.</p>
<p>🐢 Now, we need completers for pipeline and step names. These are
straightforward.</p>
<p>🐇 Added these. Let’s fill up where they are needed.</p>
<p>🐢 I want to skip some of the fine-grained completions in this version. We can
have a specific completer for <code>--params</code> options for files and params inside,
for example. Or a special completer for directories.</p>
<p>🐇 Let’s not forget these. Reading YAML files and extracting hyperparameter
keys for the prompt would be a really good feature for the user.</p>
<p>🐢 I agree. These are good features in general, but we need to ship the current
version as soon as possible.</p>
<p>🐇 Let’s check the TODO comments once more.</p>
<pre><code class="language-sh">$ rg 'TODO:' 
...
- ✅ pipeline/src/pipeline/api/update.rs:    /// TODO: Add a repository_dirs completer (11:08)
- ✅ file/src/remove/mod.rs:    /// TODO: Add a storage_identifier completer (11:08)
- ✅ file/src/bring/mod.rs:    /// TODO: Add a storage_identifier completer (11:08)
...
</code></pre>
<p>🦊 We can use <code>xvc_path_completer</code> for the tracked directory completer for the
time being. For <code>pipeline update</code>, we can use a <code>ValueHint</code> instead. It’s not
necessary to use a custom completion.</p>
<p>🐢 For the <code>xvc file copy</code> destination, we can have a file or a directory that we
track and is not available, or we don’t track and is available. It’s a similar situation
to xvc_path_completer, but we also need to check the local paths. It requires
some more care.</p>
<p>🐇 Now we can begin to run the tests.</p>
<p>🐢 Is this building?</p>
<p>🐇 It should.</p>
<p>🐢 We also have one bug. xvc shouldn’t print help text when the <code>COMPLETE</code>
environment variable is set.</p>
<p>🐇 Umm, yeah, that’s a blocker.</p>
<p>🐢 Fixed it. We can bump the version and push the changes.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
