<?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: Fernando Paladini</title>
    <description>The latest articles on DEV Community by Fernando Paladini (@paladini).</description>
    <link>https://dev.to/paladini</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%2F1065831%2F79b4d650-5838-4481-a62f-8f03f4010512.jpeg</url>
      <title>DEV Community: Fernando Paladini</title>
      <link>https://dev.to/paladini</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paladini"/>
    <language>en</language>
    <item>
      <title>Build a Citation-Ready Static Site with GEO Basics</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Sun, 04 Oct 2026 12:36:35 +0000</pubDate>
      <link>https://dev.to/paladini/build-a-citation-ready-static-site-with-geo-basics-257f</link>
      <guid>https://dev.to/paladini/build-a-citation-ready-static-site-with-geo-basics-257f</guid>
      <description>&lt;h1&gt;
  
  
  Build a Citation-Ready Static Site with GEO Basics
&lt;/h1&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Static sites are easy to deploy, but easy to leave ambiguous. A page can look correct in a browser while missing a canonical URL, language alternates, valid structured data, a sitemap, or a clear crawler policy.&lt;/p&gt;

&lt;p&gt;This tutorial uses &lt;a href="https://github.com/paladini/generative-engine-optimization-basic-guide" rel="noopener noreferrer"&gt;GEO Basics&lt;/a&gt;, an MIT-licensed open-source static guide, as a small working example. You will clone the repository, serve it without a framework, run its validator, and inspect the files that make the site understandable to both people and machines.&lt;/p&gt;

&lt;p&gt;The goal is not to manufacture rankings or force an AI system to cite a page. The goal is a crawlable, readable, source-backed site with checks that catch common publishing mistakes before deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you will build
&lt;/h2&gt;

&lt;p&gt;You will run a static site locally and verify that it has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;human-readable HTML with a clear page title and headings;&lt;/li&gt;
&lt;li&gt;canonical and &lt;code&gt;hreflang&lt;/code&gt; links for English and Brazilian Portuguese;&lt;/li&gt;
&lt;li&gt;JSON-LD that matches visible &lt;code&gt;TechArticle&lt;/code&gt; and &lt;code&gt;FAQPage&lt;/code&gt; content;&lt;/li&gt;
&lt;li&gt;a root &lt;code&gt;robots.txt&lt;/code&gt;, &lt;code&gt;sitemap.xml&lt;/code&gt;, and optional &lt;code&gt;llms.txt&lt;/code&gt; guide;&lt;/li&gt;
&lt;li&gt;a repeatable Node.js validation command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GEO Basics has no framework, build step, database, or runtime server in its documented workflow. The current repository also includes committed Mermaid sources and rendered SVG diagrams. There is no tagged release, so the commands below use the current &lt;code&gt;main&lt;/code&gt; checkout. At the time of writing, the verified commit is &lt;code&gt;acd0b37f041db701aa8f7edcd3e280ee4662f7db&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git;&lt;/li&gt;
&lt;li&gt;Node.js with a current &lt;code&gt;node&lt;/code&gt; executable;&lt;/li&gt;
&lt;li&gt;a browser;&lt;/li&gt;
&lt;li&gt;a terminal with network access for the clone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The validator uses only Node.js built-ins. You do not need to install an npm package for this repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clone and inspect the site
&lt;/h2&gt;

&lt;p&gt;Clone the public repository and enter it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/paladini/generative-engine-optimization-basic-guide.git
&lt;span class="nb"&gt;cd &lt;/span&gt;generative-engine-optimization-basic-guide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository's top-level files are intentionally understandable. &lt;code&gt;index.html&lt;/code&gt; is the canonical English page. The Portuguese page lives at &lt;code&gt;lang/pt-br/index.html&lt;/code&gt;. &lt;code&gt;robots.txt&lt;/code&gt; points crawlers to the sitemap, and &lt;code&gt;sitemap.xml&lt;/code&gt; lists both language URLs and their alternates.&lt;/p&gt;

&lt;p&gt;The site is served as files. Start a local server from the repository root:&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; http.server 8123 &lt;span class="nt"&gt;--bind&lt;/span&gt; 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;a href="http://127.0.0.1:8123/" rel="noopener noreferrer"&gt;http://127.0.0.1:8123/&lt;/a&gt; in a browser. The Python command is only a local smoke test. It is not part of the project's validator and does not deploy anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand the page signals
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;index.html&lt;/code&gt; and look at the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; before changing the body. The page declares &lt;code&gt;lang="en"&lt;/code&gt;, a viewport, a descriptive &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;, a meta description, &lt;code&gt;robots&lt;/code&gt; instructions, and a canonical URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"canonical"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://paladini.github.io/generative-engine-optimization-basic-guide/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://paladini.github.io/generative-engine-optimization-basic-guide/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"pt-BR"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://paladini.github.io/generative-engine-optimization-basic-guide/lang/pt-br/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The canonical URL answers which URL represents the English page. The alternate links connect the language variants. They do not translate content, improve a page by themselves, or guarantee that a search engine will display a particular version. They reduce ambiguity when the same guide exists in more than one language.&lt;/p&gt;

&lt;p&gt;The page also includes JSON-LD with a &lt;code&gt;TechArticle&lt;/code&gt; and an &lt;code&gt;FAQPage&lt;/code&gt;. The important rule is consistency: structured data should describe content that is visible on the page. If you add an FAQ only to JSON-LD but not to the rendered HTML, the metadata describes something a reader cannot see.&lt;/p&gt;

&lt;p&gt;Google's current guidance says that normal SEO foundations remain relevant for its generative AI features. It also says that structured data is not required for generative AI search and that there is no special markup that guarantees visibility. Treat JSON-LD as a useful description of an already-good page, not as a shortcut.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add crawler and agent context carefully
&lt;/h2&gt;

&lt;p&gt;The repository contains a simple &lt;code&gt;robots.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User-agent: *
Allow: /

Sitemap: https://paladini.github.io/generative-engine-optimization-basic-guide/sitemap.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This permits normal crawling and advertises the sitemap. Crawler policy is an access decision, not a ranking trick. If you change it, check the rules for the user agents you actually intend to control.&lt;/p&gt;

&lt;p&gt;The site also includes &lt;code&gt;llms.txt&lt;/code&gt;, a human-readable Markdown map of the guide, repository, contributing instructions, sitemap, and primary references. The file is useful as a concise orientation document for tools that choose to read it. It is not a replacement for HTML, &lt;code&gt;robots.txt&lt;/code&gt;, or &lt;code&gt;sitemap.xml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That distinction matters because the current Google documentation explicitly says Google Search ignores &lt;code&gt;llms.txt&lt;/code&gt; for Search and that creating one neither helps nor harms Google rankings. The &lt;code&gt;/llms.txt&lt;/code&gt; proposal itself describes it as a convention for giving agents a concise guide to important resources. Use it as an optional documentation surface, not as an access-control file or a promise of AI citations.&lt;/p&gt;

&lt;p&gt;For OpenAI crawlers, the controls are separate. OpenAI documents &lt;code&gt;OAI-SearchBot&lt;/code&gt; for search and &lt;code&gt;GPTBot&lt;/code&gt; for potential training use. A site owner can make an independent policy decision for each user agent in &lt;code&gt;robots.txt&lt;/code&gt;. Do not infer that allowing one means allowing the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the project's validator
&lt;/h2&gt;

&lt;p&gt;From the repository root, run the exact documented command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node scripts/validate-site.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current script checks that the required HTML, CSS, JavaScript, image, diagram, crawler, sitemap, and &lt;code&gt;llms.txt&lt;/code&gt; files exist. It checks JavaScript syntax, expected PNG dimensions, internal anchors, external-link safety attributes, canonical and &lt;code&gt;hreflang&lt;/code&gt; links, JSON-LD parsing, required schema types, and the canonical GitHub Pages URL.&lt;/p&gt;

&lt;p&gt;The expected result for the current checkout is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Static validation passed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During copy edits, the repository also documents a faster command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node scripts/validate-site.mjs &lt;span class="nt"&gt;--quick&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The quick mode skips some diagram, image-dimension, sitemap, and robots checks. Use it for fast feedback while editing text, then run the full command before opening a pull request or publishing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify a useful failure mode
&lt;/h2&gt;

&lt;p&gt;A validator is more valuable when it can fail for a meaningful reason. In a temporary copy, remove a required file such as &lt;code&gt;llms.txt&lt;/code&gt; and run the full command again. The script should report a missing required file and return a non-zero exit code. Restore the file before continuing.&lt;/p&gt;

&lt;p&gt;Do this experiment in a disposable copy or with version control ready to restore the file. The repository's contributor guide says that changes should stay focused and that metadata, canonical URLs, language alternates, structured data, accessibility, and validation should be checked together.&lt;/p&gt;

&lt;p&gt;The validator is deliberately local. It does not prove that GitHub Pages has deployed the latest commit, that a search engine indexed a page, or that an AI answer system will use it. Those are separate operational and external states.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this structure works
&lt;/h2&gt;

&lt;p&gt;The site makes the primary content visible in ordinary HTML, uses descriptive section headings, links to original sources, and keeps the English and Portuguese pages connected. These choices help readers first and provide clearer inputs for crawlers and downstream tools.&lt;/p&gt;

&lt;p&gt;The repository also keeps its validation close to the content. That is a practical design decision for a static site: a contributor can run one command without learning a framework-specific build pipeline. The checks are not a substitute for editorial review, accessibility testing, link review, or a real deployment check, but they prevent several easy-to-miss regressions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and security boundaries
&lt;/h2&gt;

&lt;p&gt;Do not treat passing validation as proof of search performance. Google says that meeting technical requirements and best practices does not guarantee crawling, indexing, or serving. Avoid services or tools that promise an internal ranking score or guaranteed AI placement.&lt;/p&gt;

&lt;p&gt;Do not copy a page's visible claims into JSON-LD without checking that the claims remain visible and accurate. Incorrect structured data can make a page less trustworthy and can fail eligibility for supported rich-result features.&lt;/p&gt;

&lt;p&gt;Do not place secrets in a static repository. Static HTML, JavaScript, &lt;code&gt;robots.txt&lt;/code&gt;, sitemaps, and &lt;code&gt;llms.txt&lt;/code&gt; are public once deployed. Review source links, email addresses, generated assets, and build artifacts before publishing.&lt;/p&gt;

&lt;p&gt;Finally, remember that &lt;code&gt;llms.txt&lt;/code&gt; is not a firewall. Use hosting permissions, authentication, and &lt;code&gt;robots.txt&lt;/code&gt; policies for the boundaries they actually support. A public page should be written as public content.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does GEO Basics require a framework?
&lt;/h3&gt;

&lt;p&gt;No. The documented path is a static site served directly from files. The repository's validator is a Node.js script, not a framework build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does JSON-LD guarantee an AI citation?
&lt;/h3&gt;

&lt;p&gt;No. It can describe visible page meaning, but no markup guarantees a citation, ranking, inclusion, or traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is &lt;code&gt;llms.txt&lt;/code&gt; required for Google Search?
&lt;/h3&gt;

&lt;p&gt;No. Google's current documentation says Google Search ignores it. It can still be maintained as an optional guide for other tools and readers.&lt;/p&gt;

&lt;h3&gt;
  
  
  What should I verify after deployment?
&lt;/h3&gt;

&lt;p&gt;Check the deployed URLs over HTTPS, inspect the rendered HTML, fetch &lt;code&gt;robots.txt&lt;/code&gt; and &lt;code&gt;sitemap.xml&lt;/code&gt;, confirm the canonical host, and run the same content checks against the deployed site where possible. Then use the relevant webmaster tools to observe indexing and performance rather than assuming them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;GEO Basics is a compact example of a static site that treats discoverability as documentation quality: readable HTML, explicit metadata, language relationships, source links, crawler files, and a local validator. Start with the page a person should trust, add machine-readable context that matches it, and keep every guarantee out of the implementation unless you can prove it.&lt;/p&gt;

&lt;p&gt;If you maintain a static site, which check would catch the most expensive publishing mistake in your workflow: canonical URLs, language alternates, structured data, crawler policy, or deployment verification?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;AI assistance disclosure:&lt;/strong&gt; AI assistance was used to organize this tutorial and review its wording. The repository state, validator behavior, commands, current files, license, and platform guidance were checked against the linked primary sources and the current public checkout.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>seo</category>
      <category>html</category>
      <category>tutorial</category>
      <category>structureddata</category>
    </item>
    <item>
      <title>Fetch Multilingual Daily Reflections in Node.js with aa-daily-reflections</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Sat, 03 Oct 2026 12:39:05 +0000</pubDate>
      <link>https://dev.to/paladini/fetch-multilingual-daily-reflections-in-nodejs-with-aa-daily-reflections-4mkp</link>
      <guid>https://dev.to/paladini/fetch-multilingual-daily-reflections-in-nodejs-with-aa-daily-reflections-4mkp</guid>
      <description>&lt;h1&gt;
  
  
  Fetch Multilingual Daily Reflections in Node.js with aa-daily-reflections
&lt;/h1&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;If you need a small Node.js integration for dated Daily Reflections from Alcoholics Anonymous, the &lt;code&gt;aa-daily-reflections&lt;/code&gt; package provides both a JavaScript API and the &lt;code&gt;aa-daily&lt;/code&gt; command-line tool. This tutorial uses the published npm package to fetch a date in English, Spanish, or French, then adds input validation and a responsible failure path.&lt;/p&gt;

&lt;p&gt;The important boundary is that this package retrieves content from the public AA.org service. It is an unofficial client, not an archive or a license to republish the returned text. Use it for personal, educational, or recovery-support workflows, respect the source's terms, and avoid unnecessary requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you will build
&lt;/h2&gt;

&lt;p&gt;You will install the published package, query one known date from the command line, request another language, and handle an invalid date without making a network request. The same package also exposes a &lt;code&gt;DailyReflections&lt;/code&gt; class for JavaScript and TypeScript programs.&lt;/p&gt;

&lt;p&gt;The project is maintained in the public &lt;a href="https://github.com/paladini/aa-daily-reflections-api" rel="noopener noreferrer"&gt;paladini/aa-daily-reflections-api repository&lt;/a&gt; and published as &lt;a href="https://www.npmjs.com/package/aa-daily-reflections" rel="noopener noreferrer"&gt;aa-daily-reflections on npm&lt;/a&gt;. At the time of writing, the npm package reports version 1.0.1, MIT licensing, Node.js &amp;gt;=16.0.0, npm &amp;gt;=8.0.0, and support for Windows, macOS, and Linux on x64 and arm64.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 16 or newer and npm 8 or newer.&lt;/li&gt;
&lt;li&gt;Network access to the upstream service when fetching a reflection.&lt;/li&gt;
&lt;li&gt;A use case that is allowed to retrieve the source content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The commands below use &lt;code&gt;npx --yes&lt;/code&gt; so you can try the CLI without adding it to a project first. For a repeatable application, install it as a dependency and commit your lockfile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the CLI
&lt;/h2&gt;

