<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://erikw.me/feed.xml" rel="self" type="application/atom+xml" /><link href="https://erikw.me/" rel="alternate" type="text/html" /><updated>2026-03-30T22:57:56+02:00</updated><id>https://erikw.me/feed.xml</id><title type="html">Erik Westrup</title><subtitle>Personal website and blog of Erik Westrup.</subtitle><author><name>Erik Westrup</name></author><entry><title type="html">Launching Dhammapada.at</title><link href="https://erikw.me/blog/buddhism/dhammapada/" rel="alternate" type="text/html" title="Launching Dhammapada.at" /><published>2026-03-07T10:05:00+01:00</published><updated>2026-03-07T10:05:00+01:00</updated><id>https://erikw.me/blog/buddhism/dhammapada</id><content type="html" xml:base="https://erikw.me/blog/buddhism/dhammapada/"><![CDATA[<p>I’ve been working on a small project that I’m happy to finally share: <a href="https://dhammapada.at">Dhammapada.at</a>.</p>

<p>It is a website for reading, searching and sharing the Dhammapada, built with the kind permission of Windhorse Publications and Sangharakshita’s Literary Executors.</p>

<p>The site features Sangharakshita’s poetic translation, including notes and annotations. My aim here has been quite simple: make the text easier to access, easier to explore and easier to share.</p>

<p>Rather than making a plain digital copy of a book, I wanted something that reflects how one might actually use these verses today. Maybe you want to quickly look up a passage after a talk, send a verse to a friend, or find that one fragment you vaguely remember but cannot fully place.</p>

<p>It is also meant to complement the <a href="https://www.windhorsepublications.com/product/dhammapada-the-way-of-truth/?utm_source=dhammapada.at&amp;utm_medium=referral&amp;utm_campaign=dhammapada.at">printed edition</a>, not replace it. If the site helps more people discover the translation, and some of them also end up with the physical book in their hands, all the better.</p>

<h1 id="reading-the-text">Reading the Text</h1>
<p>I wanted the reading experience to be simple and uninterrupted.</p>

<p>You can browse by chapter, open individual verses and move through the text without much friction. With 26 chapters and 423 verses, structure matters. One verse should lead naturally to the next, whether you are reading chapter by chapter, studying a specific section or just sitting down for a few quiet minutes with the text.</p>

<h1 id="referencing-and-sharing">Referencing and Sharing</h1>
<p>One thing I cared quite a bit about was referencing.</p>

<p>If you want to quote or share a verse, the links should be short, predictable and easy to remember. The same content can therefore be reached through multiple URL variants.</p>

<p>Chapter examples:</p>

<ul>
  <li><a href="https://dhammapada.at/chapter/1"><code class="language-plaintext highlighter-rouge">/chapter/1</code></a> (canonical)</li>
  <li><a href="https://dhammapada.at/1"><code class="language-plaintext highlighter-rouge">/1</code></a> (shorthand)</li>
  <li><a href="https://dhammapada.at/vagga/1"><code class="language-plaintext highlighter-rouge">/vagga/1</code></a> (Pali-style)</li>
</ul>

<p>Verse examples:</p>

<ul>
  <li><a href="https://dhammapada.at/chapter/1/verse/1"><code class="language-plaintext highlighter-rouge">/chapter/1/verse/1</code></a> (canonical)</li>
  <li><a href="https://dhammapada.at/1/1"><code class="language-plaintext highlighter-rouge">/1/1</code></a> (shorthand)</li>
  <li><a href="https://dhammapada.at/v1"><code class="language-plaintext highlighter-rouge">/v1</code></a> (global permalink)</li>
  <li><a href="https://dhammapada.at/vagga/1/gatha/1"><code class="language-plaintext highlighter-rouge">/vagga/1/gatha/1</code></a> (Pali-style)</li>
</ul>

<p>This means that verses can move quite naturally into conversations, emails or follow-up notes after a talk.</p>

<h1 id="random-verses">Random Verses</h1>
<p>I also added a small feature that I personally enjoy: random verse access.</p>

<p>There is an endpoint at <a href="https://dhammapada.at/random"><code class="language-plaintext highlighter-rouge">/random</code></a>, and from the menu you can hit the die button to jump to a random verse. This can even work nicely as a browser start page if you want to begin the day with a new verse.</p>

<h1 id="search-themes-and-offline-use">Search, Themes and Offline Use</h1>
<p>Search covers the full text and chapter titles. It opens from the search button or with <code class="language-plaintext highlighter-rouge">Cmd/Ctrl+K</code>, and closes with <code class="language-plaintext highlighter-rouge">Esc</code>. Search assets are only loaded when needed.</p>

<p>There are also three themes: Modern, Scripture and Contemplative. Together with light/dark mode and remembered preferences, this makes the site feel a bit more personal without getting in the way of the reading.</p>

<p>Offline use is supported as well through a Progressive Web App. The homepage, offline page, canonical chapter routes and verse routes remain available without a connection. The shorthand and Pali-style aliases continue to work too, which was important to me.</p>

<h1 id="static-first-but-app-like">Static First, but App-Like</h1>
<p>The site is built static-first with Astro and deployed at Neltify, yet behaves much more like an app.</p>

<p>Search indexes, route variants, offline support and Open Graph images are all generated at build time. I like this approach quite a lot; hosting stays simple, while the site still gets features like deep linking, theming, offline reading and random verse access through <a href="https://dhammapada.at/random"><code class="language-plaintext highlighter-rouge">/random</code></a>.</p>

<p>Pre-generated Open Graph images also means that when a verse or chapter is shared in chat apps or emails, the previews are consistent and ready to go.</p>

<h1 id="closing">Closing</h1>
<p>The purpose of Dhammapada.at is to make the Dhammapada easier to access, study and share in daily life, while still supporting discovery of the printed edition.</p>

<p>If you want, take a look around the chapters, try the search with <code class="language-plaintext highlighter-rouge">Cmd/Ctrl+K</code>, open a few verse links or install it on mobile for offline reading. May it be of benefit.</p>

<p>And if these verses speak to you, I do recommend getting the physical book as well. It is a beautiful edition and a very good companion to bring on a meditaiton retreat: <a href="https://www.windhorsepublications.com/product/dhammapada-the-way-of-truth/?utm_source=dhammapada.at&amp;utm_medium=referral&amp;utm_campaign=dhammapada.at">Dhammapada: The Way of Truth</a></p>]]></content><author><name>Erik Westrup</name></author><category term="Buddhism" /><category term="buddhism" /><category term="dhammapada" /><category term="astro" /><category term="pwa" /><category term="search" /><summary type="html"><![CDATA[I’ve been working on a small project that I’m happy to finally share: Dhammapada.at.]]></summary></entry><entry><title type="html">Git Hacks</title><link href="https://erikw.me/blog/tech/git-hacks/" rel="alternate" type="text/html" title="Git Hacks" /><published>2025-10-27T19:10:00+01:00</published><updated>2025-10-27T19:10:00+01:00</updated><id>https://erikw.me/blog/tech/git-hacks</id><content type="html" xml:base="https://erikw.me/blog/tech/git-hacks/"><![CDATA[<h1 id="diggin-dotfiles-collection">Diggin’ Dotfiles Collection</h1>
<p>It’s time to dig the dotfiles, and to dig in to them! This post is a part of a blog post collection called <em>Diggin’ Dotfiles</em> where I unearth some gems from my personal dotfiles repo <a href="https://github.com/erikw/dotfiles">erikw/dotfiles</a>.</p>

<p><a href="https://github.com/erikw/dotfiles" target="_blank">
  <img align="center" src="https://github-readme-stats.vercel.app/api/pin/?username=erikw&amp;repo=dotfiles" alt="" />
</a></p>

<p>The following articles are a part of this series:</p>
<ul>
  <li><a href="/blog/tech/diggin-dotfiles-manage-shell-configurations-across-different-systems/">Manage shell configurations across different systems</a></li>
  <li><a href="/blog/tech/wrap-dangerous-commands-in-pre-post-zfs-or-brtfs-snapshots/">Wrap Dangerous Commands in pre-post ZFS or BRTFS Snapshots</a></li>
  <li><a href="/blog/tech/finger-of-god-using-sudo-with-touchid-on-macos/">Finger of god: using sudo with TouchID on macOS</a></li>
  <li><a href="/blog/tech/generate-permutation-shell-aliases/">Generate permutation shell aliases</a></li>
  <li><a href="/blog/tech/git-hacks/">Git hacks</a></li>
</ul>

<h1 id="git-hacks">Git Hacks</h1>
<p>There’s so many small hacks for git that make everyday usage quicker and more pleasing. In this post I share a few of my collected helpers from my <a href="https://github.com/erikw/dotfiles/blob/main/.config/git/config">.gitconfig</a>.</p>

<h2 id="git-root">git root</h2>
<p>What: you’re deep down in a directory tree of a git repo and you want to go back to the top root folder of the repo.</p>

