<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kevin Scott</title>
    <description>The latest articles on DEV Community by Kevin Scott (@thekevinscott).</description>
    <link>https://dev.to/thekevinscott</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F585%2F151596.png</url>
      <title>DEV Community: Kevin Scott</title>
      <link>https://dev.to/thekevinscott</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thekevinscott"/>
    <language>en</language>
    <item>
      <title>Use your filesystem as your database</title>
      <dc:creator>Kevin Scott</dc:creator>
      <pubDate>Fri, 09 Oct 2026 13:57:23 +0000</pubDate>
      <link>https://dev.to/thekevinscott/use-your-filesystem-as-your-database-3c0i</link>
      <guid>https://dev.to/thekevinscott/use-your-filesystem-as-your-database-3c0i</guid>
      <description>&lt;p&gt;If you’re like me, you generate a lot of markdown files with your agents. Maybe they’re things like research documents, or a daily docket. They might be communication devices, like a plan committed to disk, or a journal you share. Maybe they’re memory files your agent reads at the start of every session, or handoff notes from one session to the next.&lt;/p&gt;

&lt;p&gt;Markdown files are the core of my daily workflow, the raw material of everything I work on. They’re portable, legible, and easy to back up. There’s a lot to recommend them.&lt;/p&gt;

&lt;p&gt;Meanwhile, I build a ton of apps just for myself, and most of them need a database. You can’t open a database and read it like a text file. My agents have to poke and prod at the database before they can read or write anything, and so do I (or ask my agent to do it for me).&lt;/p&gt;

&lt;p&gt;And since the data usually starts as markdown, every app needs a script to get data into the app and keep it in sync. If I delete a row in the app, which copy is right? Then I need &lt;em&gt;another&lt;/em&gt; script to export the data for the inevitable day I stop using the vibe-coded app (but still want to keep my data).&lt;/p&gt;

&lt;p&gt;I built a tool, &lt;code&gt;dirsql&lt;/code&gt;, that lets you query your files with SQL. It’s like React for data, a declarative way of deriving a database from your files as the source of truth. That means that if you add or delete a file on disk, the application’s rows update automatically. And if you stop using the app there’s nothing to export, because your data are your files. You and your agents collaborate on plain text files, but you still build the kinds of dynamic software we know and love without the backend machinery.&lt;/p&gt;

&lt;p&gt;I’ve been using it in a lot of my personal software and I think it might be helpful for yours too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Can You Do When Your Filesystem is a Database?
&lt;/h2&gt;

&lt;p&gt;I think the best way to demonstrate why it’s useful is showing how I use &lt;code&gt;dirsql&lt;/code&gt; in my own software. There’s also &lt;a href="https://dirsql.dev" rel="noopener noreferrer"&gt;plenty of docs if you’d prefer a more technical introduction&lt;/a&gt;. [1]&lt;/p&gt;

&lt;p&gt;Let’s get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Can &lt;code&gt;SELECT * FROM './'&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;SQL via &lt;code&gt;dirsql&lt;/code&gt; lets you find files on your filesystem.&lt;/p&gt;

&lt;p&gt;Below I’ll provide both the &lt;code&gt;dirsql&lt;/code&gt; command and the native equivalent. Native tools are easier to read for basic commands. The more complicated you get, the more SQL shows its worth.&lt;/p&gt;

&lt;p&gt;Start with a basic list:&lt;/p&gt;

&lt;p&gt;dirsql&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"SELECT * FROM './'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;native&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;path basename dir ext size mtime ctime
--------------- --------------- --- ---- ----- ---------- ----------
AGENTS.md AGENTS.md md 3941 1791240287 1791281924
ARCHITECTURE.md ARCHITECTURE.md md 23080 1791199872 1791281924
CHANGELOG.md CHANGELOG.md md 67821 1784394668 1791281924
Cargo.lock Cargo.lock lock 91276 1791224560 1791281924
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also can easily recurse with &lt;code&gt;./**&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;dirsql&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"
SELECT * FROM './**'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;native&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-not&lt;/span&gt; &lt;span class="nt"&gt;-path&lt;/span&gt; &lt;span class="s1"&gt;'*/.*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;path basename dir ext size mtime ctime
----------------------------- --------------- ------------------- --- ----- ---------- ----------
AGENTS.md AGENTS.md md 3941 1791240287 1791281924
ARCHITECTURE.md ARCHITECTURE.md md 23080 1791199872 1791281924
…
agents/environments/local.md local.md agents/environments md 7450 1789758352 1791281924
agents/environments/remote.md remote.md agents/environments md 5037 1791224560 1791281924
…
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Re-order, limit, and filter in a single SQL command:&lt;/p&gt;

&lt;p&gt;dirsql&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"
SELECT path FROM './**'
WHERE ext = 'md'
ORDER BY mtime DESC
LIMIT 5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;native&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'*.md'&lt;/span&gt; &lt;span class="nt"&gt;-not&lt;/span&gt; &lt;span class="nt"&gt;-path&lt;/span&gt; &lt;span class="s1"&gt;'*/.*'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;path
-----------------------------------
PARITY.md
README.md
agents/reference/pr-monitor.md
agents/reference/session-handoff.md
docs/reference/cli.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pick columns and compute new ones:&lt;/p&gt;

