<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="https://riceball.com/wp-content/plugins/pretty-rss-feeds/xslt/pretty-feed.xsl" type="text/xsl" media="screen" ?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Obsidian - 🍙</title>
	<atom:link href="https://riceball.com/tag/obsidian/feed/" rel="self" type="application/rss+xml" />
	<link>https://riceball.com</link>
	<description></description>
	<lastBuildDate>Sun, 06 Sep 2026 18:04:47 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.4</generator>

<image>
	<url>https://riceball.com/wp-content/uploads/sites/2/2025/10/onigiri-150x150.png</url>
	<title>Obsidian - 🍙</title>
	<link>https://riceball.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Thaura Work: File System Lint Tool, python venv issues</title>
		<link>https://riceball.com/2026/06/29/thaura-work-file-system-lint/</link>
					<comments>https://riceball.com/2026/06/29/thaura-work-file-system-lint/#respond</comments>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Mon, 29 Jun 2026 04:38:23 +0000</pubDate>
				<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Obsidian]]></category>
		<category><![CDATA[Thaura]]></category>
		<guid isPermaLink="false">https://riceball.com/?p=29790</guid>

					<description><![CDATA[<p>I used Thaura Work to develop this simple tool that helps maintain file archives by enforcing file naming standards. The tool doesn&#8217;t do it directly, but helps you create simple scripts to enforce these rules. To use it, download these two files, and then install the whl file using pipx. These are pure Thaura Work [&#8230;]</p>
<p>The post <a href="https://riceball.com/2026/06/29/thaura-work-file-system-lint/">Thaura Work: File System Lint Tool, python venv issues</a> first appeared on <a href="https://riceball.com">🍙</a>.</p>]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">I used Thaura Work to develop this simple tool that helps maintain file archives by enforcing file naming standards. The tool doesn&#8217;t do it directly, but helps  you create simple scripts to enforce these rules.</p>



<p class="wp-block-paragraph">To use it, download these two files, and then install the whl file using <a href="https://github.com/pypa/pipx">pipx</a>.</p>



<div class="wp-block-file"><a id="wp-block-file--media-c3616bf6-6354-446f-a443-aa1afa14cd8f" href="https://riceball.com/wp-content/uploads/sites/2/2026/06/fslint-0.0.0-py3-none-any.whl">fslint-0.0.0-py3-none-any.whl</a><a href="https://riceball.com/wp-content/uploads/sites/2/2026/06/fslint-0.0.0-py3-none-any.whl" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-c3616bf6-6354-446f-a443-aa1afa14cd8f">Download</a></div>



<div class="wp-block-file"><a id="wp-block-file--media-4708dcf5-8158-4ae1-b692-6ed853fe714b" href="https://riceball.com/wp-content/uploads/sites/2/2026/06/fslint-site.zip">fslint-site.zip &#8211; a small website with docs and a tutorial on how to use the command in scripts</a><a href="https://riceball.com/wp-content/uploads/sites/2/2026/06/fslint-site.zip" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-4708dcf5-8158-4ae1-b692-6ed853fe714b">Download</a></div>



<p class="wp-block-paragraph">These are pure Thaura Work code. I haven&#8217;t read it over.</p>



<p class="wp-block-paragraph">Here are commands to use on these archives:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly># to extract the documentation website
unzip fslint-site.zip

# to install pipx on debian
sudo apt install pipx

# to install the wheel file
pipx install fslint-0.0.0-py3-none-any.whl </textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">to</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">extract</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">the</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">documentation</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">website</span></span>
<span class="line"><span style="color: #D8DEE9">unzip</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fslint</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">site</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">zip</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">to</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">install</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">pipx</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">on</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">debian</span></span>
<span class="line"><span style="color: #D8DEE9">sudo</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">apt</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">install</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">pipx</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"># </span><span style="color: #D8DEE9">to</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">install</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">the</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">wheel</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span></span>
<span class="line"><span style="color: #D8DEE9">pipx</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">install</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fslint</span><span style="color: #81A1C1">-</span><span style="color: #B48EAD">0.0</span><span style="color: #ECEFF4">.</span><span style="color: #B48EAD">0</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">py3</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">none</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">any</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">whl</span><span style="color: #D8DEE9FF"> </span></span></code></pre></div>



<p class="wp-block-paragraph">These new commands will be available to you:</p>



<pre class="wp-block-preformatted">fslint<br>fix_android_live<br>fix_android_photo<br>fix_android_screenshot<br>fix_android_video<br>fix_eu_ddmmyy<br>fix_iphone<br>fix_kodak_scans<br>fix_linux_screenshot<br>fix_mobizen<br>fix_snapchat<br>fix_taiji_yymmdd<br>fix_usa_mmddyy</pre>



<p class="wp-block-paragraph">Details are in the docs. Basically, fslint reports on files that don&#8217;t match the features specified in the options. I used a naming convention of &#8220;YYYY-MM-DD description.ext&#8221;.</p>



<p class="wp-block-paragraph">The fix_* commands will fix some filenames by extracting the parts, and prefixing the filename with the date. </p>



<p class="wp-block-paragraph">Here&#8217;s an example of how to use fslint in a script:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly># Year folders must have a date prefix
fslint --isfolder --dateprefix ./Photos/

# Event folders inside any year must have a date prefix
fslint --isfolder --dateprefix ./Photos/*/

# Files inside event folders must be jpg, mp4, mov, or png
fslint --isfile --isext jpg ./Photos/*/*/
fslint --isfile --isext mp4 ./Photos/*/*/
fslint --isfile --isext mov ./Photos/*/*/
fslint --isfile --isext png ./Photos/*/*/</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88"># Year folders must have a date prefix</span></span>
<span class="line"><span style="color: #88C0D0">fslint</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--isfolder</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--dateprefix</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">./Photos/</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># Event folders inside any year must have a date prefix</span></span>
<span class="line"><span style="color: #88C0D0">fslint</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--isfolder</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--dateprefix</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">./Photos/</span><span style="color: #81A1C1">*</span><span style="color: #A3BE8C">/</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># Files inside event folders must be jpg, mp4, mov, or png</span></span>
<span class="line"><span style="color: #88C0D0">fslint</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--isfile</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--isext</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">jpg</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">./Photos/</span><span style="color: #81A1C1">*</span><span style="color: #A3BE8C">/</span><span style="color: #81A1C1">*</span><span style="color: #A3BE8C">/</span></span>
<span class="line"><span style="color: #88C0D0">fslint</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--isfile</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--isext</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">mp4</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">./Photos/</span><span style="color: #81A1C1">*</span><span style="color: #A3BE8C">/</span><span style="color: #81A1C1">*</span><span style="color: #A3BE8C">/</span></span>
<span class="line"><span style="color: #88C0D0">fslint</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--isfile</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--isext</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">mov</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">./Photos/</span><span style="color: #81A1C1">*</span><span style="color: #A3BE8C">/</span><span style="color: #81A1C1">*</span><span style="color: #A3BE8C">/</span></span>
<span class="line"><span style="color: #88C0D0">fslint</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--isfile</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">--isext</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">png</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">./Photos/</span><span style="color: #81A1C1">*</span><span style="color: #A3BE8C">/</span><span style="color: #81A1C1">*</span><span style="color: #A3BE8C">/</span></span></code></pre></div>



<p class="wp-block-paragraph">Just like that, you can enforce some discipline on your archives.</p>



<p class="wp-block-paragraph">Here&#8217;s the program help:</p>


<pre class="wp-block-code has-d-8-dee-9-ff-color has-text-color" style="font-size:14px"><span><code class="hljs language-http"><span class="hljs-attribute">usage</span>: fslint &#91;-h] &#91;--version] &#91;--isfolder] &#91;--isfile] &#91;--dateprefix] &#91;--pattern PATTERN] &#91;--isext ISEXT] directory

Check that files in a folder conform to naming conventions.

positional arguments:
  directory          directory to scan

options:
  -h, --help         show this help message and exit
  --version          show program's version number and exit
  --isfolder         apply filters only to folders
  --isfile           apply filters only to files (skip folders)
  --dateprefix       require a date prefix like 2026, 2026-04, or 2026-04-01
  --pattern PATTERN  require filenames to match a glob pattern (e.g., "IMG_*", "&#91;0-9]*")
  --isext ISEXT      only check files with the given extension (e.g., jpg, mp4, md)
</code></span></pre>


<h2 class="wp-block-heading">venv</h2>



<p class="wp-block-paragraph">I used venv and <a href="https://pypi.org/project/makeapp/">makeapp</a>. I wanted to see how well Thaura Work handled something that was made from a scaffold. The end product is above and it took a 3-5 hours.</p>



<p class="wp-block-paragraph">The only real snag I had was how Thaura Work interacts with the virtual env. I&#8217;m not sure how Thaura works, but it seems to cause problems with venv, and when it hits error messages, it tries to fix it. It messed up the venv, but eventually figured out how to run the python code.</p>



<p class="wp-block-paragraph">Later on, I repaired the files manually, while creating the whl file, but these fixes were just a big guessing game for me.</p>



<h2 class="wp-block-heading">Obsidian</h2>



<p class="wp-block-paragraph">I made the work directory into an Obsidian vault. This is for reading and writing MD files.</p>



<h2 class="wp-block-heading">Development Cycle</h2>



<p class="wp-block-paragraph">I started out writing a <strong>README</strong> file, giving an overview of how the tool worked. I told it to produce code, and then a directory of folders and files with names.</p>



<p class="wp-block-paragraph">Then, I edited the names, by hand, to produce some good data and some bad data. I had Thaura read the names, and use the tool against the data. It failed, so it fixed the problems.</p>



<p class="wp-block-paragraph">Then, I told it to create a bash script, <strong>create_testfs.sh</strong>, to build the sample data directory. </p>



<p class="wp-block-paragraph">(Create directories first, with the LLM, alter them, then make a script to create them. That saved me the headache of writing a long prompt about what I wanted the script for.)</p>



<p class="wp-block-paragraph">Then I brought in some files from my photo archives, with various names. I also downloaded a bunch of photos and other files from Google Photos.</p>



<p class="wp-block-paragraph">I told Thaura to tell me what kinds of files these were, and then had it create the fix_* scripts to rename these files. </p>



<p class="wp-block-paragraph">(I didn&#8217;t want &#8220;fixing code&#8221; inside the auditing tool, because I didn&#8217;t want to risk having any disk writes or file renaming code inside there.)</p>



<p class="wp-block-paragraph">I repeated this process several times, running the code against both good and bad file names, and finding new files, identifying where they were from, and producing a fix_* script for that.</p>



<p class="wp-block-paragraph">Sometimes, I&#8217;d tell Thaura to update the docs and create_testfs.sh script.</p>



<p class="wp-block-paragraph">Sometimes, the process got weird, with date formats like &#8220;31-Jan-2026&#8221; and &#8220;2026-Jan-31&#8221;, and &#8220;2026-report.txt&#8221; and &#8220;2026-05-report.txt&#8221;.</p>



<p class="wp-block-paragraph">The development cycle was extremely interactive, and reminiscent of programming in Basic in the 1980s, or writing shell scripts.</p>



<p class="wp-block-paragraph">Here&#8217;s a portion of create_testfs.sh:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly># a portion of the create_testfs.sh

set -euo pipefail

TARGET="${1:-testfs}"

rm -rf "$TARGET"
mkdir -p "$TARGET"

# --- Valid entries ---
mkdir -p "$TARGET/2026-01-24"           # full date
mkdir -p "$TARGET/2026-02"             # year-month
mkdir -p "$TARGET/2026-03-15 Description"  # full date with space + description
mkdir -p "$TARGET/2026-05"             # year-month
mkdir -p "$TARGET/2026-06 Selfies"     # year-month with description
mkdir -p "$TARGET/2026-07"             # year-month
mkdir -p "$TARGET/2026"                # year only
touch "$TARGET/2026-report.txt"        # year only with suffix
touch "$TARGET/2026-12-report.txt"     # year-month with suffix</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88"># a portion of the create_testfs.sh</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">set</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-euo</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">pipefail</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">TARGET</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">${1</span><span style="color: #81A1C1">:-</span><span style="color: #D8DEE9">testfs}</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">rm</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-rf</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">$TARGET</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #88C0D0">mkdir</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-p</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">$TARGET</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88"># --- Valid entries ---</span></span>
<span class="line"><span style="color: #88C0D0">mkdir</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-p</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">$TARGET</span><span style="color: #A3BE8C">/2026-01-24</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">           </span><span style="color: #616E88"># full date</span></span>
<span class="line"><span style="color: #88C0D0">mkdir</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-p</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">$TARGET</span><span style="color: #A3BE8C">/2026-02</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">             </span><span style="color: #616E88"># year-month</span></span>
<span class="line"><span style="color: #88C0D0">mkdir</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-p</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">$TARGET</span><span style="color: #A3BE8C">/2026-03-15 Description</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88"># full date with space + description</span></span>
<span class="line"><span style="color: #88C0D0">mkdir</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-p</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">$TARGET</span><span style="color: #A3BE8C">/2026-05</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">             </span><span style="color: #616E88"># year-month</span></span>
<span class="line"><span style="color: #88C0D0">mkdir</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-p</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">$TARGET</span><span style="color: #A3BE8C">/2026-06 Selfies</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">     </span><span style="color: #616E88"># year-month with description</span></span>
<span class="line"><span style="color: #88C0D0">mkdir</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-p</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">$TARGET</span><span style="color: #A3BE8C">/2026-07</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">             </span><span style="color: #616E88"># year-month</span></span>
<span class="line"><span style="color: #88C0D0">mkdir</span><span style="color: #D8DEE9FF"> </span><span style="color: #A3BE8C">-p</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">$TARGET</span><span style="color: #A3BE8C">/2026</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">                </span><span style="color: #616E88"># year only</span></span>
<span class="line"><span style="color: #88C0D0">touch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">$TARGET</span><span style="color: #A3BE8C">/2026-report.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">        </span><span style="color: #616E88"># year only with suffix</span></span>
<span class="line"><span style="color: #88C0D0">touch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9">$TARGET</span><span style="color: #A3BE8C">/2026-12-report.txt</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">     </span><span style="color: #616E88"># year-month with suffix</span></span></code></pre></div>



<h2 class="wp-block-heading">Documentation</h2>



<p class="wp-block-paragraph">Through this process, Thaura produced docs, updating the README, creating a HOWTO that&#8217;s now in the docs in the ZIP file, and even saving out a progress report.</p>



<p class="wp-block-paragraph">This process, of writing docs seems to help Thaura, but I&#8217;m not sure how. They also give me a peek into what it&#8217;s doing.</p>



<p class="wp-block-paragraph">It helps to have these docs in MD format, and in the work directory, so new chat sessions can pull in an overview of the project.</p>



<h2 class="wp-block-heading">Results?</h2>



<p class="wp-block-paragraph">I thought the experience was pretty good. </p>



<p class="wp-block-paragraph">I don&#8217;t know what the code quality is like, because I haven&#8217;t read it yet. </p>



<p class="wp-block-paragraph">I forgot so much about venv, packages, etc. The Python ecosystem is complex.</p>



<p class="wp-block-paragraph">Exporting the work to the WHL file was a pain in the ass. It makes me miss simple scripts that run anywhere, don&#8217;t require installation, etc.</p>



<p class="wp-block-paragraph">I&#8217;ll try making other types of projects.</p><p>The post <a href="https://riceball.com/2026/06/29/thaura-work-file-system-lint/">Thaura Work: File System Lint Tool, python venv issues</a> first appeared on <a href="https://riceball.com">🍙</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://riceball.com/2026/06/29/thaura-work-file-system-lint/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Thaura Work: Vibe Coding an RSS Feed Aggregator</title>
		<link>https://riceball.com/2026/06/26/thaura-work-vibe-coding-an-rss-feed-aggregator/</link>
					<comments>https://riceball.com/2026/06/26/thaura-work-vibe-coding-an-rss-feed-aggregator/#respond</comments>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Fri, 26 Jun 2026 09:29:41 +0000</pubDate>
				<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Obsidian]]></category>
		<category><![CDATA[Prompts]]></category>
		<category><![CDATA[Thaura]]></category>
		<guid isPermaLink="false">https://riceball.com/?p=29751</guid>

					<description><![CDATA[<p>My feed aggregator finds RSS URLs, and converts feeds into Markdown files with headlines and links. A CLI orchestrates the processing, which is done by commands. A simple GUI helps the user accumulate URLs. Obsidian is used to browse the resulting folder structure. This document describes some of the architecture and some design decisions, and [&#8230;]</p>
<p>The post <a href="https://riceball.com/2026/06/26/thaura-work-vibe-coding-an-rss-feed-aggregator/">Thaura Work: Vibe Coding an RSS Feed Aggregator</a> first appeared on <a href="https://riceball.com">🍙</a>.</p>]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">My feed aggregator finds RSS URLs, and converts feeds into Markdown files with headlines and links. A CLI orchestrates the processing, which is done by commands. A simple GUI helps the user accumulate URLs. Obsidian is used to browse the resulting folder structure.</p>



<p class="wp-block-paragraph"><em>This document describes some of the architecture and some design decisions, and some prompts.</em></p>



<p class="wp-block-paragraph">Thaura Work is a feature in the Thaura desktop app that allows the LLM to access your files. It&#8217;s a shell around OpenCode.</p>



<p class="wp-block-paragraph">Here are some screenshots. First, the &#8220;user&#8221; side:</p>



<figure class="wp-block-image size-large"><a href="https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-02-59.png"><img fetchpriority="high" decoding="async" width="1024" height="576" src="https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-02-59-1024x576.png" alt="" class="wp-image-29752" srcset="https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-02-59-1024x576.png 1024w, https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-02-59-300x169.png 300w, https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-02-59-768x432.png 768w, https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-02-59-1536x864.png 1536w, https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-02-59.png 1920w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p class="wp-block-paragraph">(I&#8217;m using the Regolith tiling window manager.) The leftmost pane is the web browser. The middle pane is Obsidian, displaying headlines from an RSS feed. The right pane is the graphical user interface that lets me put a URL into the system.</p>



<p class="wp-block-paragraph"><strong>The most general use case</strong> is to find a web site or page, and copypaste the URL into the GUI, which then runs the program. If there&#8217;s an RSS feed, it&#8217;s downloaded, and if there are recent stories, they&#8217;re written out as a Markdown file.</p>



<p class="wp-block-paragraph">Here is the &#8220;programmer&#8221; screen:</p>



<figure class="wp-block-image size-large"><a href="https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-11-09.png"><img decoding="async" width="1024" height="576" src="https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-11-09-1024x576.png" alt="" class="wp-image-29754" srcset="https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-11-09-1024x576.png 1024w, https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-11-09-300x169.png 300w, https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-11-09-768x432.png 768w, https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-11-09-1536x864.png 1536w, https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-11-09.png 1920w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p class="wp-block-paragraph">The left page shows the folder structure and programs &#8212; one is a double clickable icon that will launch the user interface. The middle pane shows the output from running run.py, the master command. The right pane shows Thaura Work.</p>



<p class="wp-block-paragraph">Normally, I use tabs instead of panes. (The window manager is Regolith.)</p>



<p class="wp-block-paragraph">The most common use cases here are:</p>



<ul class="wp-block-list">
<li>Running <code>run.py</code> bare, to process the inbox.</li>



<li>Running <code>run.py --refresh</code>, to process known good URLs.</li>



<li>Telling Thaura Work to write or debug code.</li>



<li>Looking for program misbehavior in the folders.</li>
</ul>



<h2 class="wp-block-heading">Design Decisions</h2>



<p class="wp-block-paragraph">Thaura Work seemed to be choking on long source code, so <strong>I decided to build the system up as Python commands</strong>. To grow the program, I can add more commands, and call them from a &#8220;master control&#8221; program called run.py.</p>



<p class="wp-block-paragraph">The program is basically a batch processor, which I&#8217;ll call a &#8220;workflow&#8221;. </p>



<p class="wp-block-paragraph">Users provide URLs in text files, stdin, and command line args.</p>



<p class="wp-block-paragraph"><strong>I decided NOT to use a database, but to use the file system like a database.</strong></p>



<p class="wp-block-paragraph">Data records are in small text files. Folders act as state buckets, and text files are moved (or read from and written) to folders.</p>



<p class="wp-block-paragraph">You have an &#8220;inbox&#8221; folder. The run.py command processes it.</p>



<p class="wp-block-paragraph">URL files get classified into &#8220;urls.bad&#8221; and &#8220;urls.good&#8221; by another command. </p>



<p class="wp-block-paragraph">The system checks RSS feeds with another command, and the ones that have no data or stale data get put into &#8220;rssfeed.bad&#8221;.</p>



<p class="wp-block-paragraph"><strong>Each stage in the process has its own command that writes data into these folders. That&#8217;s the workflow.</strong></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">The data files should look like they move through the folders organized like this:</p>



<figure class="wp-block-image size-full"><img decoding="async" width="410" height="209" src="https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-11-40-57.png" alt="" class="wp-image-29780" srcset="https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-11-40-57.png 410w, https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-11-40-57-300x153.png 300w" sizes="(max-width: 410px) 100vw, 410px" /></figure>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">Example file in rssfeed.bad:</p>



<pre class="wp-block-preformatted"># RSS URL: https://www.pcgamer.com/feeds.xml<br># Error: No recent articles found in past 24 hours<br># Moved to rssfeed.bad: 2026-06-26T02:17:59.706051</pre>



<p class="wp-block-paragraph">Example file in url.good:</p>



<p class="wp-block-paragraph">https://www.pcgamer.com https://www.pcgamer.com/feeds.xml</p>



<p class="wp-block-paragraph">There&#8217;s not really any rhyme or reason to these formats. They just evolved, so they&#8217;ll need to be forced to converge onto a single format.</p>



<p class="wp-block-paragraph">Initially, I imagined the URL files would just pass through these folders. What actually happened was, the data was transformed, so files were read and written into these folders.</p>



<p class="wp-block-paragraph"><strong>Filenames are standardized</strong> and look like this: c9a9d5ad_www_kotaku_com.txt</p>



<p class="wp-block-paragraph">That&#8217;s a hash of the URL and the domain name. This is useful for deduplication, and because some web pages have multiple RSS feeds on it.</p>



<p class="wp-block-paragraph"><strong>Rather than create HTML output, I decided to make Markdown output</strong>, because the Obsidian editor is smoother than using a web browser. (An HTML version of the project would require creating index files, at the very least.)</p>



<p class="wp-block-paragraph">I also wanted the program to be able to produce other formats in the future, like JSON files, so emitting Markdown seemed like a good first step. </p>



<p class="wp-block-paragraph"><strong>The decision was to have files as input, and files as output, so this workflow could be part of a larger workflow.</strong></p>



<p class="wp-block-paragraph">At all times, I wanted the programs to run standalone, outside of Thaura Work. Thaura Work can run the program, and I have it do that, but I would like to just run the workflow without Thaura Work most of the time.</p>



<p class="wp-block-paragraph"><strong>The decision was to run outside of Thaura, and save energy.</strong></p>



<h2 class="wp-block-heading">Other Decisions</h2>



<p class="wp-block-paragraph">I don&#8217;t really like the program&#8217;s output, and prefer a less chatty TTY, but Thaura Work uses the extensive messages as a debugging aid. So they remain.</p>



<p class="wp-block-paragraph">I didn&#8217;t like a lot of the code it produced, at first, but have fought resisted &#8220;fixing&#8221; it. I assume the code produced is a reverse map back into the conceptual tokens deep in the model. I shouldn&#8217;t touch it too much.</p>



<p class="wp-block-paragraph">I had it create a GUI program for the most common form of input &#8211; a single URL. I didn&#8217;t want to use Terminal to submit my URLs.</p>



<p class="wp-block-paragraph">It created a great little one-line input dialog, and it shows the program output in the lower pane. That&#8217;s so snazzy!</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="810" height="1080" src="https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-02-59-edited.png" alt="" class="wp-image-29763" srcset="https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-02-59-edited.png 810w, https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-02-59-edited-225x300.png 225w, https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-02-02-59-edited-768x1024.png 768w" sizes="auto, (max-width: 810px) 100vw, 810px" /></figure>



<p class="wp-block-paragraph">I had it write a C program to create a double clickable icon that runs the GUI program.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="609" height="666" src="https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-03-07-46.png" alt="" class="wp-image-29762" srcset="https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-03-07-46.png 609w, https://riceball.com/wp-content/uploads/sites/2/2026/06/Screenshot-from-2026-06-26-03-07-46-274x300.png 274w" sizes="auto, (max-width: 609px) 100vw, 609px" /></figure>



<h2 class="wp-block-heading">Some Interesting Prompts</h2>


<pre class="wp-block-code has-d-8-dee-9-ff-color has-text-color has-875-rem-font-size"><span><code class="hljs language-javascript shcb-code-table shcb-wrap-lines"><mark class='shcb-loc'><span>run the command <span class="hljs-keyword">with</span> https:<span class="hljs-comment">//truthout.org observe the output and fix bugs</span>
</span></mark></code></span></pre>


<p class="wp-block-paragraph">It just ran the program over and over, fixing bugs. Pretty amazing.</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript shcb-code-table shcb-wrap-lines"><mark class='shcb-loc'><span>make sure that when run.py is called <span class="hljs-keyword">with</span> --refresh, that it resets the state <span class="hljs-keyword">of</span> all the good urls <span class="hljs-keyword">from</span> processed to unprocessed
</span></mark></code></span></pre>


<p class="wp-block-paragraph">This created a way to re-run the good URLs, running a step in the workflow without running all the steps. The workflow is made up of steps, each one working a bit like a workflow.</p>


<pre class="wp-block-code"><span><code class="hljs language-php shcb-code-table shcb-wrap-lines"><mark class='shcb-loc'><span>create a naming convention <span class="hljs-keyword">for</span> the files in the url <span class="hljs-keyword">and</span> rssurl folders. <span class="hljs-keyword">use</span> <span class="hljs-title">a</span> <span class="hljs-title">hash</span>(<span class="hljs-title">url</span>) <span class="hljs-title">and</span> <span class="hljs-title">domain</span> <span class="hljs-title">in</span> <span class="hljs-title">the</span> <span class="hljs-title">filename</span>. <span class="hljs-title">this</span> <span class="hljs-title">can</span> <span class="hljs-title">help</span> <span class="hljs-title">speed</span> <span class="hljs-title">up</span> <span class="hljs-title">deduping</span> <span class="hljs-title">urls</span>.
</span></mark></code></span></pre>


<p class="wp-block-paragraph">This was really nice. It understands about putting the data in the file, and essential metadata in the filename, so it forms a &#8220;key&#8221;.</p>



<p class="wp-block-paragraph">After stating this, it edited the code to use the convention all over. Unfortunately, it seems to have repeated code. If I were coding this, I&#8217;d have a library of functions to create standardized names. Oh well.</p>



<p class="wp-block-paragraph">The code is only as correct as it has to be.</p>


<pre class="wp-block-code"><span><code class="hljs shcb-code-table shcb-wrap-lines"><mark class='shcb-loc'><span>please clean up the project, removing deprecated programs
</span></mark></code></span></pre>


<p class="wp-block-paragraph">It is doing a much hated task!</p>



<p class="wp-block-paragraph">(I shouldn&#8217;t have used &#8220;please&#8221;.)</p>



<h2 class="wp-block-heading">Surprises and Thoughts</h2>



<p class="wp-block-paragraph">I had originally made the program to process a single URL at a time. Along the way, Thaura Work subverted me, and changed it, so processing the inbox loops over the whole thing.</p>



<p class="wp-block-paragraph">I did not want that optimization, because it means I can&#8217;t interrupt the workflow, quit after the current &#8220;crank of the handle&#8221;, and resume it.</p>



<p class="wp-block-paragraph">I&#8217;m assuming this optimization will be worse when this workflow becomes subsumed in a larger one.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">Building the app as commands makes it easy for Thaura to run it and debug it.</p>



<p class="wp-block-paragraph">However, to &#8220;tie it all together&#8221;, when I normally write programs like this by hand, I make libraries to standardize things like file naming, reading data files, and so forth. </p>



<p class="wp-block-paragraph">I didn&#8217;t do that with Thaura, so it produced a lot of repetitive code, and broken code. However, it eventually fixed the bugs. It just never did it with little abstractions.</p>



<p class="wp-block-paragraph">I should have used JSON or INI or whatever Thaura produced as the data format, and made a small library to create the filenames, load data records, write data records, enforce fields, etc. Then, I could see if Thaura could learn it and use it, and maybe save some tokens.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">I&#8217;m not sure how much time I saved by coding this with an LLM. A lot of the conversations were exploratory.</p><p>The post <a href="https://riceball.com/2026/06/26/thaura-work-vibe-coding-an-rss-feed-aggregator/">Thaura Work: Vibe Coding an RSS Feed Aggregator</a> first appeared on <a href="https://riceball.com">🍙</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://riceball.com/2026/06/26/thaura-work-vibe-coding-an-rss-feed-aggregator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
