<?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: Florin </title>
    <description>The latest articles on DEV Community by Florin  (@florin7).</description>
    <link>https://dev.to/florin7</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%2F4121235%2Fad3d6b97-5eae-4653-978b-c4608996e33f.png</url>
      <title>DEV Community: Florin </title>
      <link>https://dev.to/florin7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/florin7"/>
    <language>en</language>
    <item>
      <title>I let an AI agent write a README for a real repo — here's what it produced</title>
      <dc:creator>Florin </dc:creator>
      <pubDate>Fri, 11 Sep 2026 17:24:10 +0000</pubDate>
      <link>https://dev.to/florin7/i-let-an-ai-agent-write-a-readme-for-a-real-repo-heres-what-it-produced-4oe2</link>
      <guid>https://dev.to/florin7/i-let-an-ai-agent-write-a-readme-for-a-real-repo-heres-what-it-produced-4oe2</guid>
      <description>&lt;p&gt;Most small open-source projects have a README that stopped matching the code&lt;br&gt;
two years ago. I wanted to know whether an AI agent could write one that&lt;br&gt;
actually reflects what's in the repository — not a plausible-sounding one, but&lt;br&gt;
one where every flag and environment variable it mentions is real.&lt;/p&gt;

&lt;p&gt;So I built a small pipeline and pointed it at someone else's repository.&lt;/p&gt;
&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;The rule I started with: &lt;strong&gt;the model never decides what's true about the code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The pipeline has four stages, and only one of them involves an LLM:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read&lt;/strong&gt; — fetch the repository's files over HTTP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extract facts&lt;/strong&gt; — parse &lt;code&gt;pyproject.toml&lt;/code&gt;, &lt;code&gt;argparse&lt;/code&gt; calls, &lt;code&gt;os.environ&lt;/code&gt;
lookups, the LICENSE file. Deterministic, no model involved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate&lt;/strong&gt; — hand the model the files &lt;em&gt;and&lt;/em&gt; the extracted facts, ask for
a README.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify&lt;/strong&gt; — check the output against the facts. Any CLI flag, environment
variable, or license claim that isn't in the extracted facts is treated as
invented, and the deliverable is rejected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Stage 4 is the part that makes this usable. A README that documents a&lt;br&gt;
&lt;code&gt;--dry-run&lt;/code&gt; flag which doesn't exist is worse than no README at all, because&lt;br&gt;
someone will try it.&lt;/p&gt;
&lt;h2&gt;
  
  
  The repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/sloria/ped" rel="noopener noreferrer"&gt;&lt;code&gt;sloria/ped&lt;/code&gt;&lt;/a&gt; — a small MIT-licensed CLI tool&lt;br&gt;
that opens Python modules in your editor. A few hundred lines across a&lt;br&gt;
handful of source files. Small enough to reason about, real enough to be interesting.&lt;/p&gt;

&lt;p&gt;I picked it because it already &lt;em&gt;has&lt;/em&gt; a decent README. That made it a fair&lt;br&gt;
test: could the agent produce something comparable, without copying it?&lt;/p&gt;
&lt;h2&gt;
  
  
  What the agent read
&lt;/h2&gt;

&lt;p&gt;Nine files, chosen by a priority list — config first, then entry points, then&lt;br&gt;
the rest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LICENSE
README.rst
pyproject.toml
src/ped/__init__.py
src/ped/guess_module.py
src/ped/install_completion.py
src/ped/ped_bash_completion.sh
src/ped/pypath.py
src/ped/style.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What it extracted, deterministically
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ped"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MIT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requires_python"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;=3.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ped"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cli_flags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"--complete"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--editor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--help"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"--version"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-h"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-v"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"env_vars"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"EDITOR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NO_COLOR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PED_EDITOR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="s2"&gt;"PED_OPEN_DIRECTORIES"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SHELL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VISUAL"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two of these were harder to get than they look.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--help&lt;/code&gt; and &lt;code&gt;-h&lt;/code&gt; are never written in the source — &lt;code&gt;argparse&lt;/code&gt; adds them. If&lt;br&gt;
you extract flags naively and then verify against that list, a correct README&lt;br&gt;
that documents &lt;code&gt;--help&lt;/code&gt; gets rejected as hallucinated. I had to add them&lt;br&gt;
explicitly as known-implicit.&lt;/p&gt;

&lt;p&gt;The environment variables were worse. The code reads them in a loop:&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PED_EDITOR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VISUAL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EDITOR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;rv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A regex looking for &lt;code&gt;os.environ.get("SOMETHING")&lt;/code&gt; finds nothing here. My first&lt;br&gt;
verifier flagged &lt;code&gt;PED_EDITOR&lt;/code&gt; as invented — in a README that was correct. The&lt;br&gt;
extractor was wrong, not the text.&lt;/p&gt;

&lt;p&gt;That failure was useful. It's the exact shape of error that makes verification&lt;br&gt;
worth building: the checker caught a mismatch, and the mismatch turned out to&lt;br&gt;
be in the checker.&lt;/p&gt;
&lt;h2&gt;
  
  
  What it produced
&lt;/h2&gt;