&lt;p&gt;dirsql&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"
SELECT path, size / 1024 AS kb,
date(mtime, 'unixepoch') AS day
FROM './**'
WHERE ext = 'md'
ORDER BY size DESC
LIMIT 5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;native&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'*.md'&lt;/span&gt; &lt;span class="nt"&gt;-not&lt;/span&gt; &lt;span class="nt"&gt;-path&lt;/span&gt; &lt;span class="s1"&gt;'*/.*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-printf&lt;/span&gt; &lt;span class="s1"&gt;'%s %TF %P\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $3, int($1 / 1024), $2 }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;path kb day
--------------------------------------- -- ----------
MIGRATIONS.md 74 2026-09-02
CHANGELOG.md 66 2026-07-18
PARITY.md 49 2026-10-06
notes/recall-skill-review-2026-09-25.md 35 2026-09-28
agents/reference/testing-gates.md 32 2026-10-05
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Group and count:&lt;/p&gt;

&lt;p&gt;dirsql&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"
SELECT ext,
count(*) AS files,
sum(size) / 1024 AS kb
FROM './**'
GROUP BY ext
ORDER BY files DESC
LIMIT 5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;native&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-not&lt;/span&gt; &lt;span class="nt"&gt;-path&lt;/span&gt; &lt;span class="s1"&gt;'*/.*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-printf&lt;/span&gt; &lt;span class="s1"&gt;'%s %f\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{
n = split($2, part, ".")
ext = n &amp;gt; 1 ? part[n] : "NULL"
files[ext]++
kb[ext] += $1
}
END {
for (e in files)
print e, files[e], int(kb[e] / 1024)
}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-k2rn&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ext files kb
---- ----- ----
md 367 907
py 340 669
json 188 40
rs 104 1539
ts 77 259
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you can stitch several queries into one list:&lt;/p&gt;