&lt;p&gt;Run the help command first. It is a useful smoke test because it exercises the published package and does not request reflection content.&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;--yes&lt;/span&gt; &lt;span class="nt"&gt;--package&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aa-daily-reflections aa-daily &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI supports today's reflection, a date in &lt;code&gt;MM/DD&lt;/code&gt; form, and language selection with &lt;code&gt;en&lt;/code&gt;, &lt;code&gt;es&lt;/code&gt;, or &lt;code&gt;fr&lt;/code&gt;. To request June 25 in Spanish:&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;--yes&lt;/span&gt; &lt;span class="nt"&gt;--package&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aa-daily-reflections aa-daily &lt;span class="nt"&gt;-d&lt;/span&gt; 06/25 &lt;span class="nt"&gt;-l&lt;/span&gt; es
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command prints display metadata such as the date, title, source reference, and reflection text. Do not copy that output into a public article, product database, or search index without checking the copyright and distribution terms that apply to the source content.&lt;/p&gt;

&lt;p&gt;For today's English entry, the shorter form is:&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;--yes&lt;/span&gt; &lt;span class="nt"&gt;--package&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aa-daily-reflections aa-daily
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a French date, use the explicit flags:&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;--yes&lt;/span&gt; &lt;span class="nt"&gt;--package&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aa-daily-reflections aa-daily &lt;span class="nt"&gt;-d&lt;/span&gt; 12/24 &lt;span class="nt"&gt;-l&lt;/span&gt; fr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package README documents English as the default language and Spanish and French as additional supported languages. The CLI also documents &lt;code&gt;aa-daily MM/DD&lt;/code&gt; as shorthand for a dated English request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the JavaScript API
&lt;/h2&gt;

&lt;p&gt;The programmatic API is a better fit when you want to show metadata in your own interface, add logging, or decide whether to display the full returned fields. Create a small project and install the package:&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;mkdir &lt;/span&gt;daily-reflection-demo
&lt;span class="nb"&gt;cd &lt;/span&gt;daily-reflection-demo
npm init &lt;span class="nt"&gt;--yes&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;aa-daily-reflections
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;index.js&lt;/code&gt; with a narrow, explicit workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DailyReflections&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aa-daily-reflections&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reflections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DailyReflections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;reflections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getReflection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;copyright&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;copyright&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Could not fetch the reflection: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package's documented &lt;code&gt;DailyReflection&lt;/code&gt; shape includes the date, day, month, month name, title, quote, reflection, reference, and copyright fields. This example intentionally prints only metadata and the copyright field. A real recovery-support UI might show the text to an authorized user, but it should not silently turn the response into a public content mirror.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add language and date validation
&lt;/h2&gt;

&lt;p&gt;The package validates calendar dates before it fetches. For example, February 30 is rejected with an error stating that day 30 is not valid for month 2. You can make that behavior part of a reusable command instead of treating every failure as a network problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DailyReflections&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aa-daily-reflections&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supportedLanguages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;readReflection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;day&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;supportedLanguages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unsupported language: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DailyReflections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getReflection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;day&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;readReflection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reference&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reference&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation matters. An invalid date is a caller-input problem. A failed fetch may be a connectivity problem, an upstream change, rate limiting, or a service response that the parser does not understand. Keeping those cases visible makes retries safer and debugging faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the result reproducibly
&lt;/h2&gt;

&lt;p&gt;Use three checks when evaluating an integration:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;aa-daily --help&lt;/code&gt; to confirm that the installed package exposes the expected CLI.&lt;/li&gt;
&lt;li&gt;Fetch a fixed date such as &lt;code&gt;06/25&lt;/code&gt; in &lt;code&gt;en&lt;/code&gt;, &lt;code&gt;es&lt;/code&gt;, or &lt;code&gt;fr&lt;/code&gt; and verify that the output contains date and source metadata.&lt;/li&gt;
&lt;li&gt;Request &lt;code&gt;02/30&lt;/code&gt; and confirm that the client rejects it with a validation error.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The fixed-date check is more reproducible than “today” because the expected input does not change with the calendar. Avoid asserting that a particular title or reflection body will remain unchanged unless you have a current, authorized source snapshot and a reason to retain it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the package is useful
&lt;/h2&gt;

&lt;p&gt;The library keeps the integration small: a language-aware client, date-oriented methods, and a CLI that maps directly to common requests. Its documented API offers &lt;code&gt;getToday()&lt;/code&gt;, &lt;code&gt;getReflection(month, day)&lt;/code&gt;, &lt;code&gt;setLanguage(language)&lt;/code&gt;, and &lt;code&gt;getLanguage()&lt;/code&gt;. That gives a script enough structure to build a private daily view without embedding an unofficial scraper in every application.&lt;/p&gt;

&lt;p&gt;The repository separates utilities, HTTP handling, parsing, types, and the main client. That organization also gives contributors clear places to inspect when the upstream HTML or API behavior changes. The current repository documents an MIT license and credits AA World Services as the source of the copyrighted content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and security boundaries
&lt;/h2&gt;

&lt;p&gt;Do not put credentials into this client. The documented workflow reads public AA.org content and does not require an API key. Still, fetched text is external input: escape it for HTML, do not render it as trusted markup, and log only the metadata you need.&lt;/p&gt;

&lt;p&gt;Respect rate limits. Do not call &lt;code&gt;getToday()&lt;/code&gt; on every page render if a daily cache is enough. Add timeouts, bounded retries, and a stale-data policy in a production integration. A network error is not proof that the date is unavailable.&lt;/p&gt;

&lt;p&gt;The client is unofficial and provides no warranty. The upstream service can change its response format, availability, or terms. The repository's own development scripts also need modernization on current Windows and TypeScript installations: the documented build script calls Unix &lt;code&gt;rm -rf&lt;/code&gt;, and the current TypeScript configuration uses the removed &lt;code&gt;moduleResolution=node10&lt;/code&gt; option. The published package's CLI remains the tested path for this tutorial, but contributors should verify the source checkout separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does this package store the reflections locally?
&lt;/h3&gt;

&lt;p&gt;The documented client fetches content when you request it. If you add caching, define retention and access rules yourself, and do not assume that local storage grants redistribution rights.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use Portuguese?
&lt;/h3&gt;

&lt;p&gt;The documented supported languages are English, Spanish, and French. Do not pass an undocumented language code and treat a fallback as correct without checking the returned language.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is this an official AA integration?
&lt;/h3&gt;

&lt;p&gt;No. The README explicitly describes the library as unofficial and asks users to respect the source, copyright, rate limits, and intended educational or recovery-support use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I publish the returned text in my app?
&lt;/h3&gt;

&lt;p&gt;Only after checking the permissions and terms that apply to your use case. This tutorial does not grant republishing rights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;aa-daily-reflections&lt;/code&gt; is a compact way to add dated, multilingual Daily Reflections access to a Node.js script or CLI workflow. Start with the published package, validate dates before fetching, keep retries and caching conservative, and treat the upstream text as copyrighted external content rather than application-owned data.&lt;/p&gt;

&lt;p&gt;If you build a private or educational integration with this package, what access-control and caching rule would you add first?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;AI assistance disclosure:&lt;/strong&gt; AI assistance was used to organize this tutorial and review its wording. The commands, package metadata, repository limitations, and smoke-test behavior were checked against the linked primary sources and the published CLI.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>api</category>
      <category>javascript</category>
      <category>node</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Harness Score 1.8.0 No Longer Fails Closed-Source Repos for Missing Files</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Fri, 02 Oct 2026 23:13:49 +0000</pubDate>
      <link>https://dev.to/paladini/harness-score-180-no-longer-fails-closed-source-repos-for-missing-files-1m5f</link>
      <guid>https://dev.to/paladini/harness-score-180-no-longer-fails-closed-source-repos-for-missing-files-1m5f</guid>
      <description>&lt;p&gt;A proprietary repository can declare its license in &lt;code&gt;composer.json&lt;/code&gt; or &lt;code&gt;package.json&lt;/code&gt; and still have no root &lt;code&gt;LICENSE&lt;/code&gt; file. It can also have no MCP config. Harness Score used to score both as hygiene failures.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/paladini/harness-score/releases/tag/v1.8.0" rel="noopener noreferrer"&gt;Harness Score 1.8.0&lt;/a&gt;, published on October 2, 2026, takes those checks out of the percentage when they do not apply. They stay in the report. They do not earn points for absence.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://github.com/andersonRogani" rel="noopener noreferrer"&gt;Anderson Rogani&lt;/a&gt; for reporting this in &lt;a href="https://github.com/paladini/harness-score/issues/73" rel="noopener noreferrer"&gt;issue #73&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What leaves the score
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://paladini.github.io/harness-score/guide/measure-and-improve#hyg-05" rel="noopener noreferrer"&gt;HYG-05&lt;/a&gt; is not applicable when there is no root &lt;code&gt;LICENSE&lt;/code&gt;, &lt;code&gt;LICENSE.md&lt;/code&gt;, &lt;code&gt;LICENSE.txt&lt;/code&gt;, or &lt;code&gt;COPYING&lt;/code&gt;, and the root manifest &lt;code&gt;license&lt;/code&gt; is &lt;code&gt;proprietary&lt;/code&gt; or &lt;code&gt;UNLICENSED&lt;/code&gt;. Composer accepts a string or an array of only those values. A nested manifest does not count. A real license file still passes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://paladini.github.io/harness-score/guide/measure-and-improve#hyg-08" rel="noopener noreferrer"&gt;HYG-08&lt;/a&gt; is not applicable when no MCP config exists. &lt;a href="https://paladini.github.io/harness-score/guide/measure-and-improve#hyg-04" rel="noopener noreferrer"&gt;HYG-04&lt;/a&gt; can still pass, because there is nothing to leak. HYG-08 does not award points for that absence.&lt;/p&gt;

&lt;p&gt;The JSON field is &lt;code&gt;checks[].applicable: false&lt;/code&gt;, with &lt;code&gt;passed&lt;/code&gt; still &lt;code&gt;false&lt;/code&gt; and zero points earned. Those points leave the numerator and the denominator. &lt;code&gt;--diff&lt;/code&gt; calls that &lt;code&gt;became-not-applicable&lt;/code&gt; or &lt;code&gt;became-applicable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"private": true&lt;/code&gt; and Cargo &lt;code&gt;publish = false&lt;/code&gt; are publish flags, not licenses. HYG-05 never reads &lt;code&gt;Cargo.toml&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I scanned
&lt;/h2&gt;

&lt;p&gt;On October 2, 2026, &lt;code&gt;npx --yes harness-score@1.8.0&lt;/code&gt; scored an otherwise empty directory with &lt;code&gt;"license": "proprietary"&lt;/code&gt; in &lt;code&gt;composer.json&lt;/code&gt; at L0, 11/103. HYG-05 and HYG-08 were not applicable. The full catalog is still 108 points when every check applies.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"private": true&lt;/code&gt; alone still failed HYG-05, at 11/105. So did &lt;code&gt;"license": "MIT"&lt;/code&gt; with no license file. Comparing the private package with the proprietary Composer sample, the CLI reported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Score: 11/105 (10%) → 11/103 (11%) (+1pp)
Now not applicable: HYG-05
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Earned hygiene points stayed at 10. The percentage rose because a failed check left the denominator.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm view harness-score version&lt;/code&gt; returned &lt;code&gt;1.8.0&lt;/code&gt;. If you gate CI on &lt;code&gt;--min-level&lt;/code&gt;, pin that version and compare earned points over the applicable maximum. I did not scan the private repository from the original report.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>opensource</category>
      <category>cli</category>
    </item>
    <item>
      <title>Install 36 Local Developer Tools in Cursor with DevUtils MCP</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Fri, 02 Oct 2026 12:37:02 +0000</pubDate>
      <link>https://dev.to/paladini/install-36-local-developer-tools-in-cursor-with-devutils-mcp-42j9</link>
      <guid>https://dev.to/paladini/install-36-local-developer-tools-in-cursor-with-devutils-mcp-42j9</guid>
      <description>&lt;p&gt;AI coding assistants can calculate a hash, decode a JWT, format JSON, or generate a UUID. The problem is not that these operations are difficult. The problem is making the assistant use a defined tool with visible boundaries instead of guessing, switching to an unreviewed web service, or producing a plausible but incorrect result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/paladini/devutils-cursor-plugin" rel="noopener noreferrer"&gt;DevUtils MCP&lt;/a&gt; packages a local &lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; server for Cursor and Claude Code. The plugin currently declares version 1.0.7 and starts &lt;code&gt;devutils-mcp-server&lt;/code&gt; through &lt;code&gt;npx&lt;/code&gt;. The related server exposes 36 utilities for hashing, encoding, UUIDs, JWTs, JSON, network calculations, and text processing.&lt;/p&gt;

&lt;p&gt;This tutorial installs the plugin, shows the equivalent configuration, and verifies the important security boundary: the utility call runs in a local process, while the first &lt;code&gt;npx&lt;/code&gt; launch may download the package from npm.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Install &lt;strong&gt;DevUtils MCP&lt;/strong&gt; from Cursor Settings or add the repository as a Claude Code plugin. If you need a manual MCP configuration, use the documented &lt;code&gt;npx -y devutils-mcp-server&lt;/code&gt; command. Then ask your assistant to generate a UUID or validate JSON and confirm that the tool appears in the client's MCP list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cursor with MCP support, or Claude Code with plugin support.&lt;/li&gt;
&lt;li&gt;Node.js 18 or newer. The server package declares &lt;code&gt;engines.node&lt;/code&gt; as &lt;code&gt;&amp;gt;=18&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Permission to run &lt;code&gt;npx&lt;/code&gt; and download a public npm package the first time the server starts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The plugin and server are MIT-licensed. The plugin repository is public and non-archived, and its manifest identifies Fernando Paladini as the author.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the plugin
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;

&lt;p&gt;Open &lt;strong&gt;Cursor Settings&lt;/strong&gt;, choose &lt;strong&gt;Customize&lt;/strong&gt;, search for &lt;strong&gt;DevUtils MCP&lt;/strong&gt;, and select &lt;strong&gt;Install&lt;/strong&gt;. The repository also documents the alternative &lt;strong&gt;Add from GitHub&lt;/strong&gt; path with &lt;code&gt;paladini/devutils-cursor-plugin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After installation, enable the &lt;code&gt;devutils&lt;/code&gt; MCP server under &lt;strong&gt;Customize &amp;gt; MCPs&lt;/strong&gt;. The plugin is a distribution wrapper: its manifest describes the plugin, while &lt;code&gt;mcp.json&lt;/code&gt; tells the client which local command to start.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;

&lt;p&gt;Run the two commands documented by the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/plugin marketplace add paladini/devutils-cursor-plugin
/plugin install devutils-mcp@devutils-cursor-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plugin repository is responsible for installation and documentation. The MCP server repository remains the place for the utility implementation and tool behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Any MCP client
&lt;/h3&gt;

&lt;p&gt;If your client supports a manually configured stdio MCP server, use this configuration from the repository's current &lt;code&gt;mcp.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&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;"mcpServers"&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;span class="nl"&gt;"devutils"&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;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"devutils-mcp-server"&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;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;The &lt;code&gt;-y&lt;/code&gt; flag lets &lt;code&gt;npx&lt;/code&gt; proceed without an interactive install confirmation. For a reproducible release-oriented setup, pin the package explicitly instead:&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; devutils-mcp-server@1.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The unpinned command is the repository's current example. The pinned command targets the server's stable GitHub release &lt;code&gt;v1.1.0&lt;/code&gt;, which is also the current npm version checked for this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try a useful tool call
&lt;/h2&gt;