&lt;p&gt;The full output is&lt;br&gt;
&lt;a href="https://agent.aitomaton.dev/example-ped-README.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;. A few things it&lt;br&gt;
got right that I didn't expect:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The environment variable precedence.&lt;/strong&gt; It documented that &lt;code&gt;PED_EDITOR&lt;/code&gt; takes&lt;br&gt;
priority over &lt;code&gt;VISUAL&lt;/code&gt;, which takes priority over &lt;code&gt;EDITOR&lt;/code&gt; — as a table. That&lt;br&gt;
ordering is only visible from the loop above; nothing states it in prose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;PED_OPEN_DIRECTORIES&lt;/code&gt;.&lt;/strong&gt; An undocumented-in-README variable that changes&lt;br&gt;
whether a package opens as a directory or as its &lt;code&gt;__init__.py&lt;/code&gt;. It's one line&lt;br&gt;
in the source. The agent found it, explained what it does, and gave a shell&lt;br&gt;
snippet for setting it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tab completion.&lt;/strong&gt; It read &lt;code&gt;install_completion.py&lt;/code&gt;, worked out that the module&lt;br&gt;
prints a completion script to stdout and picks bash vs zsh from &lt;code&gt;$SHELL&lt;/code&gt;, and&lt;br&gt;
wrote a section for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works.&lt;/strong&gt; Four steps, each traceable to actual code — &lt;code&gt;sys.path&lt;/code&gt;&lt;br&gt;
manipulation for pipx installs, &lt;code&gt;difflib&lt;/code&gt; for partial name matching, &lt;code&gt;inspect&lt;/code&gt;&lt;br&gt;
for locating source and line numbers, and the specific list of editors that&lt;br&gt;
get a &lt;code&gt;+lineno&lt;/code&gt; argument.&lt;/p&gt;
&lt;h2&gt;
  
  
  What went wrong
&lt;/h2&gt;

&lt;p&gt;One thing, and it's instructive.&lt;/p&gt;

&lt;p&gt;The Bash tab-completion section came out as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; ped.install_completion &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /usr/local/etc/bash_completion.d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That redirects into a &lt;em&gt;directory&lt;/em&gt;, which fails. My first instinct was that the&lt;br&gt;
agent had invented it.&lt;/p&gt;

&lt;p&gt;It hadn't. That exact command is in the upstream README. The agent reproduced&lt;br&gt;
the source faithfully — including its mistake.&lt;/p&gt;

&lt;p&gt;This is a real limit of the approach, and I'd rather state it than hide it:&lt;br&gt;
&lt;strong&gt;the pipeline verifies that claims match the code, not that the code's own&lt;br&gt;
documentation is correct.&lt;/strong&gt; When a project's README has an error, a&lt;br&gt;
code-grounded rewrite can carry it forward.&lt;/p&gt;

&lt;p&gt;I changed it to a generic form that can't be pasted into an invalid path:&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="c"&gt;# Redirect the output to a file inside your shell's completion directory.&lt;/span&gt;
&lt;span class="c"&gt;# The exact location depends on your OS.&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; ped.install_completion &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &amp;lt;bash-completion-dir&amp;gt;/ped
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I didn't invent &lt;code&gt;/ped&lt;/code&gt; as the filename, because nothing in the code specifies&lt;br&gt;
a destination — &lt;code&gt;install_completion.py&lt;/code&gt; just writes to stdout. Where it goes&lt;br&gt;
is the user's choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The verification output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cli_flags   mentioned=8   invented=[]
env_vars    mentioned=5   invented=[]
sections    12
trailing newline: ok
requires_python stated: yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero invented flags, zero invented environment variables. Not because the&lt;br&gt;
model is trustworthy — because anything it invented would have been caught and&lt;br&gt;
the whole deliverable rejected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;$0.04&lt;/strong&gt; for the generation. One call, ~3,500 output tokens.&lt;/p&gt;

&lt;p&gt;The reading and verification added no LLM cost; they run as deterministic&lt;br&gt;
code. That ratio matters: the expensive part is the writing, and the writing is the&lt;br&gt;
part you can't trust without checking.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd take away from this
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Extraction is harder than generation.&lt;/strong&gt; The model wrote a good README on the&lt;br&gt;
first try. Getting a &lt;em&gt;reliable&lt;/em&gt; list of what's actually in the code took&lt;br&gt;
several iterations, and in this run the bugs I found were in my parser&lt;br&gt;
rather than in the generated prose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verification changes what you can ship.&lt;/strong&gt; Without stage 4, this is a demo. A&lt;br&gt;
README that's 95% right is unusable for anything real, because you don't know&lt;br&gt;
which 5%. With a checker that rejects unverifiable claims, the failure mode&lt;br&gt;
becomes "output rejected" instead of "output subtly wrong."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Faithful isn't the same as correct.&lt;/strong&gt; The completion-path bug is the clearest&lt;br&gt;
lesson here. Grounding output in source code means inheriting the source's&lt;br&gt;
errors too. A human still has to read the result.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I turned this into a small service: send a public GitHub repository URL, get&lt;br&gt;
a README back within 24 hours, £30, and you see the full file before you pay&lt;br&gt;
anything. It's run by an AI agent that I supervise — I read every deliverable&lt;br&gt;
before it's sent. Details at&lt;br&gt;
&lt;a href="https://agent.aitomaton.dev" rel="noopener noreferrer"&gt;agent.aitomaton.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The example above is the unedited pipeline output, apart from the one&lt;br&gt;
correction described. Compare it with&lt;br&gt;
&lt;a href="https://github.com/sloria/ped" rel="noopener noreferrer"&gt;the repository's own README&lt;/a&gt; if you want to&lt;br&gt;
judge for yourself.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>documentation</category>
      <category>python</category>
    </item>
  </channel>
</rss>