&lt;p&gt;dirsql&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"
SELECT 'files' AS stat, count(*) AS value
FROM './**'
UNION ALL
SELECT 'markdown', count(*)
FROM './**' WHERE ext = 'md'
UNION ALL
SELECT 'kb', sum(size) / 1024
FROM './**'
UNION ALL
SELECT 'newest', date(max(mtime), 'unixepoch')
FROM './**'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;native&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-not&lt;/span&gt; &lt;span class="nt"&gt;-path&lt;/span&gt; &lt;span class="s1"&gt;'*/.*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\t%T@\t%P\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'\t'&lt;/span&gt; &lt;span class="s1"&gt;'{
files++; kb += $1
if ($3 ~ /\.md$/) md++
if ($2 &amp;gt; newest) newest = $2
}
END {
print "files", files
print "markdown", md
print "kb", int(kb / 1024)
print "newest", strftime("%Y-%m-%d", newest, 1)
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stat value
-------- ----------
files 1106
markdown 367
kb 3728
newest 2026-10-06
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where SQL really shines is combining rules or filtering on columns, which makes agents’ behavior more readable and precise (with fewer tokens, too!)&lt;/p&gt;

&lt;p&gt;A specific example: I run a daily “dossier generation” script. SQL gives me exact control over the list of files that gets passed to my agent. Otherwise the agent goes poking around on its own with a dozen non-deterministic tool calls, which costs tokens and time[2]:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"
SELECT 1 AS rank, 'standing' AS kind, path, date(mtime, 'unixepoch') AS modified
FROM './planner/background/*.md'
UNION ALL
SELECT 2, 'daily', path, date(mtime, 'unixepoch')
FROM './journal/*.md'
WHERE mtime &amp;gt; unixepoch('now', '-3 days')
UNION ALL
SELECT 3, 'recent', path, date(mtime, 'unixepoch')
FROM './**'
WHERE ext = 'md'
AND mtime &amp;gt; unixepoch('now', '-7 days')
AND path NOT LIKE 'journal/%'
AND path NOT LIKE 'planner/background/%'
ORDER BY rank, modified DESC"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rank kind path modified
---- -------- ----------------------------------------- ----------
1 standing planner/background/projects.md 2026-09-10
1 standing planner/background/profile.md 2026-08-13
2 daily journal/2026-10-01.md 2026-10-01
2 daily journal/2026-09-30.md 2026-09-30
2 daily journal/2026-09-29.md 2026-09-30
3 recent planner/dockets/2026-09-30.md 2026-09-30
3 recent projects/dirsql/notes/launch-checklist.md 2026-09-29
3 recent projects/blog/drafts/dirsql-outline.md 2026-09-28
3 recent reading/sqlite-virtual-tables.md 2026-09-26
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Your Text Files Can Be Your Database
&lt;/h3&gt;

&lt;p&gt;I particularly like this use case.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dirsql&lt;/code&gt; can turn your file content into a database.&lt;/p&gt;

&lt;p&gt;Let’s say you keep a journal, or a lab notebook, or some sort of file with entries divided by dates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Journal of my time in the Antarctic&lt;/span&gt;
&lt;span class="gu"&gt;## 2026-09-22&lt;/span&gt;
Dang, saw some really cool Penguins today
&lt;span class="gu"&gt;## 2026-09-25&lt;/span&gt;
More penguins! How many penguins can one man see?
&lt;span class="gu"&gt;## 2026-09-26&lt;/span&gt;
Saw a walrus today.
&lt;span class="gu"&gt;## 2026-09-27&lt;/span&gt;
An albatross circled the ship for an hour.
&lt;span class="gu"&gt;## 2026-09-28&lt;/span&gt;
Ran out of coffee at the station. Morale is low.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It sure would be nice to see my latest entries first! Let’s do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"SELECT date, content FROM './journal.md' ORDER BY date DESC"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--on-file&lt;/span&gt; &lt;span class="s1"&gt;'python3 parse.py'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s a new flag here, &lt;code&gt;--on-file&lt;/code&gt;. It specifies a command that turns file contents into rows. In Python &lt;code&gt;parse.py&lt;/code&gt; might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]:&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;## &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]:&lt;/span&gt;
&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it doesn’t have to be Python, you can use any language for parsing that your system supports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"SELECT date, content FROM './journal.md'
ORDER BY date DESC"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--on-file&lt;/span&gt; &lt;span class="s2"&gt;"jq -Rsc '
[splits(&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s2"&gt;## &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;)][1:]
| map(split(&lt;/span&gt;&lt;span class="se"&gt;\"\n\n\"&lt;/span&gt;&lt;span class="s2"&gt;))
| map({date: .[0], content: (.[1] | rtrimstr(&lt;/span&gt;&lt;span class="se"&gt;\"\n\"&lt;/span&gt;&lt;span class="s2"&gt;))})
| .[]
'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which produces:&lt;/p&gt;

&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;date content
---------- -------------------------------------------------
2026-09-28 Ran out of coffee at the station. Morale is low.
2026-09-27 An albatross circled the ship for an hour.
2026-09-26 Saw a walrus today.
2026-09-25 More penguins! How many penguins can one man see?
2026-09-22 Dang, saw some really cool Penguins today
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And of course, we can use native SQL. This notebook sure talks a lot about penguins; what entries &lt;em&gt;don’t&lt;/em&gt; mention penguins?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"SELECT date, content FROM './journal.md'
WHERE content NOT LIKE '%penguin%'"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--on-file&lt;/span&gt; &lt;span class="s1"&gt;'python3 parse.py'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;date content
---------- -------------------------------------------------
2026-09-26 Saw a walrus today.
2026-09-27 An albatross circled the ship for an hour.
2026-09-28 Ran out of coffee at the station. Morale is low.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  You Can Search By Meaning
&lt;/h3&gt;

&lt;p&gt;Because we’re using SQL (specifically, SQLite), we have access to the entire SQLite ecosystem. A cool thing we can do is use vector embeddings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.pinecone.io/learn/vector-embeddings/" rel="noopener noreferrer"&gt;Vector embeddings&lt;/a&gt; are a way to represent text by &lt;em&gt;meaning&lt;/em&gt;. Instead of searching for something by keyword (give me all entries talking about &lt;code&gt;penguins&lt;/code&gt;), you can search by intent (give me all entries about &lt;code&gt;polar birds&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flqzc87m5nza7yn2m2mcq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flqzc87m5nza7yn2m2mcq.png" alt="Penguin, puffin and duck cluster together, walrus and seal in another cluster, SQL and database in a third. The query “polar birds” lands among the birds, nearest to penguin, emperor penguin and puffin, despite sharing no words with them."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To support vector embeddings, we need to include a plugin (by the way, &lt;a href="https://dirsql.dev/plugins.html" rel="noopener noreferrer"&gt;&lt;code&gt;dirsql&lt;/code&gt; supports plugins&lt;/a&gt; [3]).&lt;/p&gt;

&lt;p&gt;Let’s reuse our journal from above. What entries discuss &lt;code&gt;polar birds&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx &lt;span class="nt"&gt;--with&lt;/span&gt; dirsql-plugin-embeddings dirsql &lt;span class="s2"&gt;"
SELECT
round(vec_distance_cosine(
embed(content),
embed('polar birds')
), 3) AS distance,
date,
content
FROM './journal.md'
ORDER BY distance"&lt;/span&gt; &lt;span class="nt"&gt;--on-file&lt;/span&gt; &lt;span class="s1"&gt;'python3 parse.py'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;distance date content
-------- ---------- -------------------------------------------------
0.637 2026-09-25 More penguins! How many penguins can one man see?
0.766 2026-09-22 Dang, saw some really cool Penguins today
0.789 2026-09-27 An albatross circled the ship for an hour.
0.821 2026-09-26 Saw a walrus today.
1.031 2026-09-28 Ran out of coffee at the station. Morale is low.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a contrived example so let’s use something more concrete.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://arxiv.org" rel="noopener noreferrer"&gt;arXiv&lt;/a&gt; hosts lots of academic papers, and I have a nightly script that pulls all “machine learning” paper abstracts to disk. Using &lt;code&gt;dirsql&lt;/code&gt; I can search all papers by meaning, which makes it much easier to find the relevant and interesting papers.&lt;/p&gt;

&lt;p&gt;For example, we can search for how to turn a selfie into a 3D face:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx &lt;span class="nt"&gt;--with&lt;/span&gt; dirsql-plugin-embeddings dirsql &lt;span class="s2"&gt;"
SELECT
round(vec_distance_cosine(
embed(content -&amp;gt;&amp;gt; 'abstract'),
embed('turning a selfie into a 3D face')
), 3) AS distance,
substr(content -&amp;gt;&amp;gt; 'title', 1, 58) AS title
FROM './papers/*/metadata.json'
ORDER BY distance
LIMIT 5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;distance title
-------- ----------------------------------------------------------
0.358 FaceGPT: Self-supervised Learning to Chat about 3D Human F
0.413 SARS: A Novel Face and Body Shape and Appearance Aware 3D
0.416 3DRealHead: Few-Shot Detailed Head Avatar
0.436 Generative Landmarks Guided Eyeglasses Removal 3D Face Rec
0.439 Generative Face Parsing Map Guided 3D Face Reconstruction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;None of the results include &lt;code&gt;selfie&lt;/code&gt;. Searching by keyword would have skipped them all.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Can Query Your Claude Code Transcripts
&lt;/h3&gt;

&lt;p&gt;Something I use every day is my own personal transcript database that I get largely for free without an ingest pipeline.&lt;/p&gt;

&lt;p&gt;Claude Code stores all its transcripts in a local directory, &lt;code&gt;~/.claude&lt;/code&gt;. Let’s select from that and see what it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"
SELECT * FROM '~/.claude/projects/*/*.jsonl'
ORDER BY mtime DESC
LIMIT 5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;path basename dir ext size mtime ctime
------------------------------------------------------------- ------------------------------------------ ---------------------------------------------------- ----- ------- ---------- ----------
/home/you/.claude/projects/-home-you-code-recipe-app/3f9c2a7… 3f9c2a71-8e4b-4d2a-9c61-7b5e0d2f8a13.jsonl /home/you/.claude/projects/-home-you-code-recipe-app jsonl 569836 1790958581 1790958384
/home/you/.claude/projects/-home-you-code-recipe-app/a81d4e0… a81d4e09-2c7f-4b3e-8f15-6e9a3c7b2d40.jsonl /home/you/.claude/projects/-home-you-code-recipe-app jsonl 5840861 1790958557 1790937682
/home/you/.claude/projects/-home-you-notes/c2e7b5f3-91a4-4f8… c2e7b5f3-91a4-4f8d-b026-3d8e1f6a9c57.jsonl /home/you/.claude/projects/-home-you-notes jsonl 745711 1790958488 1790898538
/home/you/.claude/projects/-home-you-code-budget-cli/7b40f8d… 7b40f8d2-5e3c-4a91-8d7e-0f2c6b9e4a18.jsonl /home/you/.claude/projects/-home-you-code-budget-cli jsonl 3368883 1790958469 1790865520
/home/you/.claude/projects/-home-you-notes/e95a1c3d-4f07-4b6… e95a1c3d-4f07-4b6e-a2d9-8c1f5e7b3a62.jsonl /home/you/.claude/projects/-home-you-notes jsonl 716330 1790958384 1790953793
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude stores those per project, with session IDs. It also stores memories and subagent transcripts in nested folders. Let’s adjust our query to show that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"
SELECT
CASE
WHEN path LIKE '%/memory/%' THEN 'memory'
WHEN path LIKE '%/subagents/%' THEN 'subagent transcript'
ELSE 'session transcript'
END AS kind,
count(*) AS files
FROM '~/.claude/projects/**'
WHERE ext = 'jsonl' OR path LIKE '%/memory/%'
GROUP BY kind
ORDER BY files DESC"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kind files
------------------- -----
subagent transcript 1342
memory 490
session transcript 405
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;jsonl&lt;/code&gt; files store each turn of a conversation as a line of JSON. Let’s add a parser to break those apart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"
SELECT
cwd AS project,
sessionId AS session,
type AS role,
CASE json_type(message, '&lt;/span&gt;&lt;span class="nv"&gt;$.&lt;/span&gt;&lt;span class="s2"&gt;content')
WHEN 'text' THEN message -&amp;gt;&amp;gt; '&lt;/span&gt;&lt;span class="nv"&gt;$.&lt;/span&gt;&lt;span class="s2"&gt;content'
ELSE message -&amp;gt;&amp;gt; '&lt;/span&gt;&lt;span class="nv"&gt;$.&lt;/span&gt;&lt;span class="s2"&gt;content[0].text'
END AS text,
timestamp
FROM '~/.claude/projects/*/*.jsonl'
WHERE text IS NOT NULL
ORDER BY timestamp DESC
LIMIT 5"&lt;/span&gt; &lt;span class="nt"&gt;--on-file&lt;/span&gt; &lt;span class="s1"&gt;'cat'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project session role text timestamp
------------------------- ------------------------------------ --------- --------------------------------------------------------- ------------------------
/home/you/code/recipe-app 3f9c2a71-8e4b-4d2a-9c61-7b5e0d2f8a13 assistant Let me look at the save handler. 2026-10-02T16:29:36.214Z
/home/you/code/recipe-app 3f9c2a71-8e4b-4d2a-9c61-7b5e0d2f8a13 user Why does the ingredient list reorder itself on save? 2026-10-02T16:29:30.871Z
/home/you/code/recipe-app 3f9c2a71-8e4b-4d2a-9c61-7b5e0d2f8a13 assistant Done: the recipe page has an Export shopping list button. 2026-10-02T16:28:40.502Z
/home/you/code/recipe-app 3f9c2a71-8e4b-4d2a-9c61-7b5e0d2f8a13 assistant I'll add an export button below the ingredient list. 2026-10-02T16:27:20.093Z
/home/you/code/recipe-app 3f9c2a71-8e4b-4d2a-9c61-7b5e0d2f8a13 user Add a shopping-list export to the recipe page 2026-10-02T16:27:12.647Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ideally we’d have a normalized layout, with &lt;code&gt;messages&lt;/code&gt; and &lt;code&gt;sessions&lt;/code&gt; tables. We can do that! We need a config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[table]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sessions"&lt;/span&gt;
&lt;span class="py"&gt;glob&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"*/*.jsonl"&lt;/span&gt;
&lt;span class="py"&gt;ddl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CREATE TABLE sessions (session TEXT, project TEXT)"&lt;/span&gt;
&lt;span class="py"&gt;on-file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'''
jq -c -s '
map(select(.sessionId))
| group_by(.sessionId)
| map({
session: .[0].sessionId,
project: (map(.cwd | values) | first)
})
| .[]
'
'''&lt;/span&gt;
&lt;span class="nn"&gt;[[table]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"messages"&lt;/span&gt;
&lt;span class="py"&gt;glob&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"*/*.jsonl"&lt;/span&gt;
&lt;span class="py"&gt;ddl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CREATE TABLE messages (session TEXT, role TEXT, text TEXT, timestamp TEXT)"&lt;/span&gt;
&lt;span class="py"&gt;on-file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'''
jq -c '
{
session: .sessionId,
role: .type,
text: (.message.content | if type == "string" then . else .[0]?.text end),
timestamp
}
| select(.text)
'
'''&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now our query becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.claude/projects
uvx dirsql &lt;span class="s2"&gt;"
SELECT s.project, m.session, m.role, m.text, m.timestamp
FROM messages m
JOIN sessions s ON s.session = m.session
ORDER BY m.timestamp DESC
LIMIT 5"&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; ./.dirsql.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project session role text timestamp
------------------------- ------------------------------------ --------- --------------------------------------------------------- ------------------------
/home/you/code/recipe-app 3f9c2a71-8e4b-4d2a-9c61-7b5e0d2f8a13 assistant Let me look at the save handler. 2026-10-02T16:29:36.214Z
/home/you/code/recipe-app 3f9c2a71-8e4b-4d2a-9c61-7b5e0d2f8a13 user Why does the ingredient list reorder itself on save? 2026-10-02T16:29:30.871Z
/home/you/code/recipe-app 3f9c2a71-8e4b-4d2a-9c61-7b5e0d2f8a13 assistant Done: the recipe page has an Export shopping list button. 2026-10-02T16:28:40.502Z
/home/you/code/recipe-app 3f9c2a71-8e4b-4d2a-9c61-7b5e0d2f8a13 assistant I'll add an export button below the ingredient list. 2026-10-02T16:27:20.093Z
/home/you/code/recipe-app 3f9c2a71-8e4b-4d2a-9c61-7b5e0d2f8a13 user Add a shopping-list export to the recipe page 2026-10-02T16:27:12.647Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also run &lt;code&gt;dirsql&lt;/code&gt; as a server with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.claude/projects
uvx dirsql server &lt;span class="nt"&gt;-c&lt;/span&gt; ./.dirsql.toml
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running at localhost:7117
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://dirsql.dev/reference/http-api.html" rel="noopener noreferrer"&gt;And then you can run POST requests against it&lt;/a&gt;. You can tell your agent to build you a frontend on top of this that lets you select, reorder, search semantically; whatever you want. &lt;code&gt;dirsql&lt;/code&gt; doesn’t give you the frontend [4], but it gives you the backend for free!&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Files Remain The Same
&lt;/h2&gt;

&lt;p&gt;Ultimately I’m allergic to lock-in.&lt;/p&gt;

&lt;p&gt;And that’s really been the trend of my life these last few years, trying to avoid lock-in of my data whenever I can. I moved from Gmail to my own email domain and from Evernote to Obsidian. I don’t like my data stuck in a walled garden and that applies as much to my own software as to somebody else’s.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;dirsql&lt;/code&gt;, my files stay mine. My journal remains a markdown file, the arXiv papers JSON on disk, Claude transcripts JSONL in my &lt;code&gt;~/.claude&lt;/code&gt; folder, all while the agents keep working, blissfully unaware there’s a read-only layer on top exposing them via SQL. And when I inevitably decide to throw away my random weekend vibe-coded app, it doesn’t affect my data because there’s nothing to export.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dirsql.dev" rel="noopener noreferrer"&gt;Check out &lt;code&gt;dirsql&lt;/code&gt; in the docs&lt;/a&gt;, or just try running it in a directory with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvx dirsql &lt;span class="s2"&gt;"SELECT * FROM './'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;-y&lt;/span&gt; dirsql &lt;span class="s2"&gt;"SELECT * FROM './'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use it and it’s helpful to you (or if it’s not!), please let me know.&lt;/p&gt;

&lt;p&gt;Happy SQL-ing!&lt;/p&gt;




&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;dirsql&lt;/code&gt; is available on PyPI, npm, and Cargo, and can be used without installing via &lt;code&gt;uvx&lt;/code&gt; or &lt;code&gt;npx&lt;/code&gt;; &lt;a href="https://dirsql.dev/getting-started.html" rel="noopener noreferrer"&gt;Getting Started instructions here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;When I presented &lt;code&gt;dirsql&lt;/code&gt; at a &lt;a href="https://nyc.aitinkerers.org/talks/rsvp_noBJeP55h0M" rel="noopener noreferrer"&gt;recent AI Tinkerers event&lt;/a&gt;, somebody asked whether giving &lt;code&gt;dirsql&lt;/code&gt; to an agent changes its behavior. I don’t have a good answer yet. Agents rarely reach for it on their own. I have to prompt them. But once it’s invoked via skill, they use it well.&lt;/li&gt;
&lt;li&gt;This plugin pulls in a vector embedding model automatically. You can use a different model too.&lt;/li&gt;
&lt;li&gt;Maybe it should? &lt;a href="https://github.com/thekevinscott/dirsql/issues/1229" rel="noopener noreferrer"&gt;Actually I like this idea so I filed it as an issue&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://thekevinscott.com/use-your-filesystem-as-database-with-dirsql/" rel="noopener noreferrer"&gt;thekevinscott.com&lt;/a&gt;. Subscribe by &lt;a href="https://thekevinscott.substack.com" rel="noopener noreferrer"&gt;email&lt;/a&gt; or &lt;a href="https://thekevinscott.com/rss.xml" rel="noopener noreferrer"&gt;RSS&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>markdown</category>
      <category>ai</category>
      <category>database</category>
    </item>
    <item>
      <title>Notes from the Tech for GoodÂ Meetup</title>
      <dc:creator>Kevin Scott</dc:creator>
      <pubDate>Fri, 11 Nov 2016 17:52:51 +0000</pubDate>
      <link>https://dev.to/thekevinscott/notes-from-the-tech-for-goodmeetup</link>
      <guid>https://dev.to/thekevinscott/notes-from-the-tech-for-goodmeetup</guid>
      <description>&lt;h1&gt;
  
  
  Notes from the Tech for Good Meetup
&lt;/h1&gt;

&lt;p&gt;Last night I went to a &lt;a href="https://www.meetup.com/Tech-For-Good-NYC/events/235472916/"&gt;meetup in New York for progressive technologists&lt;/a&gt; to discuss responses to the recent election. People shared thoughts, feelings, and ideas for ways to move forward, and I'd like to share them. In the conclusion I'll try to summarize what I heard.&lt;/p&gt;

&lt;h4&gt;
  
  
  These are my notes written as fast as I could jot them, so not all quotes are accurate, and I'm keeping everyone anonymous.
&lt;/h4&gt;

&lt;p&gt;One person talked about being frustrated and being angry at the electorate that voted on Tuesday, and content staying angry. She said “Anger can be beautiful, and powerful. It doesn't let you stand still.”&lt;/p&gt;

&lt;p&gt;Another person talked about how he was shocked. “Surreal. Like it's not really happening. You knew the world was broken but you didn't know how broken it is.”&lt;/p&gt;

&lt;p&gt;Another: “We don't understand the country as well as we think we do. I want to question my bias.”&lt;/p&gt;

&lt;p&gt;Another: “I don't understand half this country. All of us don't.” He asked those in the room how we'd feel if one day we came to work to find the entire staff laid off and replaced with UX designers overseas for half the price. How would we feel? In that scenario, maybe you'd willing to overlook the sins of your candidate if it meant addressing that issue. “Don't fight your fellow citizens; find a cause, and fight for that.”&lt;/p&gt;

&lt;p&gt;A woman said that “we as a group [the tech community] have so much more power than we realize. We can influence minds more than we realize. This is not over.” She discussed how it had been easy to become complacent with the idea that the last 8 years were forward progress.&lt;/p&gt;

&lt;p&gt;Another man brought up a quote: “Bringing facts to a culture war is like bringing a knife to a gunfight.” He recommended a number of steps privacy-minded folks should take moving forward:&lt;/p&gt;

&lt;p&gt;1) &lt;a href="https://www.torproject.org/"&gt;Get Tor&lt;/a&gt;&lt;br&gt;&lt;br&gt;
2) &lt;a href="https://en.wikipedia.org/wiki/Multi-factor_authentication"&gt;Enable 2 factor authentication&lt;/a&gt; everywhere it's available&lt;br&gt;&lt;br&gt;
3) Get &lt;a href="https://whispersystems.org/"&gt;Signal&lt;/a&gt;&lt;br&gt;&lt;br&gt;
4) Get a &lt;a href="https://en.wikipedia.org/wiki/Virtual_private_network"&gt;VPN&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another: “I'm just realizing that half the country voted to endorse sexism, and racism.” She was hearing lots of people encouraging folks to be open minded, understanding, and how the other side has their reasons to vote the way they did. Her perspective was that all the people espousing sexism and racism had been validated; that those views were now mainstream, and ok. And that that viewpoint is the first thing that needs to be addressed.&lt;/p&gt;

&lt;p&gt;A woman brought up the fact that, the fact that Trump is egotistical, could work in progressives' favor. That he could be baited.&lt;/p&gt;

&lt;p&gt;Someone else expressed surprise that the other speakers were surprised. She brought up Brexit. Russia, Turkey, and China. She talked about how we don't need 70,000 more apps; it was a homeowner who found lead in the Michigan water. It's information like that that needs to be collected, and shared.&lt;/p&gt;

&lt;p&gt;An immigrant spent the day after the election walking around the National History museum, thinking “How could someone want to destroy this?” He encouraged people to get out in their communities, and teach, as a way of empowering people. Help those that need it the most.&lt;/p&gt;

&lt;p&gt;A recent transplant from Silicon Valley asked a question: Why did the United States allow the election to come down to a choice between Clinton and Trump? He said people voted for Trump because he told them what they wanted to hear. That Americans did not want to hear about Syrian immigrants. That she did not address her money issues. As an Indian immigrant, he talked about how his it was a dream that coming to America would make a huge difference. “I did not come to this country to be in despair. I want to focus on hope.”&lt;/p&gt;

&lt;p&gt;A first generation American, son of a single mother talked about feeling defeated for the last 48 hours. Not knowing how to process his emotions. Feeling scared of the inevitable racism to come.&lt;/p&gt;

&lt;p&gt;A man was thrilled to be discussing activism, as a coping mechanism. About scouring for ideas, and figuring out how to leave the bubble of NYC. “What can we do to make sure technology doesn't give us blinders? And not turn people into cartoons?”&lt;/p&gt;

&lt;p&gt;A woman talked about how she felt guilty for not doing more. And feeling guilty about thinking technology can change much.&lt;/p&gt;

&lt;p&gt;A man was deeply worried about Trump, domestically and abroad. He wanted to build a stronger sense of personal connection, and think about ways of encouraging compassion, and solidarity.&lt;/p&gt;

&lt;p&gt;Someone suggested we go to where the other side is, places like Breitbart, and engage, and start commenting. And speak up even if others don't share your political views.&lt;/p&gt;

&lt;p&gt;A man implored everyone to “shake the surprise off. This is how the world is.” He talked about how he gave out the number for the suicide hotline to folks undergoing extreme stress and anxiety. “This is where our country is. This is where it's been. A person basically put up an effigy of things we believe and won.” He said that, just as there's a diversity gap in tech for women and minorities, there's also a political diversity gap, “created by our insistence to publicly shame and excommunicate those of opposing positions, which then instilled a fear in those who would prefer to simply not speak up in order to maintain relationships.” And that prevented folks in the community from understanding how the other side voted. And that “they're afraid to talk to us too.”&lt;/p&gt;

&lt;p&gt;A man talked about how folks in the community have tons of ideas. Tons of energy, engagement in open source. “I recognize a lot of opportunity to do what we do best. And be there for each other, and for the rest of the world.”&lt;/p&gt;

&lt;p&gt;A woman talked about being a technocrat and feeling a dull rage. She talked about her Southern family seeing her as living in a fantasy land. A vote for Trump was like throwing a brick through her window. “Are you looking now?” she asked rhetorically. She talked about how tech can't solve everything, about her desire to get out of the bubble, but not knowing how. She implored people to meet in person, not on the internet.&lt;/p&gt;

&lt;p&gt;A woman talked about how she's “not in a hopeful place.” About how data failed. She said “White people failed us. I only focused my outreach on the black and LGBT communities. I didn't try to change white people. As programmers, we understand where the bugs (aka, the problems) lie.”&lt;/p&gt;

&lt;p&gt;A man talked about how the long term is important, but short term things were important to, like addressing an oncoming uptick in bullying, white power chants, and worse. And how it's important to let victims know they're not alone, even if they live in a very conservative place.&lt;/p&gt;

&lt;p&gt;An immigrant discussed using work as therapy, and encouraged people to help as technologists within campaigns, and identify on a meta level, what approaches were most successful.&lt;/p&gt;

&lt;p&gt;A product manager encouraged the crowd to treat the electorate like you would a client (sometimes clients can be very frustrating, but necessary to deal with effectively).&lt;/p&gt;

&lt;p&gt;A man made the point that the room had been “referring to the other side as â€˜the other half'. Just want to point out that less than half of the population voted. So it's important to understand that only a quarter of the country voted for this man. And it's important to understand why the half that didn't vote sat this one out.”&lt;/p&gt;

&lt;p&gt;A woman discussed how she had been adopted, as a result of being born into a country that rejected her for being a girl. To see the rejection of the first female nominee was heartbreaking for her. She recounted how her cousin's little girl was scared to go to school now. And how it was unconscionable for children to feel like they don't belong.&lt;/p&gt;

&lt;h4&gt;
  
  
  At that point, the conversation moved to asking for action-oriented ideas to move forward.
&lt;/h4&gt;

&lt;p&gt;A CS student talked about the US digital service, and how technologists were excited to work there under Obama but running in the other direction from Trump. And about how maybe there was an opportunity there, even though it felt like the antithesis.&lt;/p&gt;

&lt;p&gt;A man suggested updating apps' algorithms to suggest more things from outside of your bubble. Maybe periodically seeing stories you don't agree with.&lt;/p&gt;

&lt;p&gt;Another man suggested supporting specific organizations you support. And as “technologists, who probably have high salaries, who will probably get large tax cuts over the next four years, why don't we donate that extra money, and volunteer as well?”&lt;/p&gt;

&lt;p&gt;Somebody suggested using data to find out who voted in previous elections, that didn't vote in 2016. Find those people who didn't think it mattered and listen.&lt;/p&gt;

&lt;p&gt;A woman suggested studying the Tea Party's methods, specifically their early focus on small and local initiatives. She mentioned how folks she knew had very little understanding of how things worked at the local level. She also suggested putting together a mailing list to document the first 100 days.&lt;/p&gt;

&lt;p&gt;Another woman also discussed the Tea Party, and how disparate groups were able to pick a cause and focus on it. And how, conversely, the left is fractured; “I'm on five different environmental group emails”.&lt;/p&gt;

&lt;p&gt;A man pushed back against the idea of piercing his bubble: “I am in a bubble. I'm queer and it's hard to deal with discriminatory stuff on Facebook. Growing up, I internalized a lot of queer discrimination. So we do need to think about ourselves, too. We don't have to pretend we're entirely different, because I don't think we are.”&lt;/p&gt;

&lt;p&gt;A man talked about voter suppression, and ways to mobilize people for future elections. He talked about how the next midterm would be an uphill battle and how “things can definitely get worse.”&lt;/p&gt;

&lt;p&gt;A man said, “I'm been using PGP since 1995. And the interface is the same as it was back then. What the fuck? No!” He implored people to make it easier, from a UX perspective, to encrypt text. That we should make cryptography a standard.&lt;/p&gt;

&lt;p&gt;A UX teacher from Tallahassee just purchased her first subscription to a newspaper. She talked about how “we're used to getting media for free because we're cheap millennials. But we need to support institutions doing the real legwork. We've been promised that the first amendment will be attacked over the next four years.” That Trump supporters shouldn't be shut out, and that we can learn from the right, and on how they promoted legislation from the bottom up.&lt;/p&gt;

&lt;p&gt;A man talked about not knowing a single Trump supporter, and feeling that it was likely that some people around him were supporters that didn't feel comfortable speaking up. Just like progressive supporters in deep red states probably didn't feel comfortable speaking up, either.&lt;/p&gt;

&lt;p&gt;A woman talked about people not knowing their congresspeople, and another man talked about making voting easier, as easy as voting over your Xbox. And encouraged people to build something allowing folks to communicate more effectively.&lt;/p&gt;

&lt;p&gt;A woman recommended the book &lt;a href="https://www.amazon.com/Integrated-Activism-Applying-Connections-Economics/dp/1583946047"&gt;“Integrated Activism” by Alexis Zeigler.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A man recommended &lt;a href="https://www.codecorps.org/"&gt;Code Corps.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A woman talked about how the Civil Rights movement redirected resources away from discriminatory businesses. She implored people to use their privilege to help the less fortunate, impoverished communities.&lt;/p&gt;

&lt;p&gt;A woman said “it'd be cool if we had people from the other side in the room. To comprehend what they're thinking.”&lt;/p&gt;

&lt;p&gt;A man talked about how we need to “protect ourselves and fight back”. That Trump's model of a ruler is Putin. He implored everyone to learn how to organize in a surveillance state. That it's hard to imagine that kind of thing coming to America, but that Trump will have control of everything: the executive branch, the legislative branch, the supreme court. There's nothing holding him back but us.&lt;/p&gt;

&lt;h4&gt;
  
  
  Takeaways
&lt;/h4&gt;

&lt;p&gt;Some common themes emerged:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; People wrestled with how to respond: engage the other side and try to understand their viewpoint, repulsive as you might find it? Or draw a line in the sand and say this (racism, sexism) is not ok?&lt;/li&gt;
&lt;li&gt; Many, many people talked about being blindsided as a result of living in a bubble. And more than a few did not know how to address that.&lt;/li&gt;
&lt;li&gt; For a group of technologists, there was a fair amount of consensus that tech could not be the sole solution; that engaging people in person had to be part of the solution.&lt;/li&gt;
&lt;li&gt; There was discussion around focusing on local, state-level politics, and studying how the Tea Party rose to power.&lt;/li&gt;
&lt;li&gt; Folks talked about supporting specific organizations you believe in with time and money. And in particular, supporting journalism by subscribing, as those organizations are already struggling financially and may be under political pressure in the future.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And, recapping what one participant recommended as protection against surveillance and hacking:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;a href="https://www.torproject.org/"&gt;Get Tor&lt;/a&gt;. Tor is software that aims to protect your privacy.&lt;/li&gt;
&lt;li&gt; &lt;a href="https://en.wikipedia.org/wiki/Multi-factor_authentication"&gt;Enable 2 factor authentication&lt;/a&gt; everywhere it's available. 2FA makes it harder for people to hack into your email and other sensitive information.&lt;/li&gt;
&lt;li&gt; Get &lt;a href="https://whispersystems.org/"&gt;Signal&lt;/a&gt; and start texting there. Signal is a private messaging app for iOS and Android that encrypts your messages and protects against their being intercepted.&lt;/li&gt;
&lt;li&gt; Get a &lt;a href="https://en.wikipedia.org/wiki/Virtual_private_network"&gt;VPN&lt;/a&gt;. A VPN will encrypt your data, and increase privacy. There are free options but you should find a paid VPN provider. &lt;a href="http://www.pcmag.com/article2/0,2817,2403388,00.asp"&gt;Here's a list&lt;/a&gt;. Comment if you've got other recommendations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To the idea of being in and getting out of your social media bubble, the &lt;a href="http://graphics.wsj.com/blue-feed-red-feed/#/hillary"&gt;WSJ recently published a red feed / blue feed I found fascinating.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, if you're interested in this stuff, &lt;a href="http://eepurl.com/cojEF5"&gt;add your email to this mailchimp list to stay in touch&lt;/a&gt;. I can't say what will come of it but it's a start.&lt;/p&gt;

</description>
      <category>politics</category>
      <category>makeadiff</category>
      <category>techforgood</category>
    </item>
  </channel>
</rss>