&lt;p&gt;Once the server is enabled, ask the assistant for a small operation with an inspectable result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use DevUtils to generate one UUID v4. Return only the UUID and the tool name.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server README lists &lt;code&gt;generate_uuid&lt;/code&gt; among its generator tools. You can also try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use DevUtils to validate this JSON and explain the error location:
{"name":"Ada","skills":["typescript",]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or ask for a deterministic transformation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use DevUtils to calculate the network, broadcast address, and host count for 10.0.0.0/24.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These prompts are deliberately narrow. They give you an easy way to distinguish an MCP tool result from an answer generated from the model's general knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the local transport
&lt;/h2&gt;

&lt;p&gt;You can verify that the pinned server starts and speaks MCP over standard input and output without opening Cursor. Send an MCP &lt;code&gt;initialize&lt;/code&gt; request to the command:&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;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke-test","version":"1.0"}}}'&lt;/span&gt; | npx &lt;span class="nt"&gt;-y&lt;/span&gt; devutils-mcp-server@1.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful response includes &lt;code&gt;serverInfo.name&lt;/code&gt; set to &lt;code&gt;devutils-mcp-server&lt;/code&gt;, &lt;code&gt;serverInfo.version&lt;/code&gt; set to &lt;code&gt;1.1.0&lt;/code&gt;, and a tools capability. The server writes a short startup message to stderr and returns the JSON-RPC response on stdout. That separation matters when an MCP client parses stdout as protocol messages.&lt;/p&gt;

&lt;p&gt;For a client-level check, open the MCP tools panel and confirm that &lt;code&gt;devutils&lt;/code&gt; is enabled. Then run one UUID or JSON validation request and save the returned value in your terminal or test notes. Repeating the same request should let you compare the tool output with a native library or a known test vector.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the wrapper is useful
&lt;/h2&gt;

&lt;p&gt;The plugin solves distribution rather than inventing a second utility implementation. Cursor and Claude Code users can install one named integration, while other MCP clients can use the same server configuration. The underlying server uses consistent names such as &lt;code&gt;hash_sha256&lt;/code&gt;, &lt;code&gt;json_validate&lt;/code&gt;, &lt;code&gt;jwt_decode&lt;/code&gt;, and &lt;code&gt;cidr_calculate&lt;/code&gt;, so an assistant can select a narrow operation instead of improvising a multi-step shell command.&lt;/p&gt;

&lt;p&gt;The project README lists 36 tools in eight groups: hash, encoding, generators, JWT, formatters, converters, network, and text. The server uses stdio transport and the package metadata identifies TypeScript, Node.js, the MCP SDK, &lt;code&gt;bcryptjs&lt;/code&gt;, &lt;code&gt;nanoid&lt;/code&gt;, and &lt;code&gt;zod&lt;/code&gt; as its implementation stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and limitations
&lt;/h2&gt;

&lt;p&gt;If the plugin does not appear, check that you installed the repository named &lt;code&gt;paladini/devutils-cursor-plugin&lt;/code&gt;, then restart or reload the client. If the server does not start, verify &lt;code&gt;node -v&lt;/code&gt;, run the pinned &lt;code&gt;npx&lt;/code&gt; command directly, and inspect stderr for npm or permission errors.&lt;/p&gt;

&lt;p&gt;The plugin does not make an MCP-incompatible client compatible. It also does not replace native libraries in application code. The server README explicitly recommends native libraries when you are writing regular programs or need extreme performance. MCP adds process and model-tool overhead in exchange for a stable tool contract that an assistant can call.&lt;/p&gt;

&lt;p&gt;The current plugin repository has no GitHub release object even though its manifest declares version 1.0.7. Treat that manifest version as the documented plugin version, not as proof of a tagged plugin release. The related server does have stable release &lt;code&gt;v1.1.0&lt;/code&gt;. Keep those two version surfaces separate when reporting an installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security and privacy boundaries
&lt;/h2&gt;

&lt;p&gt;The plugin privacy policy says that tool inputs are processed locally by the MCP server and that the author does not operate a cloud service or receive telemetry from the plugin. That is a useful boundary, but it is not a blanket security guarantee.&lt;/p&gt;

&lt;p&gt;The first &lt;code&gt;npx&lt;/code&gt; invocation can contact npm to download &lt;code&gt;devutils-mcp-server&lt;/code&gt;. Review the package source, lock down the version when your workflow requires it, and use your organization's npm controls if package downloads are restricted. Also remember that your AI client still receives the prompt and may decide which tool to call. Do not paste secrets into an assistant conversation merely because the utility itself runs locally.&lt;/p&gt;

&lt;p&gt;JWT decoding is not signature verification, and hashing is not encryption. A local tool can reduce accidental guessing, but it does not make sensitive data safe to disclose or prove that a token is trusted.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does the plugin send input to Fernando Paladini?
&lt;/h3&gt;

&lt;p&gt;The published privacy policy says no. The tool process runs locally, while npm may be contacted to download the package on first use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need Cursor to use the server?
&lt;/h3&gt;

&lt;p&gt;No. The server is intended for any MCP-compatible client. Cursor and Claude Code are the convenient plugin paths.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I use the plugin or manual configuration?
&lt;/h3&gt;

&lt;p&gt;Use the plugin when your client supports it and you want a guided install. Use manual configuration when you need explicit control over the command and version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is this a replacement for application code?
&lt;/h3&gt;

&lt;p&gt;No. It is an assistant-facing utility layer. Use a native library when your application needs direct calls, tests, or high throughput.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;DevUtils MCP gives Cursor and Claude Code a small, local toolbox with a reviewable installation path. Start with the plugin, verify one narrow tool call, and pin &lt;code&gt;devutils-mcp-server@1.1.0&lt;/code&gt; when release reproducibility matters. The useful habit is to keep the client integration, package download, local process, and sensitive input boundaries visible.&lt;/p&gt;

&lt;p&gt;What is the first repetitive developer utility you would rather delegate to a validated local MCP tool than ask an AI assistant to recreate?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;AI assistance disclosure:&lt;/strong&gt; This tutorial was researched and drafted with AI assistance. Repository files, package metadata, the stable server release, and the MCP initialize example were checked against primary sources before publication.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/paladini/devutils-cursor-plugin" rel="noopener noreferrer"&gt;DevUtils MCP plugin repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paladini/devutils-cursor-plugin/blob/master/mcp.json" rel="noopener noreferrer"&gt;Current plugin MCP configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paladini/devutils-cursor-plugin/blob/master/.cursor-plugin/plugin.json" rel="noopener noreferrer"&gt;Plugin manifest and declared version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paladini/devutils-cursor-plugin/blob/master/PRIVACY.md" rel="noopener noreferrer"&gt;Plugin privacy policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paladini/devutils-mcp-server/blob/main/README.md" rel="noopener noreferrer"&gt;DevUtils MCP Server README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paladini/devutils-mcp-server/blob/main/package.json" rel="noopener noreferrer"&gt;DevUtils MCP Server package metadata&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paladini/devutils-mcp-server/releases/tag/v1.1.0" rel="noopener noreferrer"&gt;Stable server release v1.1.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/devutils-mcp-server" rel="noopener noreferrer"&gt;Published npm package metadata&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mcp</category>
      <category>cursor</category>
      <category>tutorial</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Measure an AI Coding Harness from L0 to L4 with Harness Score</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Thu, 01 Oct 2026 12:36:22 +0000</pubDate>
      <link>https://dev.to/paladini/measure-an-ai-coding-harness-from-l0-to-l4-with-harness-score-2g89</link>
      <guid>https://dev.to/paladini/measure-an-ai-coding-harness-from-l0-to-l4-with-harness-score-2g89</guid>
      <description>&lt;p&gt;AI coding assistants can edit a repository quickly, but speed does not tell you whether the repository can catch a bad edit. A project with no instructions, tests, or CI may produce a plausible patch and still leave every important check to chance.&lt;/p&gt;

&lt;p&gt;This tutorial shows how to measure that surrounding system with &lt;a href="https://github.com/paladini/harness-score" rel="noopener noreferrer"&gt;Harness Score&lt;/a&gt; and use a small open-source lab to improve it step by step. The result is not a quality certificate. It is a deterministic checklist of repository evidence: context files, scoped rules, skills, hooks, sensors, CI, and hygiene.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a disposable copy of the &lt;a href="https://github.com/paladini/harness-score-tutorial" rel="noopener noreferrer"&gt;Harness Score tutorial&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npx --yes harness-score .&lt;/code&gt; and record the initial level and score.&lt;/li&gt;
&lt;li&gt;Improve one maturity layer at a time, checking the diff after each agent task.&lt;/li&gt;
&lt;li&gt;Treat the score as a diagnostic and gate, not as proof that the application is correct.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;The tutorial documents Git, Node.js 24 or newer, and an AI coding agent that can edit a local repository. GitHub CLI is optional if you want to create a private or public copy from the template. The scanner package itself declares Node.js &lt;code&gt;&amp;gt;=18&lt;/code&gt; in its npm metadata, but following the lab's Node.js 24 prerequisite keeps the exercise aligned with its current README.&lt;/p&gt;

&lt;p&gt;No API key is required: Harness Score is designed to make zero LLM calls and zero network requests while scanning a repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with an isolated copy
&lt;/h2&gt;

&lt;p&gt;The lab is a GitHub template rather than a finished application. That is intentional. You create the small Meeting Cost CLI during the first stage, so the harness improvements remain visible instead of being hidden inside an already-complete project.&lt;/p&gt;

&lt;p&gt;Using GitHub CLI, create a private copy and clone it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh repo create my-harness-lab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template&lt;/span&gt; paladini/harness-score-tutorial &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--private&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--clone&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;my-harness-lab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do not want to create a remote repository, clone the public template locally and remove its origin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/paladini/harness-score-tutorial.git my-harness-lab
&lt;span class="nb"&gt;cd &lt;/span&gt;my-harness-lab
git remote remove origin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The template README is written in Portuguese, but the commands and paths are ordinary Git, Node.js, and Harness Score workflows. The prompts work with different coding agents. Inspect every diff yourself and do not allow an agent to commit automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Establish a baseline
&lt;/h2&gt;

&lt;p&gt;Run the exact current command documented by the template and the scanner project:&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;--yes&lt;/span&gt; harness-score &lt;span class="nt"&gt;--version&lt;/span&gt;
npx &lt;span class="nt"&gt;--yes&lt;/span&gt; harness-score &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current published package is &lt;code&gt;1.7.5&lt;/code&gt; and the current template clone is expected to start at L0, because it contains the tutorial and license but not the application harness. In a clean clone of the template, the scanner reported &lt;code&gt;17/108&lt;/code&gt; and &lt;code&gt;L0 - Unharnessed&lt;/code&gt; on October 1, 2026.&lt;/p&gt;

&lt;p&gt;That number belongs to one commit and scanner version, not a permanent promise. The tutorial runs an unpinned command, so record the tool version beside every baseline.&lt;/p&gt;

&lt;p&gt;For machine-readable evidence, add &lt;code&gt;--json&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;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; harness-score &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; baseline.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep that report outside the repository if you do not want it to affect future scans. The JSON report includes the maturity level, earned points, dimensions, checks, evidence, and remediation links.&lt;/p&gt;

&lt;h2&gt;
  
  
  Climb the maturity ladder
&lt;/h2&gt;

&lt;p&gt;Run one prompt, inspect the resulting files, execute local checks, scan the repository, and commit a checkpoint only after review.&lt;/p&gt;

&lt;h3&gt;
  
  
  L0 to L1: give the agent durable context
&lt;/h3&gt;

&lt;p&gt;Start by creating the smallest functional Meeting Cost CLI. The application accepts participants, meeting minutes, and hourly labor cost, then calculates the total. Keep the domain calculation separate from terminal argument handling and use Node.js built-ins only.&lt;/p&gt;

&lt;p&gt;After confirming that the command works, add a substantive root &lt;code&gt;AGENTS.md&lt;/code&gt;. It should describe the real files, commands, domain invariants, error handling, and security boundaries. Do not add future-stage artifacts early. A long file is not automatically useful: the scanner can detect that a context file is present and substantive, but it cannot decide whether every rule is true.&lt;/p&gt;

&lt;p&gt;Run the scan again and inspect the failed checks. The next-level message is more useful than the headline score because it tells you which dimension is blocking progress.&lt;/p&gt;

&lt;h3&gt;
  
  
  L1 to L2: scope guidance and protect hygiene
&lt;/h3&gt;

&lt;p&gt;Move procedural guidance into artifacts that load when needed. The lab asks for a path-scoped rule, a reusable skill, and an explicit workflow. It also adds a &lt;code&gt;.gitignore&lt;/code&gt; and a lockfile.&lt;/p&gt;

&lt;p&gt;This separation matters. A root context file orients every session; a scoped rule applies to relevant paths; a skill packages a repeatable procedure. The hygiene checks then make local state and credentials less likely to enter a commit or an agent context.&lt;/p&gt;

&lt;p&gt;Do not treat a passing hygiene check as proof that a repository is safe. It only means the scanner found the expected structural evidence, such as ignored environment files and no credential signatures in harness files.&lt;/p&gt;

&lt;h3&gt;
  
  
  L2 to L3: add sensors and CI
&lt;/h3&gt;

&lt;p&gt;At this stage, add actual feedback: tests for valid and invalid calculations, a formatter, a linter, strict type checking, and a GitHub Actions workflow. The tutorial uses fixed development-tool versions for this stage and asks you to run the complete check command locally.&lt;/p&gt;

&lt;p&gt;The important design choice is independent feedback. A README claim that tests exist is weaker than a test file that runs. A local command is weaker than a CI job that repeats the test, lint, and type checks on every push or pull request. Harness Score checks for the presence and wiring of these sensors; you still need to review whether the tests cover meaningful behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  L3 to L4: close the loop with hooks
&lt;/h3&gt;

&lt;p&gt;The final stage adds two Cursor hooks: a gate hook that denies dangerous shell commands and a feedback hook that formats supported files after edits. The tutorial also adds tests for allowed, denied, and malformed hook payloads.&lt;/p&gt;

&lt;p&gt;Hooks are useful because they execute at a boundary where prose can be ignored. They are not a universal security boundary. Keep scripts local, review their inputs, fail safely on malformed payloads, and retain CI as an independent check. The scanner's L4 result means the expected hook configuration and evidence were detected. It does not prove that every possible destructive command is blocked.&lt;/p&gt;

&lt;p&gt;Finally, add a separate GitHub Actions workflow that runs &lt;code&gt;npx --yes harness-score . --min-level 4&lt;/code&gt; or the equivalent project action. The gate turns the maturity level into a regression check, so removing a hook or sensor becomes visible in review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify each transition
&lt;/h2&gt;

&lt;p&gt;Use the same small loop after every stage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff &lt;span class="nt"&gt;--stat&lt;/span&gt;
npm run check
npx &lt;span class="nt"&gt;--yes&lt;/span&gt; harness-score &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
git status &lt;span class="nt"&gt;--short&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command checks scope. The second checks product behavior and sensors once they exist. The third checks harness evidence. The last catches generated files or local state that should not be committed.&lt;/p&gt;

&lt;p&gt;If the score differs from the README's approximate table, compare the scanner version and inspect the JSON checks rather than adding decorative files to chase points.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and honest limits
&lt;/h2&gt;

&lt;p&gt;The scanner measures filesystem and configuration facts. It does not establish that the Meeting Cost CLI is commercially useful, that tests are comprehensive, that rules are correct, or that a team actually reviews pull requests. A high score means that more feedback and guardrails are present. It does not mean an agent can be trusted without human review.&lt;/p&gt;

&lt;p&gt;The score can change when the scanner's maturity model changes. Pin a version for release gates when stable comparisons matter. Do not compare scores from different versions without recording that difference.&lt;/p&gt;

&lt;p&gt;The tutorial itself is a template. Its prompts are guidance for an agent, not an automatic migration. Review changes, preserve the stated file boundaries, and keep credentials out of the repository. Read the &lt;a href="https://github.com/paladini/harness-score/blob/main/LICENSE" rel="noopener noreferrer"&gt;Harness Score license&lt;/a&gt; and the &lt;a href="https://github.com/paladini/harness-score-tutorial/blob/main/LICENSE" rel="noopener noreferrer"&gt;tutorial license&lt;/a&gt; before redistributing either project.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does Harness Score inspect prompts or call an LLM?
&lt;/h3&gt;

&lt;p&gt;No. The project describes its checks as deterministic filesystem facts and says the scanner makes zero LLM calls and zero network requests during a scan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need Cursor to complete the tutorial?
&lt;/h3&gt;

&lt;p&gt;No. The README says the prompts work with different coding agents. Cursor is used for the runtime hook example because its hook format is recognized by the scanner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I optimize for 108 out of 108?
&lt;/h3&gt;

&lt;p&gt;No. Use the failed checks to choose controls that fit your repository. A hook that is irrelevant to your threat model may add noise, while an untested deployment script may deserve attention even if it does not change the score.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;An AI coding harness is an engineering system around the model. Measure it from a known commit, improve one feedback layer at a time, and preserve evidence in code, tests, hooks, and CI. The useful outcome is not a magic number. It is a repository where an agent has clearer context, faster feedback, and fewer ways to make an unsafe change silently.&lt;/p&gt;

&lt;p&gt;What is the first missing harness layer in your repository today: durable context, scoped procedures, sensors, CI, or runtime guardrails?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI assistance disclosure: I used AI assistance to organize and edit this tutorial. The repository behavior, package metadata, current version, commands, and baseline scan were checked against the linked primary sources and a clean clone on October 1, 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devtools</category>
      <category>tutorial</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Curate Verifiable ActiveAdmin 4 Themes with Markdown and CI</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Wed, 30 Sep 2026 12:37:23 +0000</pubDate>
      <link>https://dev.to/paladini/curate-verifiable-activeadmin-4-themes-with-markdown-and-ci-390h</link>
      <guid>https://dev.to/paladini/curate-verifiable-activeadmin-4-themes-with-markdown-and-ci-390h</guid>
      <description>&lt;p&gt;When a Rails theme claims ActiveAdmin 4 support, the difficult part is often not finding a screenshot. It is checking whether the theme actually targets ActiveAdmin 4's Tailwind-based asset model, whether the installation commands are current, and whether the claim can be reviewed later.&lt;/p&gt;

&lt;p&gt;This tutorial shows how to build that review workflow with &lt;a href="https://github.com/paladini/activeadmin-v4-themes" rel="noopener noreferrer"&gt;activeadmin-v4-themes&lt;/a&gt;, an MIT-licensed directory that curates documented ActiveAdmin 4 themes. The repository stores documentation and links, not third-party theme code. You will inspect its evidence standard, add a candidate entry, and run the same kinds of local checks used by its CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Use a small Markdown repository as a compatibility index, but require evidence before adding a theme. For each candidate, record the public source URL, license, ActiveAdmin 4 version range, asset and styling requirements, installation commands, screenshots or demo, and known limitations. Then run link checking and Markdown linting before opening a pull request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;Node.js and &lt;code&gt;npx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A public theme repository to evaluate&lt;/li&gt;
&lt;li&gt;Enough familiarity with Rails and ActiveAdmin to understand its asset pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The directory itself is not a Ruby gem and does not install ActiveAdmin. Its current &lt;code&gt;main&lt;/code&gt; README describes a documentation workflow, and its &lt;code&gt;pyproject.toml&lt;/code&gt; equivalent does not exist because the project has no package manifest. The repository currently has no versioned release, so the commands below target the current &lt;code&gt;main&lt;/code&gt; documentation checked on September 30, 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Clone the directory and inspect the standard
&lt;/h2&gt;

&lt;p&gt;Start from the public repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/paladini/activeadmin-v4-themes.git
&lt;span class="nb"&gt;cd &lt;/span&gt;activeadmin-v4-themes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current README defines a narrow scope: entries must have documented, verifiable ActiveAdmin 4 support. Legacy ActiveAdmin 3 and SCSS-only themes do not belong in the main list. That distinction is important because a visual match does not prove compatibility with the newer Tailwind-based interface.&lt;/p&gt;

&lt;p&gt;Read the contributor checklist before evaluating a project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;less CONTRIBUTING.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\CONTRIBUTING.md&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contribution guide asks you to confirm public source code and an open-source license, verify the ActiveAdmin 4 version range, check the asset and styling model, and document installation, screenshots, and limitations. It also says not to copy third-party source code or screenshots into the directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Turn a compatibility claim into evidence
&lt;/h2&gt;

&lt;p&gt;Before writing a README entry, inspect the theme's own primary sources. A useful evidence record answers five questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Where is the public source repository?&lt;/li&gt;
&lt;li&gt;Which license covers the theme?&lt;/li&gt;
&lt;li&gt;Which ActiveAdmin 4 version or range is named?&lt;/li&gt;
&lt;li&gt;Does the installation path use the ActiveAdmin 4 asset and styling model?&lt;/li&gt;
&lt;li&gt;Is there a test, demo, screenshot, or maintainer statement that supports the claim?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The repository's issue template turns those questions into required fields. Open the template in your browser or inspect it locally:&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;cat&lt;/span&gt; .github/ISSUE_TEMPLATE/new-theme.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The required fields include the project URL, ActiveAdmin version support, compatibility evidence, and exact installation commands. This is a practical design choice: a maintainer can review structured evidence before editing the curated list.&lt;/p&gt;

&lt;p&gt;For example, the current entry for &lt;a href="https://github.com/paladini/activeadmin-claude-theme" rel="noopener noreferrer"&gt;activeadmin-claude-theme&lt;/a&gt; names ActiveAdmin &lt;code&gt;4.0.0.beta22+&lt;/code&gt;, Tailwind CSS v4, and a Rails Engine generator. Its installation snippet is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"activeadmin-claude-theme"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle &lt;span class="nb"&gt;install
&lt;/span&gt;rails generate activeadmin_claude_theme:install
npm run build:css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entry also states that the application must already use ActiveAdmin 4 with Tailwind v4 assets. That limitation belongs beside the install commands because it changes whether a reader can use the theme in an existing Rails app.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Add a concise, reviewable entry
&lt;/h2&gt;

&lt;p&gt;The directory uses ordinary Markdown rather than a custom database. A new entry should be short enough to scan but detailed enough to verify. Give it a descriptive heading, a one-sentence summary, and a small table with these fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project and canonical source URL&lt;/li&gt;
&lt;li&gt;License&lt;/li&gt;
&lt;li&gt;ActiveAdmin version range&lt;/li&gt;
&lt;li&gt;Styling and asset model&lt;/li&gt;
&lt;li&gt;Rails integration method&lt;/li&gt;
&lt;li&gt;Verification status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow the table with an &lt;code&gt;Install&lt;/code&gt; heading containing the exact commands, then a &lt;code&gt;Known limitations&lt;/code&gt; heading. For example, the current entry uses &lt;code&gt;bundle install&lt;/code&gt;, &lt;code&gt;rails generate activeadmin_claude_theme:install&lt;/code&gt;, and &lt;code&gt;npm run build:css&lt;/code&gt; as separate commands rather than hiding setup in prose.&lt;/p&gt;

&lt;p&gt;Keep the links pointed at the original project. The directory's purpose is to make compatibility easier to inspect, not to become a mirror of someone else's code.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Run reproducible checks before review
&lt;/h2&gt;

&lt;p&gt;The contributor guide documents the local link check:&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;--yes&lt;/span&gt; markdown-link-check README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository's GitHub Actions workflow runs that check together with Markdown linting. You can run both locally with:&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;--yes&lt;/span&gt; markdown-link-check README.md
npx &lt;span class="nt"&gt;--yes&lt;/span&gt; markdownlint-cli2 &lt;span class="s2"&gt;"**/*.md"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the current checkout, the link checker reported 22 successful links and the Markdown linter reported zero issues across five Markdown files. Those results are verification of the directory snapshot, not proof that every listed theme works in every Rails application.&lt;/p&gt;

&lt;p&gt;If a link check fails, fix the source URL or remove the claim. Do not replace a dead primary source with a search result or an unrelated mirror. If the theme's ActiveAdmin 4 support cannot be demonstrated, leave it outside the curated list and explain what evidence is missing in the issue or review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this works
&lt;/h2&gt;

&lt;p&gt;Compatibility directories become useful when they separate discovery from certification. A reader can discover a candidate from the list, then follow the project link to inspect its own code and installation guide. The directory adds a second, reviewable layer: version claims, asset assumptions, screenshots or demos, and limitations are visible in one place.&lt;/p&gt;

&lt;p&gt;The repository also keeps its security boundary small. Its security policy says it contains documentation and links to external projects. It does not ship third-party theme code or application credentials. That means a contribution cannot silently add a copied dependency or a secret-bearing example to the index.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and honest limits
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A screenshot looks right, but the integration is legacy
&lt;/h3&gt;

&lt;p&gt;A polished screenshot does not establish ActiveAdmin 4 support. Check whether the project documents the ActiveAdmin 4 Tailwind architecture rather than only importing the classic &lt;code&gt;active_admin/base&lt;/code&gt; SCSS stylesheet.&lt;/p&gt;

&lt;h3&gt;
  
  
  The version claim is too broad
&lt;/h3&gt;

&lt;p&gt;Write the narrowest range supported by primary evidence. The current directory notes that ActiveAdmin 4 is still a beta series and tells readers to confirm the upstream compatibility range before production use. Treat that as a compatibility warning, not as a promise of production readiness.&lt;/p&gt;

&lt;h3&gt;
  
  
  The install command hides prerequisites
&lt;/h3&gt;

&lt;p&gt;Commands such as &lt;code&gt;rails generate&lt;/code&gt; may assume an existing Rails application, ActiveAdmin, CSS bundling, import maps, or a specific Node setup. Put those prerequisites in the entry instead of presenting a partial command as a complete installation.&lt;/p&gt;

&lt;h3&gt;
  
  
  A listed theme changes after review
&lt;/h3&gt;

&lt;p&gt;The directory is a snapshot, not a live compatibility oracle. Recheck the source repository, release notes, tests, and installation guide when upgrading ActiveAdmin or choosing a theme for a real application.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does this repository install themes?
&lt;/h3&gt;

&lt;p&gt;No. It curates documentation and links. Installation remains the responsibility of each theme's own repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can an ActiveAdmin 3 theme be listed?
&lt;/h3&gt;

&lt;p&gt;Not in the main ActiveAdmin 4 list unless its current documentation verifies ActiveAdmin 4 compatibility. The README explicitly excludes legacy ActiveAdmin 3 and SCSS-only themes from that scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Markdown linting enough to approve a theme?
&lt;/h3&gt;

&lt;p&gt;No. Linting checks document structure. It does not prove license ownership, version compatibility, asset behavior, or security properties. Those require primary-source review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;A small Markdown directory can provide real engineering value when every entry has a clear evidence threshold. For ActiveAdmin themes, verify the version, asset model, installation path, and limitations first. Then keep the record concise, link to the original project, and run link and Markdown checks before review.&lt;/p&gt;

&lt;p&gt;This article was prepared with AI assistance for research organization and drafting. Repository facts, commands, and validation results were checked against the current public project sources and a local checkout.&lt;/p&gt;

&lt;p&gt;What evidence would you require before trusting a compatibility directory for another Rails ecosystem?&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>tutorial</category>
      <category>activeadmin</category>
    </item>
    <item>
      <title>Run Private Audio Transcription Locally with EchoTranscribe</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Wed, 30 Sep 2026 02:54:54 +0000</pubDate>
      <link>https://dev.to/paladini/run-private-audio-transcription-locally-with-echotranscribe-4lnk</link>
      <guid>https://dev.to/paladini/run-private-audio-transcription-locally-with-echotranscribe-4lnk</guid>
      <description>&lt;p&gt;Sending a meeting recording to a hosted transcription service is convenient, but it also creates a data-handling decision before you have even inspected the transcript. For interviews, internal meetings, research recordings, or voice notes, a local workflow can be a better starting point.&lt;/p&gt;

&lt;p&gt;This tutorial uses &lt;a href="https://github.com/paladini/echo-transcribe" rel="noopener noreferrer"&gt;EchoTranscribe&lt;/a&gt;, an MIT-licensed desktop application that combines a Tauri interface with a FastAPI backend and local Whisper inference. You will install the stable &lt;code&gt;v0.1.1&lt;/code&gt; release, transcribe one file, choose a model, export the result, and verify what “local” means in the implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Download the installer for &lt;a href="https://github.com/paladini/echo-transcribe/releases/tag/v0.1.1" rel="noopener noreferrer"&gt;EchoTranscribe v0.1.1&lt;/a&gt;, start the application, select an audio file, choose the &lt;code&gt;base&lt;/code&gt; model, and export TXT, SRT, or JSON. The first transcription downloads the selected model. The repository source binds its backend to &lt;code&gt;127.0.0.1&lt;/code&gt;, but the application is still experimental software, not a hardened production service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;For the release path, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows 10 or later, macOS, or a Linux distribution supported by the published package.&lt;/li&gt;
&lt;li&gt;Enough disk space for the application and a Whisper model. The README lists approximate model sizes from 39 MB for &lt;code&gt;tiny&lt;/code&gt; to 769 MB for &lt;code&gt;medium&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;An audio file in MP3, WAV, FLAC, M4A, OGG, or WebM format.&lt;/li&gt;
&lt;li&gt;Internet access for the first model download. Later runs can reuse a model already stored locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project publishes an MSI and an EXE installer for Windows, DMG files for macOS, and AppImage, DEB, and RPM packages for Linux in the &lt;a href="https://github.com/paladini/echo-transcribe/releases/tag/v0.1.1" rel="noopener noreferrer"&gt;v0.1.1 release assets&lt;/a&gt;. Pick the asset that matches your operating system and CPU architecture.&lt;/p&gt;

&lt;p&gt;If you want to run from source instead, the stable README lists Node.js 18 or later, Python 3.8 or later, Rust, and platform-specific Tauri prerequisites. That is a development path, not a requirement for using a release installer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the stable release
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://github.com/paladini/echo-transcribe/releases/tag/v0.1.1" rel="noopener noreferrer"&gt;v0.1.1 release page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Download the installer for your platform.&lt;/li&gt;
&lt;li&gt;Install and launch EchoTranscribe.&lt;/li&gt;
&lt;li&gt;If your platform asks for permission to open an application downloaded from the internet, confirm that you obtained the file from the project's GitHub release page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The release page is important here because the repository's default branch can change independently of the last packaged release. The commands and behavior in this article target &lt;code&gt;v0.1.1&lt;/code&gt;, which is the published stable release inspected for this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transcribe a file
&lt;/h2&gt;

&lt;p&gt;The interface is intentionally small. Use this sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select one or more audio files. The README documents a maximum of 10 files in one batch.&lt;/li&gt;
&lt;li&gt;Choose a model.&lt;/li&gt;
&lt;li&gt;Leave automatic language detection enabled unless you know the language and want to select it explicitly.&lt;/li&gt;
&lt;li&gt;Start transcription.&lt;/li&gt;
&lt;li&gt;Review the text and timestamps.&lt;/li&gt;
&lt;li&gt;Export the result as TXT, SRT, or JSON.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a first run, start with &lt;code&gt;tiny&lt;/code&gt; or &lt;code&gt;base&lt;/code&gt;. The project describes &lt;code&gt;tiny&lt;/code&gt; as faster with lower precision and &lt;code&gt;base&lt;/code&gt; as a balance between speed and precision. &lt;code&gt;small&lt;/code&gt; and &lt;code&gt;medium&lt;/code&gt; may improve recognition for some material but require more resources and take longer. These are model tradeoffs, not guarantees for every recording.&lt;/p&gt;

&lt;p&gt;The first run may take longer because EchoTranscribe downloads the chosen model. The source stores models under &lt;code&gt;.echo-transcribe/models&lt;/code&gt; in the user's home directory. The README documents the corresponding Windows location as &lt;code&gt;%USERPROFILE%\\.echo-transcribe\\models\&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Expected result
&lt;/h3&gt;

&lt;p&gt;After processing, you should see the transcription in the application, with word-level timestamps when the backend returns them. An export should contain the selected format's text or timing data. The exact recognition quality depends on the recording, language, model, noise, and speaker overlap, so inspect the output before treating it as a source of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the local backend
&lt;/h2&gt;

&lt;p&gt;The desktop interface starts a Python backend. The stable source exposes a health endpoint and API documentation while the backend is running. If you are developing from a checkout, start the backend from the repository's backend directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;src-tauri/backend&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main.py&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source searches for an available port starting at &lt;code&gt;8000&lt;/code&gt; and reports the selected URL in its logs. When it starts on the default port, verify the service with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Invoke-RestMethod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http://127.0.0.1:8000/health&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should receive a JSON response whose &lt;code&gt;status&lt;/code&gt; is &lt;code&gt;healthy&lt;/code&gt;. The API documentation is available at &lt;code&gt;http://127.0.0.1:8000/docs&lt;/code&gt; when that port is selected. The backend code binds its port search to the loopback address, and its CORS configuration names the local frontend origins. That is useful evidence about the intended boundary, but it is not a security certification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run from source when you need development mode
&lt;/h2&gt;

&lt;p&gt;Clone the exact stable tag instead of silently using an unreleased default branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--branch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v0.1.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://github.com/paladini/echo-transcribe.git&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;echo-transcribe&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;npm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ci&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frontend scripts are documented in the stable &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;npm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;npm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tauri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice, treat this as a development workflow. I verified &lt;code&gt;npm ci&lt;/code&gt; and Python bytecode compilation on the &lt;code&gt;v0.1.1&lt;/code&gt; checkout. The frontend build did not complete with Node.js 24 and the dependency range resolved by the lockfile because the installed TypeScript reports that the configured &lt;code&gt;baseUrl&lt;/code&gt; option has been removed. That is a reproducible toolchain limitation, not evidence that the packaged release is unusable. Use the published installer for the shortest path, or pin and test a compatible development toolchain before changing the project configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What stays local, and what does not
&lt;/h2&gt;

&lt;p&gt;The repository's architecture supports a local processing workflow: the application sends files to its local FastAPI backend, and the backend uses &lt;code&gt;faster-whisper&lt;/code&gt; to load models and transcribe them. The source writes temporary files under &lt;code&gt;.echo-transcribe/temp&lt;/code&gt; and removes them after processing or shutdown.&lt;/p&gt;

&lt;p&gt;There are still important boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first model download needs network access. Local inference does not mean the initial model acquisition is offline.&lt;/li&gt;
&lt;li&gt;A local HTTP service is not automatically safe to expose beyond the host. Do not forward its port, bind it to a public interface, or place it behind a proxy without adding authentication, transport security, input limits, and a review of the API.&lt;/li&gt;
&lt;li&gt;Audio files and generated transcripts may remain in application, download, temporary, or operating-system locations depending on the workflow. Review and delete them according to your retention policy.&lt;/li&gt;
&lt;li&gt;The project accepts multiple file formats, but malformed or unusual media can still fail. Keep the original recording and verify important transcripts manually.&lt;/li&gt;
&lt;li&gt;EchoTranscribe is MIT licensed, but the license does not provide a guarantee of accuracy, availability, compliance, or fitness for a particular workload.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These constraints are why “runs locally” is a useful architectural description, not a complete threat model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting checklist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The application cannot load the backend
&lt;/h3&gt;

&lt;p&gt;Check whether the backend is running and whether the selected port is already occupied. The README specifically points to the local API and recommends checking port &lt;code&gt;8000&lt;/code&gt;. Starting the backend manually can reveal missing Python dependencies or a port selection in the logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  The model cannot be found
&lt;/h3&gt;

&lt;p&gt;Allow the first model download to finish and check your network connection. If you moved or removed the user's &lt;code&gt;.echo-transcribe/models&lt;/code&gt; directory, the application may need to download the model again.&lt;/p&gt;

&lt;h3&gt;
  
  
  The result is poor
&lt;/h3&gt;

&lt;p&gt;Try a larger model, reduce background noise, check the selected language, and review timestamps around speaker changes. Larger models use more resources and are not guaranteed to fix every recording.&lt;/p&gt;

&lt;h3&gt;
  
  
  The frontend build fails
&lt;/h3&gt;

&lt;p&gt;Separate packaged use from source development. Confirm the checkout is &lt;code&gt;v0.1.1&lt;/code&gt;, inspect the installed Node.js and TypeScript versions, and reproduce the error before changing &lt;code&gt;tsconfig.json&lt;/code&gt; or dependency ranges. Do not claim a successful build when only the backend compiled.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does EchoTranscribe send audio to a cloud API?
&lt;/h3&gt;

&lt;p&gt;The documented design uses local Whisper inference through its Python backend. The first model download still uses the network, and the source should be reviewed before making stronger privacy claims for a specific deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I process several files?
&lt;/h3&gt;

&lt;p&gt;Yes. The README documents batch transcription with up to 10 files per request, subject to available CPU, memory, and model resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which model should I choose?
&lt;/h3&gt;

&lt;p&gt;Start with &lt;code&gt;base&lt;/code&gt; for a general test. Use &lt;code&gt;tiny&lt;/code&gt; when iteration speed matters, then compare a larger model on a representative recording if the text needs improvement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is this a production transcription service?
&lt;/h3&gt;

&lt;p&gt;No conclusion like that follows from the README or a local smoke test. Treat it as a self-hosted open-source desktop application and evaluate reliability, resource use, data retention, and security for your own context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;EchoTranscribe gives you a practical local transcription path: install a tagged release, keep model downloads explicit, process audio on the same computer, and export a reviewable result. The strongest habit is to verify the boundary yourself: inspect the release, check the loopback backend, and keep the development build limitations separate from the packaged application.&lt;/p&gt;

&lt;p&gt;This article was prepared with AI assistance for research organization, drafting, and checklist review. The repository, release metadata, source examples, and local validation results were checked against the linked primary sources.&lt;/p&gt;

&lt;p&gt;What would you verify first before trusting a local transcription workflow with sensitive recordings: the model files, the network boundary, or the retention behavior of exported transcripts?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>tutorial</category>
      <category>transcription</category>
    </item>
    <item>
      <title>Build and Test an AI Agent Skill with SKILL.md and Python</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Sat, 19 Sep 2026 12:37:02 +0000</pubDate>
      <link>https://dev.to/paladini/build-and-test-an-ai-agent-skill-with-skillmd-and-python-4kn1</link>
      <guid>https://dev.to/paladini/build-and-test-an-ai-agent-skill-with-skillmd-and-python-4kn1</guid>
      <description>&lt;p&gt;AI agents are good at interpreting intent, but they are not a reliable place to hide every rule in a workflow. If an agent must count characters, parse a file, or refuse to overwrite an existing artifact, prose instructions alone make the result harder to verify.&lt;/p&gt;

&lt;p&gt;An Agent Skill gives that workflow a reusable home. The skill describes when it should activate and how the agent should reason. Small scripts handle repeatable checks. Tests protect the behavior when the skill changes.&lt;/p&gt;

&lt;p&gt;This tutorial builds a small &lt;code&gt;commit-crafter&lt;/code&gt; skill from the public &lt;a href="https://github.com/paladini/how-to-create-a-skill-tutorial" rel="noopener noreferrer"&gt;how-to-create-a-skill-tutorial repository&lt;/a&gt;. The project is MIT licensed and documents the open &lt;a href="https://agentskills.io/specification" rel="noopener noreferrer"&gt;Agent Skills specification&lt;/a&gt;. The repository's stable documentation describes the same folder structure and examples used here.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Create a folder with a &lt;code&gt;SKILL.md&lt;/code&gt;, put deterministic validation in &lt;code&gt;scripts/&lt;/code&gt;, keep long references in &lt;code&gt;references/&lt;/code&gt;, and test the script independently. The model chooses and explains; the script verifies and computes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.9 or newer&lt;/li&gt;
&lt;li&gt;An agent that discovers skills from a supported skills directory&lt;/li&gt;
&lt;li&gt;A Git repository with staged changes if you want to try the commit workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The example repository tests its skills with Python's standard library. You do not need an API key, a hosted model, or a third-party Python package for the minimal path.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Create the skill structure
&lt;/h2&gt;

&lt;p&gt;The specification requires &lt;code&gt;SKILL.md&lt;/code&gt;. The other directories are conventions that keep a skill maintainable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit-crafter/
|-- SKILL.md
|-- scripts/
|   `-- check_message.py
`-- references/
    `-- conventional-commits.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a project-local skill directory on macOS or Linux:&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .agents/skills/commit-crafter/scripts
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .agents/skills/commit-crafter/references
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows PowerShell, use the equivalent commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;New-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Directory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;agents&lt;/span&gt;&lt;span class="nx"&gt;\skills\commit-crafter\scripts&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;New-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Directory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;agents&lt;/span&gt;&lt;span class="nx"&gt;\skills\commit-crafter\references&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project-local location is useful when the skill belongs to one repository. A user-level location such as &lt;code&gt;~/.agents/skills/commit-crafter&lt;/code&gt; is better when you want the same skill available across projects. Check your agent's discovery rules before installing it globally.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Write a triggerable SKILL.md
&lt;/h2&gt;

&lt;p&gt;The front matter is not decoration. The agent may read only the &lt;code&gt;description&lt;/code&gt; while deciding whether to activate the skill, so describe both the job and the phrases that should trigger it.&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="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;commit-crafter&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Write git commit messages from staged changes following Conventional Commits. Use when the user asks to commit, asks for a commit message, wants to fix or improve a commit message, or mentions conventional commits.&lt;/span&gt;
&lt;span class="na"&gt;license&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MIT&lt;/span&gt;
&lt;span class="na"&gt;compatibility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Requires git and Python 3.9+&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Commit Crafter&lt;/span&gt;

Write a Conventional Commit message from the staged diff. The user reviews
and approves the message before anything is committed.

&lt;span class="gu"&gt;## Workflow&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Run &lt;span class="sb"&gt;`git status`&lt;/span&gt; and &lt;span class="sb"&gt;`git diff --staged`&lt;/span&gt;.
&lt;span class="p"&gt;2.&lt;/span&gt; Classify the change and whether it is breaking.
&lt;span class="p"&gt;3.&lt;/span&gt; Draft the message in the format &lt;span class="sb"&gt;`type(scope): imperative summary`&lt;/span&gt;.
&lt;span class="p"&gt;4.&lt;/span&gt; Validate it with &lt;span class="sb"&gt;`python scripts/check_message.py --file message.txt`&lt;/span&gt;.
&lt;span class="p"&gt;5.&lt;/span&gt; Show the result. Commit only after explicit approval.

&lt;span class="gu"&gt;## Rules&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Never stage files yourself.
&lt;span class="p"&gt;-&lt;/span&gt; Never commit secrets or unrelated files.
&lt;span class="p"&gt;-&lt;/span&gt; Never add an AI attribution footer unless requested.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the boundary around &lt;code&gt;git commit&lt;/code&gt;. The skill can prepare and validate a message without silently changing repository history. That is a better default for an agent workflow that may be invoked from an ambiguous prompt.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;name&lt;/code&gt; must match the parent directory and use lowercase letters, numbers, and single hyphens. Keep the body short enough to load comfortably. Move detailed rules and examples into &lt;code&gt;references/&lt;/code&gt; and link to them one level deep.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Move stable rules into a deterministic script
&lt;/h2&gt;

&lt;p&gt;The tutorial's central design rule is simple: let the model decide, let the script verify, and let the script crunch. A model can choose whether a change is a feature or a fix. A Python script can enforce the subject format every time.&lt;/p&gt;

&lt;p&gt;The repository's &lt;code&gt;check_message.py&lt;/code&gt; validates the Conventional Commits shape, allowed types, subject length, imperative mood warnings, blank-line separation, and breaking-change footers. It uses exit codes as a protocol:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt; means the input is valid&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt; means validation found a rule violation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2&lt;/code&gt; means there was no input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run it against a candidate message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python scripts/check_message.py &lt;span class="nt"&gt;--file&lt;/span&gt; message.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script prints actionable violations to stderr. For example, it can report that a subject is too long or that a breaking marker lacks a &lt;code&gt;BREAKING CHANGE:&lt;/code&gt; footer. The agent can read that output, revise the draft, and run the same check again.&lt;/p&gt;

&lt;p&gt;That feedback loop is more robust than adding another paragraph of instructions. It also makes the rule testable without an agent in the loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Test the script before installing the skill
&lt;/h2&gt;

&lt;p&gt;The example skill includes tests beside the script. Run them from the repository root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python examples/commit-crafter/tests/test_check_message.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The public repository's current test run completed successfully for the example suites. The &lt;code&gt;commit-crafter&lt;/code&gt; suite covers valid messages, unknown types, subject limits, breaking changes, body formatting, and warning behavior.&lt;/p&gt;

&lt;p&gt;Add a regression test whenever a real prompt exposes a failure. A useful test is not a copy of one successful output. It is a small input that would have caused the agent to make the wrong decision before the fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Try the skill with realistic prompts
&lt;/h2&gt;

&lt;p&gt;Trigger quality is part of the implementation. Test both positive and negative cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"commit this"                      -&amp;gt; should trigger
"write a good commit message"      -&amp;gt; should trigger
"reword my last commit"            -&amp;gt; should trigger
"what does the staged diff do?"     -&amp;gt; should not trigger
"create a git branch"               -&amp;gt; should not trigger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the skill fails to activate, improve the description with the user's vocabulary. If it activates for unrelated requests, narrow the wording. This is a practical evaluation of the trigger, not a promise that every agent implements discovery identically.&lt;/p&gt;

&lt;p&gt;When the skill does activate, inspect whether it follows the important boundaries: it should inspect staged changes, never stage files on its own, run the validator, and wait for approval before committing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this structure works
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;SKILL.md&lt;/code&gt; is the workflow contract. &lt;code&gt;scripts/&lt;/code&gt; is the deterministic execution layer. &lt;code&gt;references/&lt;/code&gt; is progressive disclosure for material that would make the main instructions noisy. &lt;code&gt;assets/&lt;/code&gt; can hold templates or fixtures when a skill needs them.&lt;/p&gt;

&lt;p&gt;This separation also helps with token use. The agent does not need to reconstruct a parser from prose or load every example before starting. It can read the short workflow, run a local script, and consult a reference only when the situation requires it.&lt;/p&gt;

&lt;p&gt;The repository extends the same pattern with &lt;code&gt;changelog-forge&lt;/code&gt;, &lt;code&gt;lessons-keeper&lt;/code&gt;, and &lt;code&gt;skill-starter&lt;/code&gt;. Those examples demonstrate deterministic Git parsing, idempotent memory writes, and a copy-paste scaffold.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and security boundaries
&lt;/h2&gt;

&lt;p&gt;A skill is executable code with the permissions of the agent. Read scripts before installing a third-party skill. Treat network calls, credential access, shell pipelines, and automatic writes as explicit capabilities that need a reason and a documented boundary.&lt;/p&gt;

&lt;p&gt;Common problems include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A vague description that never triggers, or triggers for unrelated requests.&lt;/li&gt;
&lt;li&gt;Instructions that claim a validation rule but have no executable check.&lt;/li&gt;
&lt;li&gt;A script that appends repeatedly when retried instead of being idempotent.&lt;/li&gt;
&lt;li&gt;Unix-only path assumptions in a skill advertised for Windows.&lt;/li&gt;
&lt;li&gt;A skill that commits, publishes, or reads secrets without an explicit user decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tutorial's examples prefer Python's standard library, bounded output, explicit exit codes, and local execution. These choices reduce dependencies, but they do not make an arbitrary skill automatically safe. Review the code and its compatibility requirements before installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is a skill the same as an MCP server?
&lt;/h3&gt;

&lt;p&gt;No. A skill is an instruction and resource bundle discovered by an agent. MCP is a protocol for exposing tools and data. A skill can tell an agent when to use an MCP tool, but the two solve different problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should every skill include a script?
&lt;/h3&gt;

&lt;p&gt;No. Use a script when a stable rule benefits from exact, repeatable behavior. A small explanatory workflow may only need &lt;code&gt;SKILL.md&lt;/code&gt; and a reference file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can the skill guarantee the agent will follow it?
&lt;/h3&gt;

&lt;p&gt;No. It can improve activation, make important rules explicit, and provide executable checks. The host agent still controls discovery and execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where should I install it?
&lt;/h3&gt;

&lt;p&gt;Use a project-local &lt;code&gt;.agents/skills/&lt;/code&gt; path for repository-specific behavior. Use a user-level path only after reviewing the skill and confirming how your agent discovers global skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;An effective Agent Skill is a small, reviewable software project: a precise trigger, a short workflow, deterministic scripts, references loaded on demand, and tests that reproduce failures. Start with one narrow task such as commit-message drafting, then add automation only where the boundary and verification are clear.&lt;/p&gt;

&lt;p&gt;What is one repetitive agent workflow in your project that would benefit from a deterministic check before the agent is allowed to continue?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI assistance disclosure: This tutorial was researched and drafted with AI assistance. Repository behavior, commands, version information, license, and test claims were checked against the public project sources before publication.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>tutorial</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>Publish a Ghost Blog on GitHub Pages with the v3 Release</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Fri, 18 Sep 2026 12:35:17 +0000</pubDate>
      <link>https://dev.to/paladini/publish-a-ghost-blog-on-github-pages-with-the-v3-release-4a0f</link>
      <guid>https://dev.to/paladini/publish-a-ghost-blog-on-github-pages-with-the-v3-release-4a0f</guid>
      <description>&lt;p&gt;Ghost is a pleasant writing environment, but running a dynamic CMS and paying for hosting is more than every personal blog needs. The &lt;code&gt;ghost-on-github-pages&lt;/code&gt; project takes a different route: write in a local Ghost installation, generate static files, and push those files to a public GitHub repository served by GitHub Pages.&lt;/p&gt;

&lt;p&gt;This tutorial follows the project's stable &lt;code&gt;v3.0.0&lt;/code&gt; release. It covers the smallest useful path from an empty machine to a published static blog, then explains what the scripts actually do and where their boundaries are.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;On macOS, Linux, or WSL, install Node.js LTS and &lt;code&gt;wget&lt;/code&gt;, download the &lt;a href="https://github.com/paladini/ghost-on-github-pages/releases/tag/v3.0.0" rel="noopener noreferrer"&gt;v3.0.0 release&lt;/a&gt;, and run:&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;chmod&lt;/span&gt; +x install.sh
./install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The installer creates &lt;code&gt;~/.ghost&lt;/code&gt;, installs Ghost locally, starts it on port &lt;code&gt;2373&lt;/code&gt;, and optionally asks whether to publish immediately. After creating a post in Ghost Admin, publish changes 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; ~/.ghost
./deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is a static site at &lt;code&gt;https://USERNAME.github.io/REPOSITORY&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;The project's &lt;a href="https://github.com/paladini/ghost-on-github-pages/blob/v3.0.0/docs/v3/REQUIREMENTS.md" rel="noopener noreferrer"&gt;v3 requirements&lt;/a&gt; are deliberately small:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS or Linux. Windows users can use WSL.&lt;/li&gt;
&lt;li&gt;Node.js LTS, documented for v18 or v20.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wget&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Git and a GitHub account.&lt;/li&gt;
&lt;li&gt;A public GitHub repository for the generated site.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do not need Python for v3. The project replaced its Classic v2 Python-based publishing tool with &lt;code&gt;gssg&lt;/code&gt;, a Node-based static-site generator. The exact Ghost and &lt;code&gt;gssg&lt;/code&gt; versions are installed by the release scripts, so do not assume that a globally installed copy is equivalent to this workflow.&lt;/p&gt;

&lt;p&gt;Check the prerequisites in a Unix-like terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt;
wget &lt;span class="nt"&gt;--version&lt;/span&gt;
git &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install Ghost locally
&lt;/h2&gt;

&lt;p&gt;Download and extract the &lt;a href="https://github.com/paladini/ghost-on-github-pages/releases/tag/v3.0.0" rel="noopener noreferrer"&gt;v3.0.0 archive&lt;/a&gt;, then enter the extracted directory. The release's &lt;code&gt;install.sh&lt;/code&gt; checks for &lt;code&gt;node&lt;/code&gt;, &lt;code&gt;npm&lt;/code&gt;, and &lt;code&gt;wget&lt;/code&gt;, creates &lt;code&gt;~/.ghost&lt;/code&gt;, installs &lt;code&gt;ghost-cli@latest&lt;/code&gt;, and runs a local Ghost installation on port &lt;code&gt;2373&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Run it like this:&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;path/to/ghost-on-github-pages
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x install.sh
./install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script may take several minutes because it downloads and configures Ghost. When it finishes, open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:2373/ghost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the administrator account, then make a small test post. The local editor is the source of truth for writing. The GitHub repository will contain the generated public output, not your local Ghost database or administrator interface.&lt;/p&gt;

&lt;p&gt;If you want to postpone the first publish, use the documented option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./install.sh &lt;span class="nt"&gt;--skip-deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That flag is useful when you want to finish local configuration before creating or selecting a GitHub destination.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publish the static site
&lt;/h2&gt;

&lt;p&gt;When the blog is ready, run the release's deploy script from the Ghost folder:&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; ~/.ghost
./deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the first run, the script asks for your GitHub username, repository name, and repository URL. It stores those settings in &lt;code&gt;~/.ghost/deploy.conf&lt;/code&gt;. For a project site named &lt;code&gt;my-blog&lt;/code&gt;, the expected public URL is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://USERNAME.github.io/my-blog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a user site named &lt;code&gt;USERNAME.github.io&lt;/code&gt;, GitHub Pages uses the root URL instead.&lt;/p&gt;

&lt;p&gt;The script first makes sure Ghost is running and sets the local site URL to &lt;code&gt;http://localhost:2373&lt;/code&gt;. It then runs &lt;code&gt;gssg&lt;/code&gt;, copies the project's small &lt;code&gt;index.html&lt;/code&gt; wrapper when present, validates the generated &lt;code&gt;static&lt;/code&gt; directory, and pushes the publish directory to the &lt;code&gt;master&lt;/code&gt; and &lt;code&gt;gh-pages&lt;/code&gt; branches. The repository's &lt;a href="https://github.com/paladini/ghost-on-github-pages/blob/v3.0.0/docs/v3/DEPLOY.md" rel="noopener noreferrer"&gt;deployment guide&lt;/a&gt; documents the same lifecycle.&lt;/p&gt;

&lt;p&gt;After the push, allow GitHub Pages time to build and serve the site. The project documentation says to wait about ten minutes, but the actual delay depends on GitHub's current Pages processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the important failure boundary
&lt;/h2&gt;

&lt;p&gt;Do not stop at a successful &lt;code&gt;git push&lt;/code&gt;. Verify both the local generated files and the public URL:&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; ~/.ghost
./scripts/validate-static.sh static
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The validator fails if generated files contain &lt;code&gt;localhost:2368&lt;/code&gt; or &lt;code&gt;localhost:2373&lt;/code&gt;, or if they contain the known malformed JPEG suffixes &lt;code&gt;.jpegg&lt;/code&gt;, &lt;code&gt;.jpegpg&lt;/code&gt;, or &lt;code&gt;.jpegjpg&lt;/code&gt;. The repository includes fixtures for both failure cases. Its validation test suite can be run from a checkout of the release:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./tests/run-validation-tests.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;code&gt;https://USERNAME.github.io/REPOSITORY&lt;/code&gt; in a browser and check the home page, one post, one tag, and one image. A static site can look healthy on its home page while a post link or asset still points at localhost, so those deeper checks matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this works
&lt;/h2&gt;

&lt;p&gt;The architecture is a simple publishing pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ghost provides the local editing and administration experience.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gssg&lt;/code&gt; turns the local Ghost site into static HTML, CSS, JavaScript, and assets.&lt;/li&gt;
&lt;li&gt;The validator checks for two classes of broken output.&lt;/li&gt;
&lt;li&gt;Git pushes the generated directory to GitHub Pages branches.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That separation gives you a rich editor without exposing Ghost Admin to the public internet. It also makes the hosted result cheap and easy to inspect: the public repository contains the files visitors receive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations and security boundaries
&lt;/h2&gt;

&lt;p&gt;This is a local publishing workflow, not a hosted Ghost service. You are responsible for keeping the local Ghost installation, Node.js, Git credentials, and administrator account secure. The public Pages repository should contain generated site output only. Never commit &lt;code&gt;.env&lt;/code&gt; files, database exports, admin credentials, access tokens, or private drafts.&lt;/p&gt;

&lt;p&gt;The deploy script uses force pushes for its &lt;code&gt;master&lt;/code&gt; and &lt;code&gt;gh-pages&lt;/code&gt; targets. That matches the project's intended publishing model, but it can overwrite history on those branches. Use a dedicated site repository, confirm the remote URL before the first publish, and do not point the script at a repository containing unrelated work.&lt;/p&gt;

&lt;p&gt;The project documents macOS, Linux, and WSL, with Node.js 18 or 20 as the supported examples. Native Windows PowerShell is not the documented environment because the workflow depends on Bash commands and Unix tools. The repository's scripts also expect network access to install Ghost and generate the static site.&lt;/p&gt;

&lt;p&gt;If the site still contains localhost links, consult the &lt;a href="https://github.com/paladini/ghost-on-github-pages/blob/v3.0.0/docs/TROUBLESHOOTING.md" rel="noopener noreferrer"&gt;troubleshooting guide&lt;/a&gt;. If you are upgrading the old Classic v2 workflow, use the project's &lt;a href="https://github.com/paladini/ghost-on-github-pages/blob/v3.0.0/docs/MIGRATION.md" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt; instead of copying v3 files into an existing Ghost folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is Ghost running publicly?
&lt;/h3&gt;

&lt;p&gt;No. Ghost runs locally at &lt;code&gt;http://localhost:2373&lt;/code&gt;. GitHub Pages serves the generated static output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need Python?
&lt;/h3&gt;

&lt;p&gt;Not for v3. Python was part of the Classic v2 path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I publish updates?
&lt;/h3&gt;

&lt;p&gt;Yes. Start Ghost, edit or publish the post locally, then run &lt;code&gt;cd ~/.ghost &amp;amp;&amp;amp; ./deploy.sh&lt;/code&gt; again.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens if there are no changes?
&lt;/h3&gt;

&lt;p&gt;The deploy script reports that there are no changes to publish. Make sure the post was saved or published in Ghost before trying again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;The useful idea is not merely hosting a blog for free. It is separating writing from delivery: Ghost handles local authoring, while &lt;code&gt;gssg&lt;/code&gt;, the validator, and GitHub Pages handle a reviewable static artifact. Start with the &lt;code&gt;v3.0.0&lt;/code&gt; release, publish a test post, and verify a post, tag, and image before treating the site as ready.&lt;/p&gt;

&lt;p&gt;Have you used a local CMS with a static publishing target? I would be interested in which part of the workflow you would automate next: preview builds, link checking, or branch protection.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI assistance disclosure
&lt;/h2&gt;

&lt;p&gt;AI assistance was used to organize this tutorial and improve wording. The commands, version references, workflow behavior, limitations, and validation claims were checked against the &lt;code&gt;v3.0.0&lt;/code&gt; release, its documentation, scripts, and fixture tests before publication.&lt;/p&gt;

</description>
      <category>ghost</category>
      <category>githubpages</category>
      <category>tutorial</category>
      <category>blogging</category>
    </item>
    <item>
      <title>Translate Git Commit Messages Offline with Python and Argos Translate</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Thu, 17 Sep 2026 12:37:32 +0000</pubDate>
      <link>https://dev.to/paladini/translate-git-commit-messages-offline-with-python-and-argos-translate-54e3</link>
      <guid>https://dev.to/paladini/translate-git-commit-messages-offline-with-python-and-argos-translate-54e3</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Mixed-language commit histories make changelogs, release notes, and repository archaeology harder to scan. &lt;a href="https://github.com/paladini/git-translate-commits" rel="noopener noreferrer"&gt;git-translate-commits&lt;/a&gt; is a Python CLI that translates commit messages to a target language. Its default engine is local Argos Translate, so the documented offline path does not require an API key.&lt;/p&gt;

&lt;p&gt;This tutorial uses the stable &lt;code&gt;v1.0.1&lt;/code&gt; release to build a cautious workflow: install the tool in an isolated environment, preview the changes, restrict the commit range when useful, and only then decide whether rewriting history is appropriate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.10 or newer.&lt;/li&gt;
&lt;li&gt;Git installed and available on your &lt;code&gt;PATH&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A local clone of a repository whose history you are allowed to rewrite.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pipx&lt;/code&gt;, &lt;code&gt;uv&lt;/code&gt;, or another isolated Python installation method.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project is released under &lt;a href="https://github.com/paladini/git-translate-commits/blob/v1.0.1/LICENSE" rel="noopener noreferrer"&gt;GPL-3.0-or-later&lt;/a&gt;. The commands below target the published &lt;code&gt;1.0.1&lt;/code&gt; package and the matching Git tag, not an unreleased default-branch change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the CLI in isolation
&lt;/h2&gt;

&lt;p&gt;The project README recommends &lt;code&gt;pipx&lt;/code&gt; or &lt;code&gt;uv&lt;/code&gt; for CLI installation. &lt;code&gt;pipx&lt;/code&gt; keeps the application's dependencies out of the rest of your Python environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pipx &lt;span class="nb"&gt;install &lt;/span&gt;git-translate-commits&lt;span class="o"&gt;==&lt;/span&gt;1.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify the installed version without touching a repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git-translate-commits &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package metadata declares the console entry point &lt;code&gt;git-translate-commits&lt;/code&gt; and requires Python 3.10 or newer. The default dependencies include Argos Translate, GitPython, language detection, Rich, and &lt;code&gt;python-dotenv&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preview a translation without rewriting history
&lt;/h2&gt;

&lt;p&gt;Change into a disposable clone or a repository where you have permission to work. Start with the documented dry-run command:&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;path/to/your-repository
git-translate-commits &lt;span class="nt"&gt;--lang&lt;/span&gt; en &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--lang&lt;/code&gt; option is required. The &lt;code&gt;--engine&lt;/code&gt; option defaults to &lt;code&gt;local&lt;/code&gt;, and &lt;code&gt;--dry-run&lt;/code&gt; reports what would change without applying a rewrite. The local engine may download a language pack on first use. After that initial download, translation is designed to run offline.&lt;/p&gt;

&lt;p&gt;If your current branch contains only English messages, a dry run may report that there is nothing to do. That is still a useful result: it confirms the repository can be read and that the selected language detector does not identify work unnecessarily.&lt;/p&gt;

&lt;p&gt;For a more controlled preview, narrow the input. For example, this checks commits after a date and only for one author:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git-translate-commits &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lang&lt;/span&gt; en &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"2025-01-01"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--author&lt;/span&gt; &lt;span class="s2"&gt;"dev@example.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI also supports &lt;code&gt;--until&lt;/code&gt;, repeatable &lt;code&gt;--branch&lt;/code&gt;, and &lt;code&gt;--all-branches&lt;/code&gt;. These filters are useful when you are cleaning a recent slice of history rather than attempting to process every ref.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the local engine deliberately
&lt;/h2&gt;

&lt;p&gt;The local engine is the safest starting point for a repository containing unpublished work. It uses Argos Translate instead of sending messages to an external API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git-translate-commits &lt;span class="nt"&gt;--lang&lt;/span&gt; pt-BR &lt;span class="nt"&gt;--engine&lt;/span&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That does not mean the tool has no external activity ever. Argos language packs are downloaded automatically when first needed, and the package itself comes from the Python package index during installation. Once the required language data is available, the documented local translation path is intended to operate without an API key.&lt;/p&gt;

&lt;p&gt;The project also provides an optional LLM engine through the &lt;code&gt;llm&lt;/code&gt; extra. It supports OpenAI, Anthropic, and OpenAI-compatible providers through LiteLLM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pipx &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"git-translate-commits[llm]==1.0.1"&lt;/span&gt;
git-translate-commits &lt;span class="nt"&gt;--lang&lt;/span&gt; en &lt;span class="nt"&gt;--engine&lt;/span&gt; llm &lt;span class="nt"&gt;--provider&lt;/span&gt; openai &lt;span class="nt"&gt;--model&lt;/span&gt; gpt-4o-mini &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the LLM path only when its data handling and cost are acceptable for your commit history. The README documents &lt;code&gt;OPENAI_API_KEY&lt;/code&gt; and &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; environment variables. Never place a real key directly in a committed script or shared shell history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apply a rewrite only after review
&lt;/h2&gt;

&lt;p&gt;If the dry-run report is correct and the repository is ready for a history rewrite, run the same selection without &lt;code&gt;--dry-run&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;git-translate-commits &lt;span class="nt"&gt;--lang&lt;/span&gt; en &lt;span class="nt"&gt;--engine&lt;/span&gt; &lt;span class="nb"&gt;local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default behavior creates a backup branch before rewriting. You can inspect the available safety controls with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git-translate-commits &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI exposes &lt;code&gt;--backup/--no-backup&lt;/code&gt;, &lt;code&gt;--force&lt;/code&gt;, and &lt;code&gt;--preserve-conventional/--no-preserve-conventional&lt;/code&gt;. The default preserves Conventional Commit prefixes such as &lt;code&gt;feat:&lt;/code&gt; and &lt;code&gt;fix:&lt;/code&gt;. The project also documents a JSON log containing the original and translated mapping.&lt;/p&gt;

&lt;p&gt;For a shared repository, coordinate before force-pushing. Commit messages are part of Git commit objects, so changing them changes commit hashes. Existing branches, open pull requests, signed commits, release references, and downstream clones may need attention after the rewrite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the result
&lt;/h2&gt;

&lt;p&gt;After a real rewrite, inspect the backup branch and compare the history before considering any push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;--list&lt;/span&gt; &lt;span class="s2"&gt;"backup/pre-rewrite-*"&lt;/span&gt;
git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; &lt;span class="nt"&gt;--decorate&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 20
git diff &lt;span class="nt"&gt;--stat&lt;/span&gt; &lt;span class="s2"&gt;"backup/pre-rewrite-*"&lt;/span&gt; HEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact backup branch name includes a timestamp, so replace the wildcard with the name printed by the tool when running commands that require one exact ref. Also inspect the generated &lt;code&gt;.git-translate-log.json&lt;/code&gt; file if the run produced it. It provides a reviewable mapping instead of requiring you to infer every change from the new hashes.&lt;/p&gt;

&lt;p&gt;If the result is wrong, stop before pushing. The backup branch is the recovery point documented by the project. A local history rewrite is reversible only while you still have an intact reference to the old commits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this workflow works
&lt;/h2&gt;

&lt;p&gt;There are three separate decisions here. First, language selection determines the intended output. Second, the engine determines whether messages remain local or are sent through an LLM provider. Third, the rewrite step changes immutable Git history. Keeping those decisions separate makes the process easier to review.&lt;/p&gt;

&lt;p&gt;The tool also preserves more than a one-line subject. The README says it preserves file contents, author and committer metadata, timestamps, Conventional Commit prefixes, issue references, and Git trailers such as &lt;code&gt;Signed-off-by&lt;/code&gt;. Translation changes the message text, but it is not intended to rewrite the files in each commit.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;v1.0.1&lt;/code&gt; checkout includes a test suite. I ran it from a clean checkout with the source package available on &lt;code&gt;PYTHONPATH&lt;/code&gt;: all 43 tests passed. That verifies the checked-out package behavior, but it is not a guarantee that every repository or language pair will produce a good translation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and honest limits
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The history is not clean
&lt;/h3&gt;

&lt;p&gt;The pipeline refuses to rewrite a repository with uncommitted changes unless it is running in dry-run mode. Commit or stash changes first, or use a disposable clone.&lt;/p&gt;

&lt;h3&gt;
  
  
  A language pack is unavailable
&lt;/h3&gt;

&lt;p&gt;The local engine may need to download a language pack. Plan for that first-run network step, and verify that the requested language pair is supported before a large run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Translation quality is not uniform
&lt;/h3&gt;

&lt;p&gt;Commit messages contain abbreviations, product names, issue identifiers, code symbols, and project-specific vocabulary. Review the dry-run output. The local engine is useful for normalization, not a substitute for a human review of important release history.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rewriting can disrupt collaborators
&lt;/h3&gt;

&lt;p&gt;Changing commit messages changes hashes. Do not force-push a shared branch casually, and remember that a backup branch on your machine does not automatically protect every remote clone.&lt;/p&gt;

&lt;h3&gt;
  
  
  LLM mode changes the privacy boundary
&lt;/h3&gt;

&lt;p&gt;The optional LLM engine can send commit content to the configured provider. Use local mode when messages may contain confidential names, incident details, customer references, or unreleased plans.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does the default mode require an API key?
&lt;/h3&gt;

&lt;p&gt;No. The documented default is the local Argos Translate engine. It may download language data on first use, but it does not require an LLM API key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I translate only one branch or author?
&lt;/h3&gt;

&lt;p&gt;Yes. Use &lt;code&gt;--branch&lt;/code&gt;, &lt;code&gt;--author&lt;/code&gt;, &lt;code&gt;--since&lt;/code&gt;, and &lt;code&gt;--until&lt;/code&gt; to constrain the selection. Use &lt;code&gt;--all-branches&lt;/code&gt; when you intentionally want all local branches processed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does it change source files?
&lt;/h3&gt;

&lt;p&gt;The project documents preservation of file contents. The operation rewrites commit messages and therefore commit hashes, not the snapshots represented by those commits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I use this on a public repository?
&lt;/h3&gt;

&lt;p&gt;Only with a reviewed migration plan. A public history rewrite can break links, signatures, forks, pull requests, and consumers that refer to old hashes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;git-translate-commits&lt;/code&gt; is most useful when language consistency is worth a controlled history migration. Start with &lt;code&gt;v1.0.1&lt;/code&gt;, use the local engine, preview with &lt;code&gt;--dry-run&lt;/code&gt;, keep the automatic backup, and review the log before changing a shared ref.&lt;/p&gt;

&lt;p&gt;Have you ever normalized a mixed-language Git history, and which preservation rule mattered most in your repository?&lt;/p&gt;

&lt;h2&gt;
  
  
  AI assistance disclosure
&lt;/h2&gt;

&lt;p&gt;AI assistance was used to help organize and edit this tutorial. The commands, version details, license, documented behavior, and test result were checked against the public &lt;code&gt;v1.0.1&lt;/code&gt; source, README, package metadata, and a clean checkout of the project.&lt;/p&gt;

</description>
      <category>python</category>
      <category>git</category>
      <category>tutorial</category>
      <category>i18n</category>
    </item>
    <item>
      <title>Add a Research-Backed Timeline Event with Astro and YAML</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Wed, 16 Sep 2026 12:37:48 +0000</pubDate>
      <link>https://dev.to/paladini/add-a-research-backed-timeline-event-with-astro-and-yaml-24aa</link>
      <guid>https://dev.to/paladini/add-a-research-backed-timeline-event-with-astro-and-yaml-24aa</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;An interactive timeline is only as useful as the evidence behind each event. In this tutorial, you will add a new milestone to &lt;a href="https://github.com/paladini/history-of-ai-assisted-development" rel="noopener noreferrer"&gt;The History of AI-Assisted Development&lt;/a&gt;, a public MIT-licensed Astro site. The workflow is deliberately small: edit one YAML collection, attach a primary source, run the repository's build checks, and inspect the generated page.&lt;/p&gt;

&lt;p&gt;The same pattern works for release histories, standards timelines, migration guides, and other documentation sites where the data should remain easy to review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js installed on your machine&lt;/li&gt;
&lt;li&gt;pnpm 9.15.0, matching the repository's &lt;code&gt;packageManager&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;A primary source for the milestone you want to document&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository currently declares version &lt;code&gt;1.0.0&lt;/code&gt; and uses Astro 5, React 19, Tailwind CSS 4, Mermaid 11, and TypeScript 5. The exact dependency versions are resolved by &lt;code&gt;pnpm-lock.yaml&lt;/code&gt;, so use the lockfile when installing.&lt;/p&gt;

&lt;p&gt;Clone the project and install its locked dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/paladini/history-of-ai-assisted-development.git
&lt;span class="nb"&gt;cd &lt;/span&gt;history-of-ai-assisted-development
pnpm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--frozen-lockfile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Find the timeline data
&lt;/h2&gt;

&lt;p&gt;The repository keeps timeline events in &lt;code&gt;content/timeline/events.yaml&lt;/code&gt;. The rendering code consumes this collection and turns the records into the interactive timeline. You do not need to edit an Astro component for a normal event.&lt;/p&gt;

&lt;p&gt;The project also documents the content contract in &lt;a href="https://github.com/paladini/history-of-ai-assisted-development/blob/main/CONTENT_GUIDE.md" rel="noopener noreferrer"&gt;&lt;code&gt;CONTENT_GUIDE.md&lt;/code&gt;&lt;/a&gt;. Every event requires a unique identifier, an ISO date, one of the supported era IDs, a title, a two or three sentence summary, a category, and at least one Tier 1 source.&lt;/p&gt;

&lt;p&gt;The source policy is important. A primary announcement, official repository, or authoritative survey is stronger than a secondary article repeating the same claim. If you only have an approximate date, use the first day of the month and say that the date is approximate in the summary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add one event
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;content/timeline/events.yaml&lt;/code&gt; and add a record with the existing indentation style. Here is a complete example for an event that can be verified against the official Model Context Protocol repository. The date and era are part of the site's content model, while the source URL gives a reader a path to the underlying evidence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mcp-open-source-release&lt;/span&gt;
  &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2024-11-25&lt;/span&gt;
  &lt;span class="na"&gt;era&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;protocols-standards&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Model&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Context&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Protocol&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;released&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;as&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;an&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;standard"&lt;/span&gt;
  &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Anthropic&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;publishes&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Model&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Context&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Protocol&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;as&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;an&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;standard&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;connecting&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;AI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;applications&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;external&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tools&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;sources.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;release&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;gives&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;developers&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;documented&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;protocol&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;boundary&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;instead&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;relying&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;only&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;on&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;provider-specific&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;integrations."&lt;/span&gt;
  &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;protocol&lt;/span&gt;
  &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Model&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Context&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Protocol&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;repository"&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://github.com/modelcontextprotocol"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before saving, check each field:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt; is unique and uses lowercase kebab case.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;date&lt;/code&gt; uses &lt;code&gt;YYYY-MM-DD&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;era&lt;/code&gt; exactly matches one of the six IDs in the content guide.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;category&lt;/code&gt; is one of &lt;code&gt;tool&lt;/code&gt;, &lt;code&gt;protocol&lt;/code&gt;, &lt;code&gt;concept&lt;/code&gt;, &lt;code&gt;culture&lt;/code&gt;, or &lt;code&gt;data&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;summary&lt;/code&gt; explains what happened without claiming more than the source supports.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sources&lt;/code&gt; contains at least one usable URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The example is a data change, not an invitation to copy a historical claim blindly. Read the linked source yourself and adjust the wording if its scope, date, or terminology differs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate the result locally
&lt;/h2&gt;

&lt;p&gt;The repository provides a verification script that installs dependencies, runs the build, and checks that the static output exists. Run it after editing the YAML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm build:verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a direct production build, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The build should finish successfully and generate the static site in &lt;code&gt;dist/&lt;/code&gt;. The project currently builds the home page, about page, data page, and glossary routes. The timeline is part of the home page, so a successful build confirms that the collection was parsed and the page was generated.&lt;/p&gt;

&lt;p&gt;To inspect the result in a local browser, start Astro's development server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open the local URL printed by Astro. Use the timeline controls to locate the new event and verify the visible title, date, era, summary, category, and source link.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this workflow works
&lt;/h2&gt;

&lt;p&gt;The data and presentation have separate responsibilities. YAML makes the historical record readable in a pull request, while Astro components handle layout and interaction. That separation keeps a content correction small and reduces the chance of breaking the interface while adding research.&lt;/p&gt;

&lt;p&gt;The source list also makes review concrete. A reviewer can check whether the event date is supported, whether the summary distinguishes a protocol from a product, and whether the chosen era is reasonable. A link is not proof by itself, but a visible source gives the reviewer something specific to evaluate.&lt;/p&gt;

&lt;p&gt;The locked install matters too. &lt;code&gt;pnpm install --frozen-lockfile&lt;/code&gt; prevents the local dependency graph from silently changing while you validate the content. This is especially useful for a static site with several client-side visualization packages, because a content edit should be tested against the same dependency resolution used by the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common failure modes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The build reports malformed YAML
&lt;/h3&gt;

&lt;p&gt;Check indentation, quoting, and list markers. YAML treats indentation as structure. A missing space after &lt;code&gt;-&lt;/code&gt; or an inconsistent nesting level can prevent the content collection from loading.&lt;/p&gt;

&lt;h3&gt;
  
  
  The event does not appear
&lt;/h3&gt;

&lt;p&gt;Confirm that the record is in &lt;code&gt;content/timeline/events.yaml&lt;/code&gt;, not a similarly named file. Check that its &lt;code&gt;era&lt;/code&gt; value exactly matches the documented IDs and that the date is a valid ISO date. Then restart the development server if its content watcher did not reload the file.&lt;/p&gt;

&lt;h3&gt;
  
  
  The source is weak or ambiguous
&lt;/h3&gt;

&lt;p&gt;Replace a search result or commentary article with the original announcement, official repository, specification, or survey. If the original source does not establish the exact date, make the date approximate and say so. Do not fill gaps with confident prose.&lt;/p&gt;

&lt;h3&gt;
  
  
  The page builds but the claim is still wrong
&lt;/h3&gt;

&lt;p&gt;Build validation checks structure and rendering. It does not fact-check dates, assess source quality, or detect a misleading summary. Historical review remains a human responsibility, supported by the source policy and pull request discussion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations and security boundaries
&lt;/h2&gt;

&lt;p&gt;This workflow produces a static site. It does not create a database, authenticate users, or fetch remote sources during the build. The source links are references for readers; they are not automatically verified by the repository's build command.&lt;/p&gt;

&lt;p&gt;The example uses public URLs and contains no credentials. Keep tokens, private research notes, and unpublished material out of &lt;code&gt;events.yaml&lt;/code&gt; and the repository. A public timeline should describe public evidence only. Review external links before merging because a link can lead to content that changes after the event is recorded.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need to know Astro?
&lt;/h3&gt;

&lt;p&gt;No. For a normal milestone, the documented YAML record is enough. Astro knowledge becomes useful when changing the timeline layout, adding filters, or modifying the generated pages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can an event have multiple sources?
&lt;/h3&gt;

&lt;p&gt;Yes. Add more entries under &lt;code&gt;sources&lt;/code&gt; when independent primary sources clarify different parts of the claim. Keep the summary concise and make each source relevant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does &lt;code&gt;pnpm build&lt;/code&gt; verify links?
&lt;/h3&gt;

&lt;p&gt;It verifies the site's build, but you should not treat that as proof that every external URL is reachable or authoritative. Check important links separately during review.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I add a new era?
&lt;/h3&gt;

&lt;p&gt;Not through an event record alone. Era IDs are part of the site's model and the content guide. A new era may require coordinated changes to data, types, and presentation, so open a focused change that documents the design decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;A research-backed timeline entry can be a small, reviewable change: one YAML record, one or more primary sources, and a reproducible build. The valuable habit is not merely knowing the syntax. It is preserving the chain from a historical claim to evidence that another person can inspect.&lt;/p&gt;

&lt;p&gt;What is the most useful validation you would add next: automated link checks, a schema-level date validator, or a review report for events missing Tier 1 sources?&lt;/p&gt;

&lt;h2&gt;
  
  
  AI assistance disclosure
&lt;/h2&gt;

&lt;p&gt;AI assistance was used to help organize and edit this tutorial. The repository documentation, current package metadata, source policy, and build commands were checked against the public project before publication. The historical example should still be reviewed against its linked primary source.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>astro</category>
      <category>tutorial</category>
      <category>timeline</category>
    </item>
    <item>
      <title>Block Accidental AI Credit in Git with a Local Go Hook</title>
      <dc:creator>Fernando Paladini</dc:creator>
      <pubDate>Tue, 15 Sep 2026 12:38:56 +0000</pubDate>
      <link>https://dev.to/paladini/block-accidental-ai-credit-in-git-with-a-local-go-hook-31hk</link>
      <guid>https://dev.to/paladini/block-accidental-ai-credit-in-git-with-a-local-go-hook-31hk</guid>
      <description>&lt;p&gt;AI coding tools can leave a small but consequential trace in a commit message or pull request: a generated-by footer or an automatic &lt;code&gt;Co-authored-by&lt;/code&gt; trailer. If that text does not belong in your project's public history, discovering it in CI is already too late.&lt;/p&gt;

&lt;p&gt;This tutorial shows how to add a local, deterministic guardrail with &lt;a href="https://github.com/paladini/ai-credit-scrub" rel="noopener noreferrer"&gt;ai-credit-scrub&lt;/a&gt;. The tool is a Go CLI that works offline for cleaning and checking text. It can rewrite the temporary commit message, reject an escaped credit before a push, and sanitize pull request text before delegating to your existing GitHub CLI authentication.&lt;/p&gt;

&lt;p&gt;The goal is narrow: remove explicit AI-agent credit lines when your policy requires it, while preserving ordinary human attribution and product mentions.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Install the released CLI, run &lt;code&gt;ai-credit-scrub install --git&lt;/code&gt; inside a repository, and test it with a message containing an explicit credit. The &lt;code&gt;commit-msg&lt;/code&gt; hook rewrites the message before Git creates the commit, while &lt;code&gt;pre-push&lt;/code&gt; checks outgoing commit messages. For pull requests, use &lt;code&gt;ai-credit-scrub pr create&lt;/code&gt; instead of calling &lt;code&gt;gh pr create&lt;/code&gt; directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git installed and a local repository where you can install hooks.&lt;/li&gt;
&lt;li&gt;Go 1.26 or a prebuilt binary from the project's &lt;a href="https://github.com/paladini/ai-credit-scrub/releases" rel="noopener noreferrer"&gt;release page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The stable &lt;code&gt;v1.1.0&lt;/code&gt; release used in this tutorial.&lt;/li&gt;
&lt;li&gt;GitHub CLI only if you want the pull request wrapper. It must already be authenticated locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository is public, MIT licensed, non-archived, and owned by &lt;a href="https://github.com/paladini" rel="noopener noreferrer"&gt;Fernando Paladini&lt;/a&gt;. The release source declares Go 1.26.0 in &lt;code&gt;go.mod&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the released CLI
&lt;/h2&gt;

&lt;p&gt;The documented Go installation command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/paladini/ai-credit-scrub/cmd/ai-credit-scrub@v1.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check that the executable is available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ai-credit-scrub &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current CLI exposes five relevant entry points: &lt;code&gt;clean&lt;/code&gt;, &lt;code&gt;scan&lt;/code&gt;, &lt;code&gt;check&lt;/code&gt;, &lt;code&gt;install&lt;/code&gt;, and &lt;code&gt;pr create&lt;/code&gt;. &lt;code&gt;clean&lt;/code&gt; rewrites text, &lt;code&gt;scan&lt;/code&gt; reports matches, and &lt;code&gt;check&lt;/code&gt; is the non-rewriting check used by the push hook.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the Git boundary
&lt;/h2&gt;

&lt;p&gt;From the repository you want to protect, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ai-credit-scrub &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The installer adds two chained hooks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;commit-msg&lt;/code&gt; receives Git's temporary message file and cleans it before the commit object is written.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pre-push&lt;/code&gt; checks commit messages that are about to leave the machine and rejects a matching explicit credit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chaining matters. If the repository already has a hook, the tool preserves and runs it as part of the local hook chain. This makes the guardrail additive instead of silently replacing existing repository behavior.&lt;/p&gt;

&lt;p&gt;You can inspect the resulting files with normal Git commands or by looking in &lt;code&gt;.git/hooks&lt;/code&gt;. The hook files are local repository state, not files committed to the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce the cleaning behavior
&lt;/h2&gt;

&lt;p&gt;Create a temporary message file with both a line that should be removed and a line that should remain:&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;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/ai-credit-message.txt &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
Document local hooks

Generated with Claude Code
Co-authored-by: Claude &amp;lt;noreply@anthropic.com&amp;gt;
Reviewed in Cursor
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the cleaner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ai-credit-scrub clean /tmp/ai-credit-message.txt
&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/ai-credit-message.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output is equivalent to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document local hooks
Reviewed in Cursor
ai-credit-scrub: removed 2 explicit credit block(s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important distinction is that the tool matches complete, explicit credit signatures. A product name alone is not treated as a credit, so a sentence such as &lt;code&gt;Reviewed in Cursor&lt;/code&gt; remains available for normal review context.&lt;/p&gt;

&lt;p&gt;For a read-only check, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ai-credit-scrub scan CHANGELOG.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;scan&lt;/code&gt; when you want findings without changing the file. Use &lt;code&gt;clean --in-place&lt;/code&gt; when you intentionally want a source text file rewritten.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protect pull request text too
&lt;/h2&gt;

&lt;p&gt;Git hooks cover commits and pushes, but a pull request title and body are not part of Git history. Create the pull request through the local wrapper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ai-credit-scrub &lt;span class="nb"&gt;pr &lt;/span&gt;create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--title&lt;/span&gt; &lt;span class="s2"&gt;"Document local hooks"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--body-file&lt;/span&gt; pull-request.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The wrapper cleans the supplied title and body, then delegates to your existing local &lt;code&gt;gh pr create&lt;/code&gt; authentication. It does not require an ai-credit-scrub account, a hosted service, or an additional token.&lt;/p&gt;

&lt;p&gt;This boundary is easy to miss when working with agents. An agent that calls a separate GitHub-writing integration can create a pull request without invoking local Git at all. In that workflow, the local Git hooks cannot see the request body. The project's integration guide recommends instructing the agent to use this wrapper, or using a local sanitizing proxy that is the only GitHub-writing server available to the agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a reviewed custom rule
&lt;/h2&gt;

&lt;p&gt;The built-in signatures cover explicit credits for Codex, Claude Code, Cursor, Windsurf, and GitHub Copilot. You can add project-specific patterns with a configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;reviewed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;literals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Internal&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;agent&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;credit"&lt;/span&gt;
&lt;span class="na"&gt;regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(?m)^Generated&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;by&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ExampleBot\\.?$'&lt;/span&gt;
&lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;historical&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;example"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;reviewed: true&lt;/code&gt; acknowledgement is deliberate. Custom rules can create false positives, so review their matches before enabling them. The project asks contributors to provide both a removal fixture and a false-positive fixture for new signatures.&lt;/p&gt;

&lt;p&gt;Install a custom configuration only after reviewing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ai-credit-scrub scan &lt;span class="nt"&gt;--config&lt;/span&gt; .ai-credit-scrub.yml CHANGELOG.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep historical examples, legal notices, and human contributor credits out of broad patterns. A conservative rule that misses an ambiguous sentence is safer than a rule that silently removes meaningful attribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this works
&lt;/h2&gt;

&lt;p&gt;The design places enforcement at three different points in the local publication path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Before commit creation, &lt;code&gt;commit-msg&lt;/code&gt; rewrites the temporary message.&lt;/li&gt;
&lt;li&gt;Before a remote update, &lt;code&gt;pre-push&lt;/code&gt; checks outgoing commit messages.&lt;/li&gt;
&lt;li&gt;Before a pull request API call, the wrapper cleans the title and body.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That separation reflects what each interface can actually control. The tool does not pretend that a Git hook can intercept every remote API or rewrite shared history safely. It keeps cleaning and checking local, deterministic, and offline-first. Only the pull request wrapper needs network access, and that call is made by the user's already-authenticated &lt;code&gt;gh&lt;/code&gt; installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes and limits
&lt;/h2&gt;

&lt;p&gt;There are several boundaries to understand before treating this as a complete publication policy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git commit --no-verify&lt;/code&gt; bypasses the commit hook. The pre-push hook can still catch the escaped credit.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git push --no-verify&lt;/code&gt; bypasses the final local check too. Treat it as an intentional override.&lt;/li&gt;
&lt;li&gt;A pull request created directly in the GitHub website or by an independent GitHub MCP server bypasses Git hooks.&lt;/li&gt;
&lt;li&gt;The tool does not rewrite Git author or committer identity.&lt;/li&gt;
&lt;li&gt;Removing a credit may conflict with an organization's contributor agreement, disclosure policy, or applicable law. Confirm the policy before enabling the rule.&lt;/li&gt;
&lt;li&gt;The repository documents local enforcement, not CI enforcement. A CI job can report a problem, but it does not provide the same pre-publication boundary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The v1.1.0 release also reports its dependency and toolchain status separately from these policy limits. In a clean checkout, &lt;code&gt;go test ./...&lt;/code&gt; passed five tests, &lt;code&gt;go vet ./...&lt;/code&gt; reported no issues, and the CLI smoke path passed. Those checks demonstrate the documented path, not a security guarantee or coverage of every Git client.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does this remove every mention of an AI tool?
&lt;/h3&gt;

&lt;p&gt;No. It targets explicit credit lines and known trailers. A product name by itself is intentionally preserved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does it upload commit messages or source code?
&lt;/h3&gt;

&lt;p&gt;Cleaning and checking require no network access and the project documents no hosted service or prompt upload. The &lt;code&gt;pr create&lt;/code&gt; command delegates to your local GitHub CLI, so the normal &lt;code&gt;gh&lt;/code&gt; network behavior still applies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does installing it destroy existing hooks?
&lt;/h3&gt;

&lt;p&gt;The documented installer preserves and chains an existing hook. Still inspect the local result in a repository with important custom automation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can it protect a pull request created by an MCP server?
&lt;/h3&gt;

&lt;p&gt;Not automatically. The MCP server must route through a local sanitizing boundary, or the agent must use &lt;code&gt;ai-credit-scrub pr create&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;If your team wants explicit AI credits out of public Git history, enforce that decision where the text is still local. &lt;code&gt;ai-credit-scrub install --git&lt;/code&gt; covers commit creation and pushes, while &lt;code&gt;ai-credit-scrub pr create&lt;/code&gt; covers the separate pull request boundary. The useful part is not the deletion itself. It is the narrow, reviewable policy and the honest list of places it cannot control.&lt;/p&gt;

&lt;p&gt;Have you found more value in sanitizing commit messages, pull request text, or agent-specific configuration files first? Share the boundary that causes the most cleanup in your workflow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI assistance disclosure: This tutorial was prepared with AI assistance from the project's public documentation and a clean checkout of the &lt;code&gt;v1.1.0&lt;/code&gt; release. The commands and claims were checked against those sources and smoke-tested locally.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>go</category>
      <category>git</category>
      <category>tutorial</category>
      <category>hooks</category>
    </item>
  </channel>
</rss>