<p>How: put this git overlay function in your shell’s startup files such as <code class="language-plaintext highlighter-rouge">~/.zshrc</code> or <code class="language-plaintext highlighter-rouge">~/.bashrc</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Git overlay which adds a command to cd to the root of a git repo.</span>
git<span class="o">()</span> <span class="o">{</span>
	<span class="c"># -q: macOS, suppress name prefix. Replaced with cut(1); not available on GNU/Linux it seems.</span>
	<span class="c"># -b: macOS, only search binary not manual</span>
	<span class="nb">local </span><span class="nv">gitbin</span><span class="o">=</span><span class="si">$(</span> whereis <span class="nt">-b</span> git | <span class="nb">cut</span> <span class="nt">-d</span> <span class="s1">' '</span> <span class="nt">-f</span> 2<span class="si">)</span>

	<span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">=</span> root <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
		<span class="c"># Try get root from git submodule:</span>
		<span class="nb">local </span><span class="nv">root</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nv">$gitbin</span> rev-parse <span class="nt">--show-superproject-working-tree</span><span class="si">)</span><span class="s2">"</span>
		<span class="c"># Else we're in the main repo already:</span>
		<span class="c"># --show-cdup will not take us to the root if we came here from a symlink.</span>
		<span class="c">#test -n "$root" || root="$($gitbin rev-parse --show-cdup)"</span>
		<span class="c"># Instead get full path with --show-to-level https://stackoverflow.com/q/39746533/265508</span>
		<span class="nb">test</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$root</span><span class="s2">"</span> <span class="o">||</span> <span class="nv">root</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nv">$gitbin</span> rev-parse <span class="nt">--show-toplevel</span><span class="si">)</span><span class="s2">"</span>
		<span class="o">[</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$root</span><span class="s2">"</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">cd</span> <span class="s2">"</span><span class="nv">$root</span><span class="s2">"</span>
	<span class="k">else</span>
		<span class="nv">$gitbin</span> <span class="nv">$@</span>
	<span class="k">fi</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Now it’s as easy as this to change directory to the root of a git repo when you are nested in some subdirectores:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git root
</code></pre></div></div>

<h2 id="skip-ci-commit-message-prefix">[skip Ci] Commit Message Prefix</h2>
<p>What: many git centric build system with automatic builds support some way of telling the build system to skip the build for a specific commit. For example <a href="https://docs.netlify.com/site-deploys/manage-deploys/#skip-a-deploy">Netlify</a> allows you to prefix the first line of a commit message with <code class="language-plaintext highlighter-rouge">[skip ci]</code>.</p>

<p>What if you have already written your commit message and just realized after that you want to prepend the <code class="language-plaintext highlighter-rouge">[skip ci]</code> directive?</p>

<p>How: Add this alias to your git config file that will prepend the directive to the last commit’s message:</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">alias</span>]
  <span class="n">skip</span>-<span class="n">ci</span> = !<span class="s2">"git reset &amp;&amp; COMMIT_MSG=$(git show -s --format=format:%B) &amp;&amp; git commit --allow-empty --amend -m \"</span>[<span class="n">skip</span> <span class="n">ci</span>] $<span class="n">COMMIT_MSG</span>\<span class="s2">" &amp;&amp; git --no-pager show -s --format='format:New commit message subject: %s%n'"</span>
</code></pre></div></div>

<h2 id="generate-commit-message-from-git-status">Generate Commit Message from <code class="language-plaintext highlighter-rouge">git status</code></h2>
<p>What: when you work esp. with other developer you should spend time craft good commit message. However being honest, there are times when we don’t have time for a private git repo and quickly need to “save the state” just….</p>

<p>How: Add the <a href="https://gist.github.com/erikw/654386d35ecfdb0354cd2b71763f19ae"><code class="language-plaintext highlighter-rouge">git cis</code></a> alias to your git config:</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">alias</span>]
	<span class="c"># commit-status: generate a commit with message from git-status (staged changes).
</span>	<span class="c"># Source: https://gist.github.com/erikw/654386d35ecfdb0354cd2b71763f19ae
</span>	<span class="c"># Explanation:
</span>	<span class="c"># - Get only staged changes
</span>	<span class="c"># - Ignore changes in working area (2nd letter, the Y in XY as explained in $(git help status))
</span>	<span class="c"># - + split label and file path to separate lines so we can process the labels separately
</span>	<span class="c"># - Keep only the first label using awk
</span>	<span class="c"># - Add newline before each label section so we later can truncate \n to put everything on one line
</span>	<span class="c"># - Make labels human readable e.g. M -&gt; Modified
</span>	<span class="c"># - Put everything on one line and trim leading &amp; trailing whitespaces
</span>	<span class="n">commit</span>-<span class="n">status</span> = !<span class="err">"</span> \
	        <span class="n">TMPFILE</span>=$(<span class="n">mktemp</span> /<span class="n">tmp</span>/<span class="n">git</span>-<span class="n">commit</span>-<span class="n">status</span>-<span class="n">message</span>.<span class="n">XXX</span>); \
		<span class="n">git</span> <span class="n">status</span> --<span class="n">porcelain</span> \
		  | <span class="n">grep</span> <span class="s1">'^[MARCDT]'</span> \
		  | <span class="n">sort</span> \
		  | <span class="n">sed</span> -<span class="n">re</span> <span class="s1">'s/^([[:upper:]])[[:upper:]]?[[:space:]]+/\\1:\\n/'</span> \
		  | <span class="n">awk</span> <span class="s1">'!x[$0]++'</span> \
		  | <span class="n">sed</span> -<span class="n">re</span> <span class="s1">'s/^([[:upper:]]:)$/\\n\\1/'</span> \
		  | <span class="n">sed</span> -<span class="n">re</span> <span class="s1">'s/^M:$/Modified: /'</span> \
		  | <span class="n">sed</span> -<span class="n">re</span> <span class="s1">'s/^A:$/Added: /'</span> \
		  | <span class="n">sed</span> -<span class="n">re</span> <span class="s1">'s/^R:$/Renamed: /'</span> \
		  | <span class="n">sed</span> -<span class="n">re</span> <span class="s1">'s/^C:$/Copied: /'</span> \
		  | <span class="n">sed</span> -<span class="n">re</span> <span class="s1">'s/^D:$/Deleted: /'</span> \
		  | <span class="n">sed</span> -<span class="n">re</span> <span class="s1">'s/^T:$/File Type Changed: /'</span> \
		  | <span class="n">tr</span> <span class="s1">'\n'</span> <span class="s1">' '</span> | <span class="n">xargs</span> \
		  &gt; $<span class="n">TMPFILE</span>; \
		<span class="n">git</span> <span class="n">commit</span> -<span class="n">F</span> $<span class="n">TMPFILE</span>; \
		<span class="n">rm</span> -<span class="n">f</span> $<span class="n">TMPFILE</span> \
		<span class="err">"</span>
	<span class="n">cis</span> = <span class="n">commit</span>-<span class="n">status</span>
</code></pre></div></div>

<h2 id="quickly-refer-to-the-previous-branch">Quickly Refer to the Previous Branch</h2>
<p>What: quickly reference the previous branch. In some git subcommand <code class="language-plaintext highlighter-rouge">-</code> will refer to the previous branch that was checked out.</p>

<p>How:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git checkout - <span class="c"># Switch to the previous branch</span>
<span class="nv">$ </span>git switch -   <span class="c"># Ditto</span>
<span class="nv">$ </span>git rebase -   <span class="c"># Rebase current branch on the previous branch</span>
</code></pre></div></div>

<h2 id="quickly-copy-the-sha1-of-the-tip-of-current-branch">Quickly Copy the Sha1 of the Tip of Current Branch</h2>
<p>What: do you frequently need to copy the git SHA-1 of HEAD e.g. to deploy that commit in a build system?</p>

<p>How: this git alias makes it easier! Put in your git config (if you’re on linux, replace <code class="language-plaintext highlighter-rouge">pbcopy</code> with <code class="language-plaintext highlighter-rouge">xclip</code>):</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">alias</span>]
  <span class="n">sha1head</span> = !<span class="s2">"git rev-parse HEAD | tee /dev/tty | tr -d '\n' | pbcopy"</span>
</code></pre></div></div>

<p>and now simply run in your git repo $ git sha1head  to get the sha1 to stdout and in your macOS clipboard!</p>

<p>Please make sure that you know what HEAD is, if you’re really at the tip of the branch that you think you are</p>

<h1 id="automatically-prune-branches-on-git-fetch">Automatically Prune Branches on <code class="language-plaintext highlighter-rouge">git fetch</code></h1>
<p>What: tired of having your old deleted branches showing up when tab-completing switching local git branches, and of having to manually run <code class="language-plaintext highlighter-rouge">$ git remote prune origin</code> to delete branch refs that are removed in origin? Then configure <code class="language-plaintext highlighter-rouge">git fetch</code> (and <code class="language-plaintext highlighter-rouge">git pull</code>) to automatically prune those!</p>

<p>How: run this:</p>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ <span class="n">git</span> <span class="n">config</span> --<span class="n">global</span> <span class="n">fetch</span>.<span class="n">prune</span> <span class="n">true</span>
</code></pre></div></div>]]></content><author><name>Erik Westrup</name></author><category term="Tech" /><category term="diggin-dotfiles" /><category term="dotfiles" /><category term="shell" /><category term="git" /><summary type="html"><![CDATA[Diggin’ Dotfiles Collection It’s time to dig the dotfiles, and to dig in to them! This post is a part of a blog post collection called Diggin’ Dotfiles where I unearth some gems from my personal dotfiles repo erikw/dotfiles. The following articles are a part of this series: Manage shell configurations across different systems Wrap Dangerous Commands in pre-post ZFS or BRTFS Snapshots Finger of god: using sudo with TouchID on macOS Generate permutation shell aliases Git hacks]]></summary></entry><entry><title type="html">AI-Solving the Telegram Event Saving Problem</title><link href="https://erikw.me/blog/tech/ai-solving-the-telegram-event-saving-problem/" rel="alternate" type="text/html" title="AI-Solving the Telegram Event Saving Problem" /><published>2025-09-08T19:50:00+02:00</published><updated>2025-09-08T19:50:00+02:00</updated><id>https://erikw.me/blog/tech/ai-solving-the-telegram-event-saving-problem</id><content type="html" xml:base="https://erikw.me/blog/tech/ai-solving-the-telegram-event-saving-problem/"><![CDATA[<h1 id="ai-vibe-coding-working-solution">AI Vibe Coding Working Solution</h1>
<p>In this article I will share how I explored <span class="jekyll-glossary">  vibe coding  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>An intuitive, exploratory approach to programming using AI that emphasizes creativity, flow, and experimentation over rigid structure or strict planning.<span class="jekyll-glossary-tooltip-hidden">)</span></span></span> a quick and working solution to a personal problem. Maybe it will inspire you to solve one of your own daily problems, where solution used to be out of (effort-) reach?</p>

<h1 id="the-problem-manual-saving-of-events-in-telegram">The Problem: Manual Saving of Events in Telegram</h1>
<p>I am no fan of the Telegram messenger, in several ways which is beyond the scope of this post, but it happens to be the platform where many events are shared in different groups in the city I live in. Thus, I’m more or less forced to use it.</p>

<p>In Telegram, I’m a member of several groups where events are share by users e.g. ecstatic dances, art nights, yoga and meditation classes etc. From time to time I drop in to check out what is on offer, and if I find something of interest I want to save this to my personal calendar, so that I can actually remember and potentially attend the event. The issue is that this is a lot of manual labor, that adds up when saving several events, involving copy-and-past event title, description, location, inserting right start and end times etc. Some events have a public web link that can be inserted, while some comes without which means that the event description must be added to the event as well. While I could copy a deep link to the Telegram message, I would like to avoid opening this app more than necessary (it’s an overwhelming and cluttered experience).</p>

<p>A typical free-structured event message shared in an Event-annoucement group:</p>

<p><img src="/assets/images/event_saver_message.png" width="40%" alt="A Telegram messae for an event." /></p>

<p>How to avoid manual copy-and-paste many information fields to my calendar?</p>

<h1 id="making-a-plan-with-ai">Making a Plan with AI</h1>
<p>First step was to explore the solution space using ChatGPT. After explaining my problem and goals to ChatGPT, we collaborated on a solution that pointed to a simple set up of:</p>

<ul>
  <li>Create a simple Telegram bot.
    <ul>
      <li>When I find an event I want to save, I simply forward that message to the bot.</li>
      <li>The bot can accept also a direct message, allowing for the use case that I have copied to my clipboard the description of an event that came from another source like a WhatsApp message e.g.</li>
      <li>The bot could also accept a direct message containing just a plain URL, allowing for the use case that I found an event though some other means and it does have a public webpage.</li>
    </ul>
  </li>
  <li><span class="jekyll-glossary">  Zapier  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>A no-code automation tool that connects apps and services so workflows run automatically.<br /><a class="jekyll-glossary-source-link" href="https://zapier.com" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> Flow (“Zap”)
    <ul>
      <li>The main part. The telegram bot uses a webhook to post received messages as input (trigger) to the Zapier flow</li>
      <li>The flow will then use a <span class="jekyll-glossary">  LLM  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>A Large Language Model — an AI trained on vast amounts of text to understand and generate human-like language.<span class="jekyll-glossary-tooltip-hidden">)</span></span></span> step using ChatGPT as a parser to pick out the event details of the message.</li>
      <li>Finally the event is saved to a new event-calendar in my personal Google Calendar.</li>
    </ul>
  </li>
