<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>2026-09 on Funky Si's Test</title><link>https://blog-test.funkysi1701.com/2026/09/</link><description>Recent content in 2026-09 on Funky Si's Test</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><managingEditor>funkysi1701@gmail.com (Simon Foster)</managingEditor><webMaster>funkysi1701@gmail.com (Simon Foster)</webMaster><lastBuildDate>Wed, 23 Sep 2026 12:00:00 +0000</lastBuildDate><atom:link href="https://blog-test.funkysi1701.com/2026/09/index.xml" rel="self" type="application/rss+xml"/><item><title>Signal Diff CLI for coding agents</title><link>https://blog-test.funkysi1701.com/posts/2026/signal-diff-cli/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Wed, 23 Sep 2026 12:00:00 +0000</pubDate><guid>https://blog-test.funkysi1701.com/posts/2026/signal-diff-cli/</guid><category term="AI">AI</category><category term="DevOps">DevOps</category><category term="SignalDiff">SignalDiff</category><category term="OpenSource">OpenSource</category><category term="SideProjects">SideProjects</category><media:content medium="image" type="image/png" url="https://blog-test.funkysi1701.com/images/2026/signal-diff-cli.png"/><description>&lt;p&gt;This post is about the &lt;code&gt;signaldiff&lt;/code&gt; CLI: how to install it, run a local sitemap crawl, and hand that to Cursor, Copilot, or Claude Code so the agent can spot SEO and crawl issues without opening a dashboard.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://signaldiff.dev/" target="_blank" rel="noopener noreferrer"&gt;Signal Diff&lt;/a&gt;
is the product behind that command. It fetches the URLs in a sitemap, checks on-page SEO and crawl health (titles, meta descriptions, redirects, slow responses, and more), and can compare a run with a baseline—often the previous deploy—so you see what changed. I already use it on this blog after each Static Web Apps release via &lt;a href="https://github.com/funkysi1701/signal-diff-action" target="_blank" rel="noopener noreferrer"&gt;&lt;code&gt;funkysi1701/signal-diff-action&lt;/code&gt;&lt;/a&gt;
. That Action is the unattended gate. The CLI is the same crawl, from a shell. (There is also a customer-hosted crawler; this post stays on the coding-agent path.)&lt;/p&gt;
&lt;p&gt;A CLI is a good fit here because coding agents already have a terminal. They can install one binary, run a crawl, and read an HTML report. You do not need a browser session, and you do not need an MCP server. Local crawls need no account. Cloud commands are optional once an API key is already in the environment. The rest of this post walks through install, a capped crawl of this site, the prompt I give an agent, and the cloud commands worth using when a key is set.&lt;/p&gt;
&lt;h2 id="cli-behaviour-agents-need"&gt;CLI behaviour agents need&lt;a class="anchor ms-1" href="#cli-behaviour-agents-need" aria-label="Permalink: CLI behaviour agents need"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once a key exists, the subcommands &lt;code&gt;sites&lt;/code&gt;, &lt;code&gt;runs&lt;/code&gt;, &lt;code&gt;diff&lt;/code&gt;, &lt;code&gt;findings&lt;/code&gt;, and &lt;code&gt;scan&lt;/code&gt; call the &lt;a href="https://signaldiff.dev/docs/agent-api" target="_blank" rel="noopener noreferrer"&gt;Agent API&lt;/a&gt;
. Full command reference: &lt;a href="https://signaldiff.dev/docs/cli" target="_blank" rel="noopener noreferrer"&gt;signaldiff.dev/docs/cli&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;A few behaviours matter once an agent is the one typing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;signaldiff&lt;/code&gt; with no arguments prints help and exits. It does not start a crawl.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--json&lt;/code&gt; belongs on cloud subcommands. Success JSON goes to stdout. A failure is one JSON object on stderr, with a status code and, on a rate limit, how many seconds to wait. Progress logs stay on stderr, so a pipe keeps the result.&lt;/li&gt;
&lt;li&gt;A local &lt;code&gt;--sitemap&lt;/code&gt; crawl refuses &lt;code&gt;--json&lt;/code&gt; and still writes HTML.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="install-then-crawl-this-blog"&gt;Install, then crawl this blog&lt;a class="anchor ms-1" href="#install-then-crawl-this-blog" aria-label="Permalink: Install, then crawl this blog"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The installers are self-contained. You do not need the .NET SDK, and you do not need a clone of the repo. They verify SHA256 before putting &lt;code&gt;signaldiff&lt;/code&gt; on your &lt;code&gt;PATH&lt;/code&gt;. Prefer that path, or download a versioned zip from &lt;a href="https://signaldiff.dev/downloads/cli/" target="_blank" rel="noopener noreferrer"&gt;the CLI downloads page&lt;/a&gt;
and check the published &lt;code&gt;.sha256&lt;/code&gt; sidecar yourself. On Windows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;irm &lt;span style="color:#e6db74"&gt;&amp;#39;https://signaldiff.dev/install/cli.ps1&amp;#39;&lt;/span&gt; -OutFile &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$env:TEMP&lt;span style="color:#e6db74"&gt;\signaldiff-install-cli.ps1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;amp; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$env:TEMP&lt;span style="color:#e6db74"&gt;\signaldiff-install-cli.ps1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On Linux or macOS:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -fsSL &lt;span style="color:#e6db74"&gt;&amp;#39;https://signaldiff.dev/install/cli.sh&amp;#39;&lt;/span&gt; | bash
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Open a new terminal so &lt;code&gt;PATH&lt;/code&gt; picks up the binary (&lt;code&gt;~/.local/bin&lt;/code&gt; on Linux and macOS). Then crawl a real sitemap, with a page cap so the first run stays small:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;signaldiff --sitemap https&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;//www.funkysi1701.com/sitemap.xml --max-pages &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt; --output seo-report.html --quiet
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;--output&lt;/code&gt; creates missing folders before the crawl starts. &lt;code&gt;--quiet&lt;/code&gt; keeps the summary short. The report path is on stdout. I point this at my own sitemap because that is the site I already monitor. Swap in yours.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;signaldiff update&lt;/code&gt; replaces the install with the latest published build and checks the SHA256. &lt;code&gt;signaldiff update --check&lt;/code&gt; tells you whether one is waiting, and leaves the current binary alone.&lt;/p&gt;
&lt;h2 id="what-i-tell-the-agent"&gt;What I tell the agent&lt;a class="anchor ms-1" href="#what-i-tell-the-agent" aria-label="Permalink: What I tell the agent"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The API key stays in the environment. It stays out of the chat. Here is the prompt I give Cursor, Copilot, or Claude Code:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Install the Signal Diff CLI from &lt;a href="https://signaldiff.dev/docs/cli" target="_blank" rel="noopener noreferrer"&gt;https://signaldiff.dev/docs/cli&lt;/a&gt;
. Then, in a new terminal, run &lt;code&gt;signaldiff --sitemap https://www.funkysi1701.com/sitemap.xml --max-pages 20 --output seo-report.html --quiet&lt;/code&gt;. Read &lt;code&gt;seo-report.html&lt;/code&gt; and list the SEO and crawl issues worth fixing, with the URL for each and a concrete change. Use cloud commands only if &lt;code&gt;SIGNALDIFF_API_KEY&lt;/code&gt; is already set. Leave the key out of the chat.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is the loop. The agent installs if it must, runs a capped crawl, and works from the HTML. I still read the suggestions. A title that is two characters over the limit is a real fix. A rewrite of a post I care about is a suggestion until I agree with it.&lt;/p&gt;
&lt;p&gt;On a recent crawl of this sitemap the report had no errors and a few hundred warnings. Even with &lt;code&gt;--max-pages&lt;/code&gt;, a site-wide CSS finding still fans out across every page the crawl touched. The agent did not invent a title rewrite for every post. It collapsed them into one finding: Font Awesome&amp;rsquo;s CSS sits at the 100 KB file-size limit, repeated across pages. That is the shape of answer I want—site-wide root cause, not hundreds of identical notes.&lt;/p&gt;
&lt;p&gt;This sits next to the split I described in &lt;a href="https://blog-test.funkysi1701.com/posts/2026/how-i-use-ai-on-side-projects/"&gt;how I use AI on side projects&lt;/a&gt;
. ChatGPT when the question needs no repo. Cursor when the answer is in the files. The CLI when the question is about the live sitemap.&lt;/p&gt;
&lt;h2 id="when-a-key-is-already-set"&gt;When a key is already set&lt;a class="anchor ms-1" href="#when-a-key-is-already-set" aria-label="Permalink: When a key is already set"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Cloud commands need a paid key from the Signal Diff dashboard (Developers → API keys). Set it in the environment. Prefer that to &lt;code&gt;--api-key&lt;/code&gt;, which shows up in the process list.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$env:SIGNALDIFF_API_KEY = &lt;span style="color:#e6db74"&gt;&amp;#34;sck_…&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$env:SIGNALDIFF_API_BASE_URL = &lt;span style="color:#e6db74"&gt;&amp;#34;https://signaldiff.dev/api&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The base URL includes &lt;code&gt;/api&lt;/code&gt;. That is the easy one to get wrong. The GitHub Action uses a different variable, and the site origin with no &lt;code&gt;/api&lt;/code&gt; suffix. Same secret value. Different names, different URL shape.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Caller&lt;/th&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Base URL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CLI and the Agent API&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SIGNALDIFF_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://signaldiff.dev/api&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Action&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SIGNALDIFF_CI_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://signaldiff.dev&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These are the commands I want an agent to run once that is set:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You want&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sites you already monitor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;signaldiff sites list&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recent runs for one site&lt;/td&gt;
&lt;td&gt;&lt;code&gt;signaldiff runs list --site example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A short brief for one run&lt;/td&gt;
&lt;td&gt;&lt;code&gt;signaldiff runs summary &amp;lt;runId&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Only the deploy diff&lt;/td&gt;
&lt;td&gt;&lt;code&gt;signaldiff diff get &amp;lt;runId&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Errors from that run&lt;/td&gt;
&lt;td&gt;&lt;code&gt;signaldiff findings list &amp;lt;runId&amp;gt; --severity Error --limit 50&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Machine-readable output&lt;/td&gt;
&lt;td&gt;add &lt;code&gt;--json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;runs list&lt;/code&gt; is how you get a &lt;code&gt;runId&lt;/code&gt;. &lt;code&gt;runs summary&lt;/code&gt; is the one I point agents at after that. It is a capped brief: headline counts, top regressions, top improvements, and fix hints. It leaves out the full page payload. &lt;code&gt;diff get&lt;/code&gt; is there when the only question is what changed against the baseline.&lt;/p&gt;
&lt;p&gt;Starting a cloud scan is a separate decision. &lt;code&gt;signaldiff scan start&lt;/code&gt; queues work on the service, and &lt;code&gt;signaldiff scan wait &amp;lt;scanId&amp;gt; --json&lt;/code&gt; polls until it finishes. I leave that out of the default prompt. A local HTML report answers &amp;ldquo;what is wrong on this sitemap right now&amp;rdquo;. A cloud scan spends quota and shows up in the dashboard, so I ask for it on purpose.&lt;/p&gt;
&lt;h2 id="the-action-still-runs-after-deploy"&gt;The Action still runs after deploy&lt;a class="anchor ms-1" href="#the-action-still-runs-after-deploy" aria-label="Permalink: The Action still runs after deploy"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The pipeline check stays. On this blog the Action still crawls after a Static Web Apps deploy, with the API key in GitHub secrets and a pinned &lt;a href="https://github.com/funkysi1701/signal-diff-action" target="_blank" rel="noopener noreferrer"&gt;&lt;code&gt;signal-diff-action&lt;/code&gt;&lt;/a&gt;
version. The Action answers &amp;ldquo;did this deploy stay healthy?&amp;rdquo; when I am not looking. The CLI answers the same SEO questions while an agent and I are still in the change. A local &lt;code&gt;--sitemap&lt;/code&gt; crawl is not a drop-in for that CI step: it does not attach a commit SHA, collect a code diff, or comment on a pull request.&lt;/p&gt;
&lt;h2 id="try-it-on-a-sitemap-you-care-about"&gt;Try it on a sitemap you care about&lt;a class="anchor ms-1" href="#try-it-on-a-sitemap-you-care-about" aria-label="Permalink: Try it on a sitemap you care about"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Install the CLI, cap a crawl with &lt;code&gt;--max-pages&lt;/code&gt;, and have the agent read the HTML before it edits templates. If you already have a key, ask for &lt;code&gt;runs summary&lt;/code&gt; on the latest run and see whether the brief is enough to act on.&lt;/p&gt;
&lt;p&gt;I am curious how you give an agent this kind of check. A CLI it can run, an MCP server, or a dashboard you still open by hand? Tell me in the comments. Command reference, updates, and checksums are on the &lt;a href="https://signaldiff.dev/docs/cli" target="_blank" rel="noopener noreferrer"&gt;CLI docs&lt;/a&gt;
.&lt;/p&gt;</description></item></channel></rss>