</ul>

<p>Seems doable!</p>

<p>Exploring the solution space and making a general plan with ChatGPT:</p>

<p><img src="/assets/images/event_saver_chatgpt_plan.png" width="40%" alt="Exploring solutions with ChatGPT." /></p>

<h1 id="executing-the-plan-with-vibe-coding">Executing the Plan with Vibe Coding</h1>
<p>The Telegram bot creation was super simple, using the bot that create bots: <a href="https://telegram.me/BotFather">@BotFather</a>.</p>

<p>Delighted I was to discover that Zapier has no introduced <a href="https://zapier.com/blog/introducing-zapier-ai-agents/">AI Agents</a> allowing users to let the agent do most of the flow design and details. Within two hours from starting, I had a working solution that already handled many different input cases and error scenarios. The outcome in terms of easy of use and result was way over my expectations!</p>

<p>Now re-branded as “Copilot”, the agent makes Zap-creation a breeze with vibe coding:</p>

<p><img src="/assets/images/event_saver_zapier_agent.png" width="40%" alt="Zapier AI agent for vibe coding." /></p>

<p>The Zap Flow is structured like this:</p>
<ol>
  <li>Trigger: Telegram message from the linked bot created from before.
    <ul>
      <li>It turns out that there is a ready Telegram integration in Zapier, so I did not even need to set up a manual <span class="jekyll-glossary">  webhook  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>A way for one app to instantly send data to another when an event happens, like a real-time notification. Like a callback in C-programming but for webservices using REST APIs.<span class="jekyll-glossary-tooltip-hidden">)</span></span></span> configuration!</li>
    </ul>
  </li>
  <li>Action: Message processing with custom JavaScript.
    <ul>
      <li>As the Telegram messages will look a bit different depending on how they are sent, text preprocessing step was needed. Luckily the Zapier <span class="jekyll-glossary">  AI Agent  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>An autonomous AI that can reason, act, and interact with apps or people to achieve specific goals.<span class="jekyll-glossary-tooltip-hidden">)</span></span></span> could write most of the code, I just needed to guide it though some iterations of extensions and error fixing.</li>
      <li>Combination: if a message was forwarded to the Telegram bot, it appears under one key in the input dictionary. If it was a direct message, in another. The custom code combines these two input keys to one output.</li>
      <li>If the input was a single URL, instead do a quick web scraping extracting readable text from the webpage.</li>
      <li>Some messages are formatted with hyperlinks in the text which are converted to plain text as the Zapier input. The URLs are stored in a separate array structure. The code iterates and collects all URLs as a separate output for further processing in the flow.</li>
    </ul>
  </li>
  <li>Action: ChatGPT as a Information Extractor
    <ul>
      <li>Most of the time I used “AI to use AI”, instructing the AI Agent to improve the ChatGPT <span class="jekyll-glossary">  prompt  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>The input or instruction given to an AI that guides its response or output.<span class="jekyll-glossary-tooltip-hidden">)</span></span></span> used to extract event details. As I tested different real world examples of event posts shared in Telegram, I discovered more and more corner cases. This AI step extracts:</li>
      <li>Start and end datetime with instructions on fallback year/month if not mentioned and a fallback end time if not mentioned.</li>
      <li>Event title. Extract existing mentioned title in the post, or generate event summary as title.</li>
      <li>Event description.</li>
      <li>Location.</li>
      <li>Pricing or monetary contribution for attending the event.</li>
      <li>Structured collection of all URLs mentioned</li>
      <li>The actual prompt is shown further down</li>
    </ul>
  </li>
  <li>Action: Create Event in Google Calendar
    <ul>
      <li>Even though I use iCloud as my personal calendar, the easiest way forward was to create a Google Calendar that I subscribe to in my Apple Calendar, as there is no iCloud integration in Zapier at the moment.</li>
      <li>Given that the previous step outputted ready-to-use variables for each specified output, it was just a matter of inserting the right variables to the corresponding calendar event fields like title, location, start/end time etc.</li>
    </ul>
  </li>
</ol>

<p>To my surprise, this really works, and great too! Now it’s as easy as tapping on a message with event info in Telegram and forward it to my custom bot. Then seconds later the event shows up in my personal event calendar with all information (mostly) correct! With the location, pricing and URL readily accessible, I can make a spontaneous decision on the day if an event I was interested in before will fit my day’s schedule and budget. If it does, there will be information in the calendar event on where to buy ticket and where and where I should be.</p>

<p>The Zapier flow looks visually like this:</p>

<p><img src="/assets/images/event_saver_zapier_flow.png" alt="Some Pic" /></p>

<h1 id="prompt-for-data-extraction-with-chatgpt">Prompt for Data Extraction with ChatGPT</h1>
<p>It could be interesting to see the actual AI-generated <span class="jekyll-glossary">  prompt  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>The input or instruction given to an AI that guides its response or output.<span class="jekyll-glossary-tooltip-hidden">)</span></span></span> used for the ChatGPT extraction action. Most of it was AI generated and some details I jumped in and fixed myself:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Extract event details from the provided text. Parse out the following information:

- Event title (follow these rules):
  • If the message has a clear title or event name, use it exactly
  • If no clear title exists, create a concise 3-5 word summary of the event
  • Make it descriptive but brief (e.g., "Tech Meetup Downtown", "Birthday Party Sarah", "Jazz Concert Friday")
- Start date and time combined (follow these rules for the time part):
  • Extract the start time from the event details
  • Combine with the date to create a complete datetime
  • Use ISO 8601 format: YYYY-MM-DDTHH:MM:SS (e.g., 2024-03-15T19:00:00)
  • If multiple times are mentioned (like a schedule), use the EARLIEST time as the start time
  • DATE FALLBACK RULES:
    - If no year is found in the text, assume it's the current year (2025)
    - If no month is mentioned, assume it's the current month (January 2025)
    - If only a day of week is mentioned (e.g., "Friday"), use the next occurrence of that day
- End date and time combined (follow these rules):
  • If an explicit end time is mentioned, use it combined with the date
  • If a duration is mentioned (e.g., "2 hours", "90 minutes"), calculate the end time by adding the duration to the start time
  • If multiple times are mentioned (like a schedule), use the LATEST time as the end time
  • If neither end time nor duration is available, set the end time to 2 hours after the start time
  • Use ISO 8601 format: YYYY-MM-DDTHH:MM:SS (e.g., 2024-03-15T21:00:00)
  • DATE FALLBACK RULES:
    - If no year is found in the text, assume it's the current year (2025)
    - If no month is mentioned, assume it's the current month (January 2025)
    - If only a day of week is mentioned (e.g., "Friday"), use the next occurrence of that day
- Location (venue name, address, or area)
- URLs (follow these rules):
  • Find ALL URLs mentioned in the text
  • If a URL is missing "https://" prefix, add it automatically
  • If no URL is found, the output of this field URLs is just "No URLs found"
  • Return as a string with each URL on a separate line. Prefix each line with "- ", followed by the URL
  • Examples: "- https://example.com\n- https://meetup.com/event123\n- https://meetup.com/event456"
- Price (follow these rules):
  • Look for any pricing information in the text. This could also be stated as "donation" or "suggestion donation" or "energy exchange"
  • Extract ticket prices, entry fees, cost information
  • Include currency symbols and amounts (e.g., "$25", "€15", "Free", "$10-$50")
  • If no price information is found, return "Not found"
- Event description (including details about the event, pricing, requirements, etc.)

If some information is not available, return "Not specified" for that field. Be thorough in extracting all relevant details and apply the datetime calculation logic carefully.
</code></pre></div></div>

<h1 id="results">Results</h1>

<p>Saving an event is now as easy as forwarding a message to the custom Telegram bot, or copy-and-paste a message or URL to it…</p>

<p><img src="/assets/images/event_saver_telegram.png" alt="Some Pic" /></p>

<p>… then the event shows up in my personal Google Calendar event calendar!</p>

<p><img src="/assets/images/event_saver_google_calendar.png" width="50%" alt="Some Pic" /></p>

<h1 id="a-brave-new-world-with-vibes-sparkles">A Brave New World with Vibes :sparkles:</h1>
<p>The accessibility of AI tools unlocks solutions that were previously no within reach. Not that they couldn’t have been done, but the effort-outcome balance was too much on the effort side. Without AI tools I would certainly not have even tried solving this minor annoyance in my daily life. But now I could with just two hours of fun vibe coding get a solution that actually works well. I’m curios to see what other smaller or larger problems now have solutions within reach!</p>]]></content><author><name>Erik Westrup</name></author><category term="Tech" /><category term="AI" /><category term="Zapier" /><summary type="html"><![CDATA[AI Vibe Coding Working Solution In this article I will share how I explored vibe coding (An intuitive, exploratory approach to programming using AI that emphasizes creativity, flow, and experimentation over rigid structure or strict planning.) a quick and working solution to a personal problem. Maybe it will inspire you to solve one of your own daily problems, where solution used to be out of (effort-) reach?]]></summary></entry><entry><title type="html">Intermittent Fasting as a Spiritual Practice</title><link href="https://erikw.me/blog/buddhism/intermittent-fasting-as-a-spiritual-practice/" rel="alternate" type="text/html" title="Intermittent Fasting as a Spiritual Practice" /><published>2025-05-03T18:10:00+02:00</published><updated>2025-05-03T18:10:00+02:00</updated><id>https://erikw.me/blog/buddhism/intermittent-fasting-as-a-spiritual-practice</id><content type="html" xml:base="https://erikw.me/blog/buddhism/intermittent-fasting-as-a-spiritual-practice/"><![CDATA[<p><em>This post was written on an empty stomach.</em></p>

<h1 id="intermittent-fasting">Intermittent Fasting</h1>
<p>By now, actually the latest by 2020 according to <a href="https://trends.google.com/trends/explore?date=all&amp;q=%2Fm%2F04ycpbq&amp;hl=en">Google Trends</a>, you should have heard about Intermittent Fasting (<em>IF</em> from now on). A fad diet? Or does it have something to it. That’s something for you to try out yourself.</p>

<p>I have done the time-restricted version of IF 5-7 days a week for an unknown amount of years, maybe 4 to 5, mostly 16:8 (16 fasting, 8 hours eating window) and 19:5. No surprise that I have found many benefits of it. There are many health claims that you can <a href="https://www.healthline.com/nutrition/intermittent-fasting-guide#effects">read</a> about including cellular repair and insulin level regulation. I won’t go in to any of these. Most importantly is to try and see what effects do you really experience.</p>

<p>Apart from the health benefits, there is a clear financial interest as well. Reducing the daily food intake to one larger meal and one smaller, can over time save you quite some money in the household budget!</p>

<p>Read on to learn how I made IF a spiritual practice as well.</p>

<h1 id="recontextualization-in-to-a-spiritual-practice">Recontextualization in to a Spiritual Practice</h1>
<p>Having already done IF for a while before coming in contact with the teachings of the Buddha and starting to (attempting to) putting those in to practice I realized that they go very well together. The IF practice can be (re-)contextualized as a spiritual practice, which should not sound strange as some sort of fasting is common in most common religions <sup id="fnref:world-fasting"><a href="#fn:world-fasting" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>.</p>

<p>Experienced health-benefits apart, something that I soon came to appreciate is that the practice of IF trains one’s mind to not react to the negative feeling (or emotions that follows thereafter) of hunger. In the past, whenever I felt hungry I would immediately act up on this thinking this is the proper thing to do, with an underlying view that otherwise some disaster would break out. Indeed, I could often get <a href="https://www.merriam-webster.com/dictionary/hangry">hangry</a>. After some time of practicing IF, I’ve retrained my brain’s habitual pattern to not react on the hungry, at least not as strongly. I have learned that this a fleeting emotion that also goes away <sup id="fnref:snack"><a href="#fn:snack" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>. The context is here of course that I happen to be very lucky live in a space-time where food is readily available, thus the feeling of hunger is mostly not an issue as food can be readily taken if/when really needed.</p>

<p>Not habitually responding to feelings goes very well with that of the Buddhist practices. In <span class="jekyll-glossary">  śamatha  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>Tranquility and calm-abiding meditations. One of two main meditation categories in Buddhism, the other being vipassanā. śamatha practices produce effects of calming the mind and develops concentration.<br /><a class="jekyll-glossary-source-link" href="https://encyclopediaofbuddhism.org/wiki/Samatha" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> meditation, e.g <span class="jekyll-glossary">  anapanasati  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Skt: anapanasmrti) a meditation practice also known as mindfulness of breathing. It is a form of śamatha meditation.<br /><a class="jekyll-glossary-source-link" href="https://www.wisdomlib.org/definition/anapanasati" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span>, one of the qualities that is cultivated is <span class="jekyll-glossary">  upekkhā  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Skt: upekṣā) a state of equanimity, a mental state of balance and non-reactivity.<br /><a class="jekyll-glossary-source-link" href="https://encyclopediaofbuddhism.org/wiki/Upek%E1%B9%A3%C4%81" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> which includes being in a state of not reacting to positive, neutral or negative feelings <sup id="fnref:feelings"><a href="#fn:feelings" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>. Furthermore, it helps us to clearly see that one of the characteristics (<span class="jekyll-glossary">  trilakṣaṇa  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Pali. tilakkhaṇa) The three marks of conditioned existence in samsara: Anicca (impermanence), Dukkha (suffering, dissatisfaction), Anatta (not-self, insubstantiality).<br /><a class="jekyll-glossary-source-link" href="https://encyclopediaofbuddhism.org/wiki/Three_marks_of_existence" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span>) of our experienced world (<span class="jekyll-glossary">  samsara  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>The endless cyclic existence of birth, death, and rebirth.<br /><a class="jekyll-glossary-source-link" href="https://www.wisdomlib.org/definition/samsara#buddhism" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span>) is that of <span class="jekyll-glossary">  impermanence  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Skt. anityatā) The first of the three marks of existence, meaning impermanence. Everything is in a constant state of flux.<br /><a class="jekyll-glossary-source-link" href="https://encyclopediaofbuddhism.org/wiki/Anitya" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span>. More than seeing, really experiencing and knowing at the depth, that the negative feeling of hunger is also impermanent and will not last is significant for spiritual development.</p>

<p>Furthermore, I experience that in the fasting window, while at some times I can experience a dip in energy, at the same time I have many moments of vigor and lightness that are great for meditation practices. It’s no surprise that some Buddhist tradition like that of the <span class="jekyll-glossary">  Theravāda  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>The oldest surviving school of Buddhism, which is based on the Pali Canon and is prevalent in Sri Lanka and Southeast Asia.<br /><a class="jekyll-glossary-source-link" href="https://encyclopediaofbuddhism.org/wiki/Therav%C4%81da" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> have developed a monastic lifestyle where most of the time day is in a fasting window after the day’s single meal before noon <sup id="fnref:buddhist-fasting"><a href="#fn:buddhist-fasting" class="footnote" rel="footnote" role="doc-noteref">4</a></sup>. Recently I’ve come to align my daily schedule have the eating window 1pm - 6pm and can thus enjoy an easy sleep on a non-processing stomach, as well as having the benefit of being in the late part of the fasting window when I do my daily sporting routines. This could be beneficial as it is believed by some that more fat is burned when exercising during the end of a fast <sup id="fnref:cardio-fast"><a href="#fn:cardio-fast" class="footnote" rel="footnote" role="doc-noteref">5</a></sup>.</p>

<p>To be clear, I don’t mean fasting in an extreme sense. We know from the mythical life-story of the Buddha that he Buddha-to-be practiced six years of ascetic practices, including extreme fasting (to the point of starvation), and he realized that this also is not a way that leads to the total liberation from the <span class="jekyll-glossary">  cycles for rebirths  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>The endless cyclic existence of birth, death, and rebirth.<br /><a class="jekyll-glossary-source-link" href="https://www.wisdomlib.org/definition/samsara#buddhism" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> <sup id="fnref:mn36"><a href="#fn:mn36" class="footnote" rel="footnote" role="doc-noteref">6</a></sup>. It is of course as always in the teachings of the Buddha, a balanced <span class="jekyll-glossary">  middle way  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Skt: Madhyamāpratipada) The Middle Way, a central teaching of the Buddha that avoids extreme views and practices. The two most important being: self-indulgence vs self-mortification, eternalism vs nihilism.<br /><a class="jekyll-glossary-source-link" href="https://encyclopediaofbuddhism.org/wiki/Middle_Way" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> of practice that we should strive for.</p>

<p>Seeing the qualities of samsara is not the whole way however; we also need to practice seeing the qualities of <span class="jekyll-glossary">  nirvāṇa  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Pali. nibbāna) The state of liberation from samsara, the end of suffering.<br /><a class="jekyll-glossary-source-link" href="https://www.wisdomlib.org/definition/samsara#buddhism" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> (blissful, eternal, true selfhood)<sup id="fnref:bhante-what-is-dharma"><a href="#fn:bhante-what-is-dharma" class="footnote" rel="footnote" role="doc-noteref">7</a></sup> This I’m not sure if IF a helpful method for, but practicing seeing the impermanence of our experienced world is a very good start!</p>

<h1 id="food-ritual-gratitude--the-interconnectedness-of-all-phenomena">Food-Ritual: Gratitude &amp; the Interconnectedness of All Phenomena</h1>
<p>The last year I’ve introduced a ritual to break the fast as this event is an excellent opportunity for practice. Instead of finally giving in to the wonderful sensations of eating food, it is a great moment to practice patience and to break the fast with mindfulness. It goes like this:</p>

<ul>
  <li>:palms_up_together: <strong>Open the space</strong>: Use the aid of physical action and symbolism to get in the right mindset: grab the full plate in an offering gesture <sup id="fnref:offering-food"><a href="#fn:offering-food" class="footnote" rel="footnote" role="doc-noteref">8</a></sup>. Gaze a few moments at the food, take in the presentation, colors, textures and smell. Close the eyes to enter contemplation.</li>
  <li>:pray: <strong>Gratitude</strong>: Bring to mind how the food in front of you ended up on this plate. Did you cook it yourself?; thank yourself for taking time to preparing this nutritios meal. Was it cooked and brought to you by another person?; give proper moments to cultivate thankfulness to this person. Even if this person was paid to do the job, consider it a genuine gift from that person to you. Start tracing back further and further: who bought the ingredients, who carried them, who sold them, which persons were involved to create the opportunity for storage and transportations, who grew those grains or vegetables, which persons were involved producing the tools needed for them to do their work on the farmland field? For each person that comes to mind, take a proper moment and cultivate gratitude towards their gift to you.</li>
  <li>:paperclips: <strong>Interconnectedness</strong>: It does not take long until the tracing back grows like an exponential curve. This ritual now transitions to the stage of seeing the interconnectedness of all phenomena, known as <span class="jekyll-glossary">  pratītyasamutpāda  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Pali: paṭiccasamuppāda) The principle of dependent origination, which states that all phenomena arise in dependence on other phenomena. This is the most central concept in Buddhism.<br /><a class="jekyll-glossary-source-link" href="https://encyclopediaofbuddhism.org/wiki/Pratityasamutpada" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span>. As you trace back to all the conditions though the ages that gave rise to the eventual production of the meal in front of you, you can get a glimpse of how the whole universe is interconnected. Nothing stands on its own. Maybe a glimpse of <span class="jekyll-glossary">  anātman  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Pali: anattā) The doctrine of non-self, which states that there is no permanent, unchanging self or soul in living beings. This is one of the three marks of existence.<br /><a class="jekyll-glossary-source-link" href="https://encyclopediaofbuddhism.org/wiki/Pratityasamutpada" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span>, seeing that you are not separated from others.</li>
  <li>:earth_africa: <strong>Existential Gratitude</strong>: Now take some moments to direct thankfulness to the earth, maybe personified as Mother Earth if that makes it easier, or the universe itself, for providing us this opportunity to experience life here and now. For giving us the nutrients, air, climate etc. being conditions for our life.</li>
  <li>:sparkles: <strong>Dedication</strong>: Ending with a dedication, that the energy sourced from this food shall be used today to conduct <span class="jekyll-glossary">  skillful actions  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Pali: kusala) The Buddhist term for wholesome or skillful actions, as performed with body, speech and mind which lead to positive results.<br /><a class="jekyll-glossary-source-link" href="https://encyclopediaofbuddhism.org/wiki/Pratityasamutpada" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> for the liberation of suffering from all sentient beings. A high goal indeed, however setting the intention will have (positive) consequences when honestly done.</li>
</ul>

<p>I originally learned a food-gratitude practice from a <a href="https://sareakyar.de/">friend</a> and came to evolve it as outlined above, connecting it with teachings of the Buddha. Later I came to learn when staying at a Tibetan monastery <sup id="fnref:kopan"><a href="#fn:kopan" class="footnote" rel="footnote" role="doc-noteref">9</a></sup> that similar food-gratitude practice exist in the Tibetan traditions and surely in other traditions as well.</p>

<h1 id="footnotes">Footnotes</h1>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:world-fasting">
      <p>Religions as mentioned at <a href="https://en.wikipedia.org/wiki/Intermittent_fasting">Wikipedia on IF</a> <a href="#fnref:world-fasting" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:snack">
      <p>I always do in my daily backpack have some sort of emergency snack with me, for those times of unexpectably long fasting occurs on the go. Usually I’ll have a green tea during the last 2-3 hours of the fast as well, as it really kills of any hunger! <a href="#fnref:snack" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:feelings">
      <p>Note that the scope of <em>feelings</em> (<span class="jekyll-glossary">  vedanā  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>The Pali word for feeling, sensation or perception. It is positive, neutral or negative and is one of the five aggregates (skandhas) in Buddhism.<br /><a class="jekyll-glossary-source-link" href="https://encyclopediaofbuddhism.org/wiki/Vedan%C4%81" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span>) is narrower than in the common sense. In Buddhist literature, feelings appear to us in positive, neutral of or negative character. From feelings emotions can arise, which is of a greater spectrum. <a href="#fnref:feelings" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:buddhist-fasting">
      <p><a href="https://en.wikipedia.org/wiki/Fasting_in_Buddhism">Fasting in Buddhism</a> <a href="#fnref:buddhist-fasting" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:cardio-fast">
      <p><a href="https://www.healthline.com/health/fitness/benefits-of-fasted-cardio">Benefits of Fasted Cardio</a> <a href="#fnref:cardio-fast" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:mn36">
      <p><a href="https://www.accesstoinsight.org/tipitaka/mn/mn.036.than.html">MN36</a> <a href="#fnref:mn36" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:bhante-what-is-dharma">
      <p>Sangharakshita, What is the Dharma?, chapter 5. <a href="#fnref:bhante-what-is-dharma" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:offering-food">
      <p>It is of course not the physical act of holding the plate in any particular way that does something, but the state of mind the action is done. However taking a physical action can aid one to get in to a particular state of mind. <a href="#fnref:offering-food" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:kopan">
      <p><a href="https://maps.app.goo.gl/iN7BxHW7UBmR6TqW8">Kopan monastery</a>, Nepal <a href="#fnref:kopan" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Erik Westrup</name></author><category term="Buddhism" /><category term="fasting" /><category term="practice" /><category term="buddhism" /><summary type="html"><![CDATA[This post was written on an empty stomach.]]></summary></entry><entry><title type="html">Impermanent Programming</title><link href="https://erikw.me/blog/buddhism/impermanent-programming/" rel="alternate" type="text/html" title="Impermanent Programming" /><published>2025-03-08T11:50:00+01:00</published><updated>2025-03-08T11:50:00+01:00</updated><id>https://erikw.me/blog/buddhism/impermanent-programming</id><content type="html" xml:base="https://erikw.me/blog/buddhism/impermanent-programming/"><![CDATA[<h1 id="blog-enters-a-new-domain">Blog Enters a New Domain</h1>
<p>Whoops here we go, time to test the deep waters <sup id="fnref:no-claims"><a href="#fn:no-claims" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>: writing about Tech +/with/other-way-around-maybe <span class="jekyll-glossary">  Buddhism  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>Buddhism is definitely not what you think, in two ways: 1) it takes years of practice with study to develop insights into what it really is and 2) [literally] it’s in the end not something you think about, not an intellectual exercise, but something you experience. Nevertheless, start learning more here:<br /><a class="jekyll-glossary-source-link" href="https://tricycle.org/beginners/" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span>.</p>

<p>I will here start exploring how the teachings of the Buddha apply to one who’s profession and craft is that of software development.</p>

<h1 id="the-characteristics-of-our-world">The Characteristics of Our World</h1>
<p>Through your life experience so far, might you have noticed some patterns on a smaller or larger scale how life in this world works for a human, especially in terms of happiness and the opposite? A classic example would be to work very hard for something, and our hopes of happiness that will be rewarded to us once we get it, and once we acquire it, we don’t really enjoy it. That we now possess that new object, status or accomplishment is soon forgotten because we have trained our mind to keep looking for happiness elsewhere; in the future.</p>

<p>No doubt we have all experienced dissatisfaction in life, from severe pain to unrest stemming from desire for something. In my own experience I’ve been battling with the insight that even those happy moments, moments that should be happy at least, that I’m not really truly enjoying them at the most ultimate sense. While I did experience emotions of happiness, it was not ultimately satisfying. This insight crushed me, because happiness is what I had been chasing thinking this is the solution to human’s situation. It was even declared as my meaning-of-life: “create happiness for me and help others experience the same”.</p>

<p>Not unlikely you have also made the connection that many times, dissatisfaction of life stems from that things are not ever-lasting; (happy) emotions, objects, your work outcome, and most importantly life in this body itself. Other sources of an unpleasant experience is with no doubt when we cling to objects, objects here in the wider sense including our own mental views for example, or when we engage in and act out of various degrees of aversion to hatred. Something we do as developers is that in gaining experience, we acquire opinions about technical solutions, we become more opinionated. This is celebrated as a mark of progression on the career in the IT-field and surely is good for the sake of engineering. But what do all these opinions do to our head, to our mental experience of the world?</p>

<p>From my own experience I see a direct link between the increase of opinionatedness and the increase of subtle mental stress and dissatisfaction. The more views of what is right/wrong, better/worse way of doing things, the more dualities in other words, the more risk that we will experience dissatisfaction when the world does not go to our liking. Now there’s a wonderful concept called equanimity that can help here, and be developed through meditation practices. That’s for another blog post. Oh wait, there is already a <a href="/blog/tech/daily-routine-with-repeating-sub-reminders-in-apple-reminders/">post</a> touching this topic; my personal revolution breaking myself free from decades of built-up opinions leading to a complex digital life, to a simple setup using as many pre-installed apps and settings left to default as possible.</p>

<p>As I first came in contact with Buddha’s teachings of the <span class="jekyll-glossary">  trilakṣaṇa  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Pali. tilakkhaṇa) The three marks of conditioned existence in samsara: Anicca (impermanence), Dukkha (suffering, dissatisfaction), Anatta (not-self, insubstantiality).<br /><a class="jekyll-glossary-source-link" href="https://encyclopediaofbuddhism.org/wiki/Three_marks_of_existence" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> and the <span class="jekyll-glossary">  triviṣa  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Skt. triviṣa; Pali. tivisa) The three mental poisons that drive our unwholesome actions: greed, hatred and ignorance.<br /><a class="jekyll-glossary-source-link" href="https://www.lionsroar.com/buddhism/three-poisons/" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> it did not take long before I could see how this applied to my life as a developer <sup id="fnref:me-developer"><a href="#fn:me-developer" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>. The teachings of trilakṣaṇa pinpoints the characteristics of living in this world, that everything we experience has the marks of impermanence, dissatisfaction and insubstantiality. This is not necessarily easy to take in. Better look at your own experience; is your experience like that, if you take a deep look and be very honest? The teachings of triviṣa, the three mental poisons that drive our unwholesome actions being greed, hatred and ignorance.</p>

<p>Through my life experiences up to that point, I had enough personal evidence of these teachings that I could immediately accept them (in an intellectual way, a truly full understanding would mean already having some degree of enlightenment). Then through increased daily meditation and reflection, I started to see more subtle mental thought patterns and the outcomes of them and could start applying the teachings to my own situation.</p>

<h1 id="this-code-is-impermanent">This Code Is Impermanent</h1>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Hello, samsara!</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>I’m a person that can become very engaged, committed and invested in tasks that I take on. Mostly I find joy in perfecting and engaging fully in what I do, but it also has its dark sides. I became aware that after producing some piece of software or project, there has built up some level of attachment to it and underlying view that expects this outcome to exist “forever”. Reality, that any seasoned developer will know, is that the code we write will often not live for long. Projects are scrapped in both startups and larger companies, scopes are changed and things rewritten, decommissioned, heavily refactored.</p>

<p>When reality dawns that the code I’ve written, that very well functioning, good looking <sup id="fnref:beauty-code"><a href="#fn:beauty-code" class="footnote" rel="footnote" role="doc-noteref">3</a></sup> and carefully unit-tested code I’ve spent so much mental energy and time on, there is <span class="jekyll-glossary">  dukkha  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Skt. duḥkha; Pali. dukkha) Most extremely meaning suffering, but has many degrees of unsatisfactoriness, an experience of ill-fitting experienced in different ways.<br /><a class="jekyll-glossary-source-link" href="https://www.wisdomlib.org/concept/dukkha" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span>. Sometimes a gross let-down when the project manager announces that the project that is almost finished after months of work, is being scrapped to a more subtle feeling of dissatisfaction and pointlessness lingering in the background stemming from the knowledge that most code I’ve written is not having that great impact as envisaged during its production.</p>

<h1 id="towards-what-is-permanent">Towards What Is Permanent</h1>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Hello, nirvāṇa!</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>What to do? Our world is characterized by impermanence, dissatisfaction and insubstantiality and the code we write have these characteristics as well. Should we stop writing good code, write less unit tests, force push to production or add a <code class="language-plaintext highlighter-rouge">while true</code> loop around your program’s entry point to just restart your app when it fails instead of fixing the root cause? Maybe some of these, sometimes, but it is not as simple as giving an answer like this. Luckily for living beings, there is an experience that is free from these marks of cyclic existence in <span class="jekyll-glossary">  samsara  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>The endless cyclic existence of birth, death, and rebirth.<br /><a class="jekyll-glossary-source-link" href="https://www.wisdomlib.org/definition/samsara#buddhism" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> the permanent state of <span class="jekyll-glossary">  nirvāṇa  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Pali. nibbāna) The state of liberation from samsara, the end of suffering.<br /><a class="jekyll-glossary-source-link" href="https://www.wisdomlib.org/definition/samsara#buddhism" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span>. Now that is a very high aspiration so let us come down to samsara for a while to see what we can do here, and save a discussion of the higher goal for another blog post.</p>

<p>Here are some thoughts that I’ve had, and no answers – that you have to find out for yourself.</p>

<p>One way to come to terms with reality is to think of our work (read: software) not as a resulting object that came in to existence and will continue to exist, but rather that our work, our code, is a contribution to a stream of energy moving forwards, giving birth to the next moment (of your company/product). Sounds a bit hippy-fluffy maybe, but think about it; what it could mean? A practice of reducing attachment to a view we have in our mind (concisely and subconsciously) that things inherently have an existence. Taking it down to something concrete: sit down and meditate, regularly, and things start looking quite different.</p>

<p>To get some perspective, I think it is very helpful to not sit in front of the computer all day. Extra warning for developers in home office (I speak of my own experience). Get out in nature, and see if you can find the joy and expand our minds that we contract so much to fit inside a computer’s working as developers.</p>

<p>We could draw inspiration from the Japanese Buddhist traditions that emphasizes the perfection of craft as a spiritual practice with mindfulness. Google term: <em>Monozukuri</em>.</p>

<p>Another method can be to navigate the extremes of views/states from not caring about our code to the opposite of strong attachment i.e. look for where the <span class="jekyll-glossary">  majjhimāpaṭipadā  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>(Skt: Madhyamāpratipada) The Middle Way, a central teaching of the Buddha that avoids extreme views and practices. The two most important being: self-indulgence vs self-mortification, eternalism vs nihilism.<br /><a class="jekyll-glossary-source-link" href="https://encyclopediaofbuddhism.org/wiki/Middle_Way" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> is.</p>

<p>A final idea; raise our perspectives. Realize that any outcome in the “worldly world” is just that, contained in this worldly experience. Instead, seek desire and attainment in something that really takes us towards the other shore.</p>

<p>Some thoughts, some ideas. Now you have to take in from here.</p>

<h1 id="footnotes">Footnotes</h1>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:no-claims">
      <p>Note that one’s understanding of the teachings of Buddha is ever progressing, and this blog post is a mere snapshot of my current understanding at this very moment. No doubt in a few years from now I will have a different understanding of these topics than now at the time of writing. <a href="#fnref:no-claims" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:me-developer">
      <p>or rather: in my experience as doing developer actions, as a developer is not an inherent quality of a “me” :wink: <a href="#fnref:me-developer" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:beauty-code">
      <p>Might be subjective. <a href="#fnref:beauty-code" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Erik Westrup</name></author><category term="Buddhism" /><category term="buddhism" /><category term="programming" /><summary type="html"><![CDATA[Blog Enters a New Domain Whoops here we go, time to test the deep waters 1: writing about Tech +/with/other-way-around-maybe Buddhism (Buddhism is definitely not what you think, in two ways: 1) it takes years of practice with study to develop insights into what it really is and 2) [literally] it’s in the end not something you think about, not an intellectual exercise, but something you experience. Nevertheless, start learning more here:). Note that one’s understanding of the teachings of Buddha is ever progressing, and this blog post is a mere snapshot of my current understanding at this very moment. No doubt in a few years from now I will have a different understanding of these topics than now at the time of writing. &#8617;]]></summary></entry><entry><title type="html">Daily Routine With Repeating Sub-reminders in Apple Reminders</title><link href="https://erikw.me/blog/tech/daily-routine-with-repeating-sub-reminders-in-apple-reminders/" rel="alternate" type="text/html" title="Daily Routine With Repeating Sub-reminders in Apple Reminders" /><published>2025-02-16T16:44:00+01:00</published><updated>2025-02-16T16:44:00+01:00</updated><id>https://erikw.me/blog/tech/daily-routine-with-repeating-sub-reminders-in-apple-reminders</id><content type="html" xml:base="https://erikw.me/blog/tech/daily-routine-with-repeating-sub-reminders-in-apple-reminders/"><![CDATA[<p>Jump to <a href="#tldr-show-me-how">TL;DR</a>.</p>

<h1 id="background">Background</h1>
<p>The last few years I’ve been following a trend of creating more simplicity and contentment, (3rd positive Buddhist <a href="https://thebuddhistcentre.com/system/files/groups/files/the_refuges_and_precepts_v2.pdf">precept</a>) and especially in my digital life (given my profession as software engineer). No doubt also inspired by the <a href="https://www.goodreads.com/book/show/40672036-digital-minimalism">digital minimalism</a> movement. Through increased daily meditation, I’ve been able to see how me being very opinionated is often a cause of negative emotions and mental stress. It is a very useful thing to be opinionated in tech, and is one accumulated effect and skill developed through experiences. However it has been great practice for me to be more content and practice equanimity in my mind thought patterns. Practically this means that after having used tech very heavily since childhood, I’ve built up very complex setups of both my desktop computer system and mobile phones and I have focused on slimming this down.</p>

<p>My strategy has been to challenge myself to move more and more towards using whatever apps/programs are provided by default, as well as using as many default values on settings as possible. This also triggered me to abandon Android as my main phone OS to iOS, striving to have everything “just work out of the box” once logging in on a new system with my Apple ID (simply, Apple does this way easier and better than Google (oh look at that, opinionatedness creped in again ;))). It has also meant the abandonment of subscription services like Spotify and Netflix, to practice contentment and also having the benefit of not being overwhelmed by choice. I have realized how having access to all the media of the world creates a subtle mental stress that leads mind-actions to always wanting to explore what is next instead of really enjoying what I have (I now rent/buy individual albums and movies instead).</p>

<p>This is a clear opposite direction which I had in my tech career so far, which was heavily towards the side of open source, open standards and data. This is not to say that I now have changed my opinion really, either direction has its advantages and I think no less of Open Source software in the past. This is not about that, it is a mind and habitual thinking practice to challenge myself to develop equanimity. Again being opinionated can be good, even better is to have contentment and analytical ability to know when is the right time to be opinionated. That said, I’ve swung the pendulum of action-to-counter-reaction quite far in the new direction, and it is bound to swing back towards the old, hopefully stopping at a middle ground. I see that the next level would be to not even have to move from one set up or thinking to the other, but to be content even with just what is. First step though, break out of old habitual thinking and clinging to opinions and old thoughts.</p>

<p>In this blog post, I want to share with you how I moved from <a href="https://www.todoist.com/">Todoist</a>, which is a far superior todo app, in terms of features, than <a href="https://www.icloud.com/reminders/">Apple Reminders</a>, which I have used as a power user for many many years. I was happy to challenge myself in this, to use a product having less features (even though the last years Apple Reminders have been keeping up well with new features), and moving away from a product I had a strong liking for. It went mostly smooth after accepting the difference in UX and data models. However one thing was hard for me, to manage my daily routine tasks. There are certain tasks that I want to do ideally every day. Admittedly certain days this list becomes more of a wishlist, but it can help me to prioritize selecting a few of these wished-for tasks and completing them for the day.</p>

<h1 id="recurring-tasks-for-my-daily-routine">Recurring Tasks for My Daily Routine</h1>
<p>In Todoist I had a recurring task called “Daily Routine” with a few sub tasks that were also <a href="https://www.todoist.com/help/articles/reset-a-sub-task-in-todoist-V6SRBGPw0">recurring</a>. Having the daily tasks hidden inside the main task has the benefit of not decluttering the home screen where the tasks schedule for today’s date is shown.</p>

<p>Apple Reminder does have recurring reminders, however that does not apply to sub-reminders. I could have made the individual sub-reminders recurring, however that would have cluttered my Today view.  I tried this for a while but had to admit that I’ve gone too far in my way of accepting the defaults, because with this setup I struggled to complete both the daily routine tasks and other tasks due to the cluttered Today home screen. However I managed to find a solution that did not involve installing yet another app (that would have been +-0, no gain of simplicity) by using the built-in Shortcuts app in iOS. I was happy with this solution.</p>

<p>Here’s a screenshot of how my Daily Routine reminder looks in the Today’s home screen view, and how it looks when expanding the sub-reminders. <sup id="fnref:note-practice"><a href="#fn:note-practice" class="footnote" rel="footnote" role="doc-noteref">1</a></sup></p>

<p><picture><img src="/assets/images/apple_reminders_daily_routine_today.png" alt="Reminders Daily Routine in Today View" loading="lazy"></picture>
<picture><img src="/assets/images/apple_reminders_daily_routine_subreminders.png" alt="Reminders Daily Routine Sub-reminders" loading="lazy"></picture></p>

<h1 id="tldr-show-me-how">TL;DR Show Me How</h1>

<p>In Apple Reminders:</p>
<ol>
  <li>Create a new Reminder named “<code class="language-plaintext highlighter-rouge">Daily Routine 🔁</code>”
    <ul>
      <li>Schedule the main reminder to have due date Today.</li>
      <li>Add to the Tags field a new tag “<code class="language-plaintext highlighter-rouge">#daily-routine</code>”</li>
      <li>I keep all my repeating tasks in a “Recurring” list to keep them organize. Put it in what ever list makes sense for you.</li>
    </ul>
  </li>
  <li>Add sub-reminders to this new main reminder, your daily routine activities.
    <ul>
      <li>Don’t put in any Due date on the sub-reminders, leave that for the main top-level reminder above.</li>
    </ul>
  </li>
</ol>

<p>In the Shortcuts app:</p>
<ol>
  <li>Import this shortcut <a href="https://www.icloud.com/shortcuts/d9c93c3a665446c5b084c27d91f0f55d">Reset Daily Routine Reminders</a>, or look screenshots further down and re-create it yourself.
    <ul>
      <li>Test: Complete some of the sub-reminder and/or the main reminder in Apple Reminder</li>
      <li>Now in Shortcuts app, manual run the new shortcut by tapping it. Verify that the Daily Routine reminder is not rested including all sub-reminders</li>
    </ul>
  </li>
  <li>Switch to the Automation tab and create a new automation as follows:
    <ul>
      <li>Automation: Run Immediately</li>
      <li>Notify When Run: Off</li>
      <li>When: At 03:00, daily</li>
      <li>Do: run shortcut “Reset Daily Routine Reminders”</li>
    </ul>
  </li>
</ol>

<h1 id="how-it-works">How It Works</h1>
<p>The shortcut is a combination of a few different solutions that I have found out there on the Internets already. Hats of to them (see references in the comments of the Shortcut itself).</p>

<p>At 3am, I assume the daily routine is not being worked on and the “new day” begins as the Shortcuts automation runs. The Shortcut fill find all reminders tagged with the tag “<code class="language-plaintext highlighter-rouge">#daily-routine</code>”, and you should set this only on the top-level main Daily Routine reminder. Then it will simply change the Due date to today (the new day), uncomplete the reminder if it was completed. Then it continues and uncompletes all sub-reminders.</p>

<p>Here is a screenshot of the full Shortcut contents, in case the shared URL would stop working in the future.</p>

<p><a href="/assets/images/apple_shortcuts_reset_daily_routine.png"><img src="/assets/images/apple_shortcuts_reset_daily_routine.png" alt="Shortcuts full content" width="50%" /></a>
<br /><br /></p>

<p>Here is the automation to run the above Shortcut every day at 3am.</p>

<p><a href="/assets/images/apple_shortcuts_automation.png"><img src="/assets/images/apple_shortcuts_automation.png" alt="Shortcuts Automation" width="50%" /></a></p>

<h1 id="terminology">Terminology</h1>
<p>Todoist uses terms <em>task</em>, <em>recurring</em>.</p>

<p>The corresponding terms in Apple Reminders are <em>reminders</em>, <em>repeating</em>.</p>

<h1 id="footnotes">Footnotes</h1>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:note-practice">
      <p>Note that quite a few of my daily reminders are Buddhist practices, and none of these should be done with a state of mind as seeing them as a “routine” or a task to complete. Then they would lose most possible benefits already before they are done! The benefit for me to keeping them here is the aforementioned situation of needing to prioritize on days with less time available which practices to focus on. <a href="#fnref:note-practice" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Erik Westrup</name></author><category term="Tech" /><category term="todos" /><category term="minimalism" /><category term="buddhism" /><summary type="html"><![CDATA[Jump to TL;DR.]]></summary></entry><entry><title type="html">Generate permutation shell aliases</title><link href="https://erikw.me/blog/tech/generate-permutation-shell-aliases/" rel="alternate" type="text/html" title="Generate permutation shell aliases" /><published>2024-10-28T07:48:00+01:00</published><updated>2024-10-28T07:48:00+01:00</updated><id>https://erikw.me/blog/tech/generate-permutation-shell-aliases</id><content type="html" xml:base="https://erikw.me/blog/tech/generate-permutation-shell-aliases/"><![CDATA[<h1 id="diggin-dotfiles-collection">Diggin’ Dotfiles Collection</h1>
<p>It’s time to dig the dotfiles, and to dig in to them! This post is a part of a blog post collection called <em>Diggin’ Dotfiles</em> where I unearth some gems from my personal dotfiles repo <a href="https://github.com/erikw/dotfiles">erikw/dotfiles</a>.</p>

<p><a href="https://github.com/erikw/dotfiles" target="_blank">
  <img align="center" src="https://github-readme-stats.vercel.app/api/pin/?username=erikw&amp;repo=dotfiles" alt="" />
</a></p>

<p>The following articles are a part of this series:</p>
<ul>
  <li><a href="/blog/tech/diggin-dotfiles-manage-shell-configurations-across-different-systems/">Manage shell configurations across different systems</a></li>
  <li><a href="/blog/tech/wrap-dangerous-commands-in-pre-post-zfs-or-brtfs-snapshots/">Wrap Dangerous Commands in pre-post ZFS or BRTFS Snapshots</a></li>
  <li><a href="/blog/tech/finger-of-god-using-sudo-with-touchid-on-macos/">Finger of god: using sudo with TouchID on macOS</a></li>
  <li><a href="/blog/tech/generate-permutation-shell-aliases/">Generate permutation shell aliases</a></li>
  <li><a href="/blog/tech/git-hacks/">Git hacks</a></li>
</ul>

<h1 id="how-i-not-learned-to-spell-and-learned-to-love-generated-aliases">How I Not Learned to Spell and Learned to Love Generated Aliases</h1>

<p>How many times per day don’t you mistype a shell command? In focused coding session with the adrenaline pumping as a new feature is closing in on working, you just need to compile the updated code quickly, issuing the <code class="language-plaintext highlighter-rouge">make</code> command. Your fingers knows the letters, and presses them all at the same time in excitement. Your result:</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>meak
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>maek
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>keam
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>eamk
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>emka
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>aekm
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>amek
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>mkae
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>kaem
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>kmae
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>kame
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>eakm
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>emak
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>meka
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>ekam
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>mkea
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>kema
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>akem
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>aemk
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>akme
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>ekma
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>amke
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>kmea
<span class="go">zsh: command not found
</span><span class="gp">$</span><span class="w"> </span>make
<span class="c">....
</span></code></pre></div></div>

<p>How hard can it be to get the 4 letters right? Sometimes surprisingly hard. Dare I remind you of <code class="language-plaintext highlighter-rouge">gti commit</code>, or maybe <code class="language-plaintext highlighter-rouge">tig commit</code>?</p>

<p>Given that there are <code class="language-plaintext highlighter-rouge">n!</code> permutations you can have a lot of fun with longer commands.</p>

<h1 id="solution-generated-aliases">Solution: Generated Aliases</h1>
<p>Let’s simply generate an alias for each permutation of the command you often misspell. Using <a href="https://github.com/erikw/dotfiles/blob/main/bin/permute_aliases.sh">permute_aliases.sh</a> from my dotfiles we can generate the aliases like this:</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>permute_aliases.sh make
<span class="go">alias keam='make'
alias eamk='make'
alias emka='make'
alias aekm='make'
alias amek='make'
alias mkae='make'
alias kaem='make'
alias kmae='make'
alias kame='make'
alias eakm='make'
alias emak='make'
alias meka='make'
alias ekam='make'
alias mkea='make'
alias kema='make'
alias akem='make'
alias aemk='make'
alias akme='make'
alias ekma='make'
alias amke='make'
alias kmea='make'
alias meak='make'
alias maek='make'
</span></code></pre></div></div>

<p>To make use of this, simply store it in a file and  let your shell source it on startup:</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>permute_aliases.sh make <span class="o">&gt;</span> ~/.aliases_make
<span class="gp">$</span><span class="w"> </span><span class="nb">echo</span> <span class="s1">'. $HOME/.aliases_make'</span> <span class="o">&gt;&gt;</span> ~/.profile  <span class="c"># or whatever your shell loads (like `~/.bashrc` or `~/.zshrc`).</span>
</code></pre></div></div>

<p>and off you go, to maek no more mistyppings.</p>]]></content><author><name>Erik Westrup</name></author><category term="Tech" /><category term="shell" /><summary type="html"><![CDATA[Diggin’ Dotfiles Collection It’s time to dig the dotfiles, and to dig in to them! This post is a part of a blog post collection called Diggin’ Dotfiles where I unearth some gems from my personal dotfiles repo erikw/dotfiles. The following articles are a part of this series: Manage shell configurations across different systems Wrap Dangerous Commands in pre-post ZFS or BRTFS Snapshots Finger of god: using sudo with TouchID on macOS Generate permutation shell aliases Git hacks]]></summary></entry><entry><title type="html">Managing large amounts of VCS repos locally</title><link href="https://erikw.me/blog/tech/managing-large-amounts-of-vcs-repos-locally/" rel="alternate" type="text/html" title="Managing large amounts of VCS repos locally" /><published>2024-08-29T08:56:00+02:00</published><updated>2024-08-29T08:56:00+02:00</updated><id>https://erikw.me/blog/tech/managing-large-amounts-of-vcs-repos-locally</id><content type="html" xml:base="https://erikw.me/blog/tech/managing-large-amounts-of-vcs-repos-locally/"><![CDATA[<h1 id="how-to-organize-local-git-clones">How to Organize Local Git Clones</h1>
<ul>
  <li>Where to put your local git repos so that they are easy to find?</li>
  <li>How do you avoid name clashes maybe two repos have the same name, or you have a clone of the upstream repo and your fork (same name)</li>
</ul>

<p>Solution: mirror the git URL locally + shell bookmark manager</p>

<h2 id="repo-cloning">Repo Cloning</h2>
<p>By mirroring the URL locally it’s easy to know where to find a repo e.g. https://github.com/someuser/somerepo will be locally at <source-base-dir>/github.com/someuser/somerepo
This additionally directly solves the problem of name clashes as my own fork (if I had one) would have the URL https://github.com/erikw/somerepo and thus live locally at  <source-base-dir>/github.com/erikw/somerepo</source-base-dir></source-base-dir></p>

<p>I use the excellent tool <a href="https://github.com/x-motemen/ghq">ghq</a> (<code class="language-plaintext highlighter-rouge">$ brew install ghq</code>) to clone my repos as it automatically clones the repos to a base dir following the above URL pattern!</p>

<p>(my clone base is <code class="language-plaintext highlighter-rouge">~/src</code>)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ pwd
~/
$ ghq get --look git@github.com:someuser/somerepo.git
$ # make ^ a shell alias like 'clone' or something
$ pwd
~/src/github.com/someuser/somerepo
</code></pre></div></div>

<p>This is how my clone base looks</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ tree -d -L 3 ~/src
/Users/erikw/src
├── aur.archlinux.org
│   ├── restic-automatic-backup-scheduler
│   ├── snp
│   └── zscreen
├── bitbucket.org
│   └── taschik
│       ├── flight-buddy-frontend
│       └── flight-buddy-server
├── github.com
│   ├── KenKundert
│   │   ├── nestedtext
│   ├── Rapptz
│   │   └── discord.py
│   ├── erikw
│   │   ├── advent-of-code-solutions
│   │   ├── dotfiles
│   │   ├── erikw.github.io
│   │   ├── escape-from-dev-null
│   │   ├── ewxb-gcc-cross-compiler-builder
│   │   ├── exsportify
│   │   ├── hackerrank-solutions
│   │   ├── nestedtext
│   │   └── ...
</code></pre></div></div>

<h2 id="navigating-locally">Navigating Locally</h2>
<p>So yes the clone paths are now a bit longer that is true. Once you’re in your base dir for working like ~/src/github.com/erikw all good though.</p>

<p>What I can really recommend is getting a bookmark manager for your shell. Then it’s a breeze to go to the above base or specific repo</p>

<p>This is how it works in my shell</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cd ~/src/github.com/erikw/dotfiles
$ s dot  # save bookmark as dot
$ cd /somewhere/else

$ g dot  # jump to my bookmark
$ pwd
$ ~/src/github.com/erikw/dotfiles
</code></pre></div></div>

<p>I use <a href="https://github.com/urbainvaes/fzf-marks">fzf-marks</a> (s and g being shell aliases to the fzf-mark commands) currently as I’m really in to the whole fzf fuzzy search toolchain.</p>

<p>Otherwise I maintain a fork of <a href="https://github.com/erikw/cd-bookmark">cd-bookmark</a> that works for zsh and bash, also works great!</p>]]></content><author><name>Erik Westrup</name></author><category term="Tech" /><category term="git" /><category term="vcs" /><category term="shell" /><summary type="html"><![CDATA[How to Organize Local Git Clones Where to put your local git repos so that they are easy to find? How do you avoid name clashes maybe two repos have the same name, or you have a clone of the upstream repo and your fork (same name)]]></summary></entry><entry><title type="html">Taming GitHub Email Notifications</title><link href="https://erikw.me/blog/tech/general/taming_github_email_notifications/" rel="alternate" type="text/html" title="Taming GitHub Email Notifications" /><published>2024-08-06T07:42:50+02:00</published><updated>2024-08-06T07:42:50+02:00</updated><id>https://erikw.me/blog/tech/general/taming_github_email_notifications</id><content type="html" xml:base="https://erikw.me/blog/tech/general/taming_github_email_notifications/"><![CDATA[<h1 id="intro">Intro</h1>
<p>Taming GitHub email notifications</p>

<p>We’re all spammed with a lot of notifications from GitHub; how can you know which ones are important? This is what works for me:</p>

<p>GitHub notifications are classified in to different categories depending on how you related to the event. When something happens on say a PR you might get a notification because you’re the owner, or you are the reviewer, or because you left a comment in it at some point. It turns out that the emails are CC:d to an address that corresponds to the notification type.</p>

<p>For an email notification that I got because I commented on the PR/issue, the email will be CC’d to comment@noreply.github.com , if I was mentioned/tagged the CC is to mention@noreply.github.com and so on. Thus it’s super easy to set up Gmail rules on these addresses and apply different labels. With these labels you can more easy get a sense of the urgency and relevancy of the email!</p>

<p>Steps.</p>
<ol>
  <li>Create rules &amp; labels for the different event types. I set mine up based on this <a href="https://gist.github.com/ldez/bd6e6401ad0855e6c0de6da19a8c50b5#github-emails">list</a>. Here’s a more elaborate <a href="https://radek.io/2015/06/07/5-useful-gmail-filters-for-github-users/">article</a> on how to set up the rules. This site even provides <a href="https://maximevaillancourt.com/blog/github-email-notifications-gmail-filters">ready</a> rules to import directly (I’ve not reviewed or used this one but it sure sounds handy)</li>
  <li>If you use Gmail in the browser, you’re ready to go! If you have macOS, I recommend using <a href="https://mimestream.com/">Mimestream</a> (macOS feel + Gmail features) over Mail.app as Mimestream  has support for showing email labels directly in the inbox just like the web version of Gmail.</li>
</ol>

<p><picture><img src="/assets/images/github_email_filter_1.png" alt="screenshot 1" loading="lazy"></picture></p>

<p><picture><img src="/assets/images/github_email_filter_2.png" alt="screenshot 2" loading="lazy"></picture></p>

<p><picture><img src="/assets/images/github_email_filter_4.png" alt="screenshot 3" loading="lazy"></picture></p>

<p><picture><img src="/assets/images/github_email_filter_4.png" alt="screenshot 4" loading="lazy"></picture></p>]]></content><author><name>Erik Westrup</name></author><category term="Tech" /><category term="General" /><category term="info" /><category term="template" /><summary type="html"><![CDATA[Intro Taming GitHub email notifications]]></summary></entry><entry><title type="html">Taiga Stats - Your Scrum &amp;amp; Kanban Statistics Tool</title><link href="https://erikw.me/blog/tech/taiga-stats-your-scrum-kanban-statistics-tool/" rel="alternate" type="text/html" title="Taiga Stats - Your Scrum &amp;amp; Kanban Statistics Tool" /><published>2024-07-07T10:36:00+02:00</published><updated>2024-07-07T10:36:00+02:00</updated><id>https://erikw.me/blog/tech/taiga-stats-your-scrum-kanban-statistics-tool</id><content type="html" xml:base="https://erikw.me/blog/tech/taiga-stats-your-scrum-kanban-statistics-tool/"><![CDATA[<p>I will introduce my Python-modernized and brushed-up statistics tool for Scrum &amp; Kanban masters using the online Taiga.io project management board.</p>

<h1 id="taiga">Taiga</h1>
<p>First a short introduction. <a href="https://taiga.io/">Taiga</a> is an open source project management board. It features what software teams expect in terms of creating stories, managing them in a backlog and visualizing in a Kanban or Scrum board. You can use the official hosted version or run your own company server. This last part is what makes this tool very attractive for business which can’t let company secrets leave their own IT infrastructure. After all, what is in the software team’s backlogs is how a company is going to compete in the market in the next 1-2 years.</p>

<h1 id="taiga-stats">Taiga-stats</h1>
<p>Some years ago I worked for a company where a few departments picked up Taiga for their software teams. I was having the Kanban and later the Scrum master role in one of these teams. I soon found that I was making quite a lot of offline calculations and manual work to get insights in to my team’s development progress.</p>

<p>After finding that Taiga has a nice <span class="jekyll-glossary">  REST  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>Representational state transfer - as of today the most common way to build inter-service or client-facing APIs. Leveraging the HTTP infrastructure.<br /><a class="jekyll-glossary-source-link" href="https://en.wikipedia.org/wiki/Representational_state_transfer" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> <a href="https://docs.taiga.io/api.html">API</a> and that there was already a nice python library <a href="https://pypi.org/project/python-taiga/">python-taiga</a> to access it, I started to hack together a script to automate my tasks. The script grew bigger and bigger and I eventually put it on GitHub at <a href="https://github.com/erikw/taiga-stats">erikw/taiga-stats</a>.</p>

<p>Some features:</p>
<ul>
  <li>Collect statistics for producing an offline burnup or burndown chart</li>
  <li>Generating a <span class="jekyll-glossary">  CFD  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>Cumulative Flow Diagram - gives for a project a quick overview of total progress over time.<br /><a class="jekyll-glossary-source-link" href="http://brodzinski.com/2013/07/cumulative-flow-diagram.html" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span> image by collecting data points (<span class="jekyll-glossary">  cron job  <span class="jekyll-glossary-tooltip"><span class="jekyll-glossary-tooltip-hidden">(</span>A Unix tool (standard) for scheduling background jobs.<br /><a class="jekyll-glossary-source-link" href="https://en.wikipedia.org/wiki/Cron" target="_blank"></a><span class="jekyll-glossary-tooltip-hidden">)</span></span></span>).</li>
  <li>Print dependency graph between user stories.</li>
</ul>

<p>Examples of the last two features:
<img src="/assets/images/taiga-stats_cfd.png" width="512" alt="Example of a generated CFD" title="CFD generated from a team's project board." />
<br /><br />
<img src="/assets/images/taiga-stats_dependencies.png" width="512" alt="Example of a generated dependency graph" title="User Story dependency graph with green stories being done already." /></p>

<h1 id="python-modernization">Python Modernization</h1>
<h2 id="state-of-the-pyart">State of the PyArt</h2>
<p>Since I wrote this tool (script) a lot has happened in the Python world. Even since I worked as a Python developer a few years ago, the Python setup on my computer is rather different! This project used the old-school <code class="language-plaintext highlighter-rouge">requirements.txt</code> file to install dependencies, and the cumbersome <a href="https://virtualenv.pypa.io/en/latest/">virtualenv</a> was used manually to setup a shielded environment for the project.</p>

<p>I did quite a bit of research to find out what the state of the art in Python is as of today and I landed with using:</p>
<ul>
  <li><a href="https://github.com/pyenv/pyenv">pyenv</a> - to manage different Python versions on my system.</li>
  <li><a href="https://python-poetry.org/">Poetry</a> - to manage a project’s dependencies and packaging.</li>
</ul>

<p>As I’ve lately been working quite a bit with ruby (see earlier blog posts <a href="/blog/tech/open-sourcing-my-second-jekyll-plugin-gossary-tooltip/">here</a> and <a href="/blog/tech/my-first-jekyll-plugin-google-search-console-site-verification-file-generator/">here</a>) these two tools met my expectation from the fantastic ruby world. pyenv even being a fork of <a href="https://github.com/rbenv/rbenv">rbenv</a> and Poetry clearly taking a lot of inspiration from <a href="https://bundler.io/">Bundler</a>.</p>

<p>The competitor to Poetry would be <a href="https://github.com/pypa/pipenv">Pipenv</a>. However I found some technical issues and also some concerns about how the project is managed and went for Poetry which seemed healthier to me.</p>

<h2 id="taiga-stats-1x">taiga-stats 1.x</h2>
<p>I migrated my old project to Poetry and some benefits are:</p>
<ul>
  <li>Published to <a href="https://pypi.org/project/taiga-stats/">PyPi</a>! A user does not need to follow the developer setup with virtualenvs etc. A user can now simply <code class="language-plaintext highlighter-rouge">$ pip install taiga-stats</code> and go!</li>
  <li>More precis dependency specification and pinning.</li>
  <li>Modularization of the code. It was all in one source file but is now split up to <a href="https://github.com/erikw/taiga-stats/tree/master/taiga_stats">modules</a>.</li>
  <li>Code is cleaned up with nice tools like flake8, black, isort etc.</li>
  <li>Added all expected files in modern open source projects like <a href="https://github.com/erikw/taiga-stats/blob/master/CHANGELOG.md">CHANGELOG</a>, <a href="https://github.com/erikw/taiga-stats/blob/master/.editorconfig">.editorconfig</a> etc.</li>
</ul>

<p>I present to you, the fresh taiga-stats. Compare <strong><a href="https://github.com/erikw/taiga-stats/tree/40ef3e442147a4a6f3b98badd0ba1e5bc4103e9c">before</a> and <a href="https://github.com/erikw/taiga-stats">after</a></strong>.</p>

<p><a href="https://badge.fury.io/py/taiga-stats"><picture><img src="https://badge.fury.io/py/taiga-stats.svg" alt="PyPI version" loading="lazy"></picture></a>
<a href="#"><picture><img src="https://img.shields.io/pypi/pyversions/taiga-stats" alt="Supported Python versions" loading="lazy"></picture></a>
<a href="https://github.com/erikw/taiga-stats" target="_blank">
  <img align="center" src="https://github-readme-stats.vercel.app/api/pin/?username=erikw&amp;repo=taiga-stats" alt="Dynamic GitHub repo image with stats" />
</a></p>

<p>Here is the CLI interface (as of the time of writing):</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
</pre></td><td class="code"><pre><span class="gp">$</span><span class="w"> </span>pip <span class="nb">install </span>taiga-stats
<span class="gp">$</span><span class="w"> </span>taiga-stats <span class="nt">--help</span>
<span class="go">usage: taiga-stats [-h] [-v] [--url URL] [--auth-token AUTH_TOKEN]
                   {config_template,list_projects,list_us_statuses,burnup,store_daily,points_sum,cfd,deps_dot_nodes,deps_dot}
</span><span class="c">                   ...
</span><span class="go">
</span><span class="gp">Taiga statistic tool. Default values for many options can be set config file;</span><span class="w">
</span><span class="go">see the command 'config_template'.

positional arguments:
  {config_template,list_projects,list_us_statuses,burnup,store_daily,points_sum,cfd,deps_dot_nodes,deps_dot}
</span><span class="gp">                        Commands. Run $</span><span class="o">(</span>taiga-stats &lt;<span class="nb">command</span><span class="o">&gt;</span> <span class="nt">-h</span><span class="o">)</span> <span class="k">for </span>more
<span class="go">                        info about a command.
    config_template     Generate a template configuration file.
    list_projects       List all found project IDs and names on the server
                        that you have access to read.
    list_us_statuses    List all the ID and names of User Story statuses.
    burnup              Print burn(up|down) statistics. Typically used for
                        entering in an Excel sheet or such that plots a
                        burnup.
    store_daily         Store the current state of a project on file so that
                        the CFD command can generate a diagram with this data.
    points_sum          Print out the sum of points in User Story statuses.
    cfd                 Generate a Cumulative Flow Diagram from stored data.
    deps_dot_nodes      Print User Story nodes in .dot file format.
    deps_dot            Print US in .dot file format with dependencies too!
                        Create a custom attribute for User Stories named
</span><span class="gp">                        'Depends On' by going to Settings&gt;</span>Attributes&gt;Custom
<span class="go">                        Fields. Then go to a User Story and put in a comma
                        separated list of stories that this story depends on
</span><span class="gp">                        e.g. '#</span>123,#456<span class="s1">'.
</span><span class="go">
optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit
  --url URL             URL to Taiga server.
  --auth-token AUTH_TOKEN
                        Authentication token. Instructions on how to get one
                        is found at
</span><span class="gp">                        https://docs.taiga.io/api.html#</span>_authentication
<span class="go">
Support: please go to https://github.com/erikw/taiga-stats/issues</span>
</pre></td></tr></tbody></table></code></pre></figure>]]></content><author><name>Erik Westrup</name></author><category term="Tech" /><category term="agile" /><category term="python" /><summary type="html"><![CDATA[I will introduce my Python-modernized and brushed-up statistics tool for Scrum &amp; Kanban masters using the online Taiga.io project management board.]]></summary></entry></feed>