Skip to main content

v0.0.174

Released Nov 11, 2026


Release Notes

Reports say why a rule did not give a verdict (JSON schema 1.9), squirrel audit has one exit code precedence with the outcome in the JSON and LLM reports, --resume says why it started a new crawl, an expired login is called out, settings writes no longer lose each other, and five rules stop flagging hidden, presentational and symbol-only content.

Added

  • Reports carry a typed outcome for a rule that did not give a verdict. skippedChecks and notChecked entries in the JSON report have outcome: coverage_gap, timed_out or errored (a rule that threw is told apart from one that ran out of time), beside the reason string, which is unchanged. One table in @squirrelscan/core-contracts now says what each coded skip reason means (classifyRuleOutcome, with not_applicable, ran and disabled as the other outcomes), and the checks that read skip reasons (isRuleNotApplicableReason, isRuleWideGapSkipReason, the not-checked set) derive from it, so a new reason without an entry fails a test instead of being misclassified. The JSON schema version is 1.9. (#1661)
  • queued is a run status, and a run's phase is a typed list. AgentRunStatus in @squirrelscan/core-contracts now includes queued (a run waiting for a container slot, which starts by itself; not terminal), so a client no longer types it away, and the REST API docs and the list_audits tool say queued runs are listed. RunPhase (crawling, external-links, cloud, rules, walk, complete) and RunNotice (memory_pressure, crawl_scope) are exported, RunSyncDelta.phase is RunPhase | null, and nextVisiblePhase keeps a notice from overwriting the phase shown. (#1665)

Fixed

  • A crawl's status can no longer go backwards. stop() after a crawl completed used to write paused over completed, and a late worker could rewrite a URL the frontier had already finished or that a resume had re-queued. Crawl and frontier status writes now follow one transition table (documented under Crawl status in the storage docs), checked in the same statement as the write. A write the table does not allow is ignored: it never throws and never stops a crawl. --resume, re-crawls and squirrel analyze move through the same edges as before. (#1659)
  • squirrel audit computes its exit code in one place, and the JSON and LLM reports carry it. The code came from 34 assignments that overwrote each other, and an explicit --publish that failed returned early and skipped the blocked code and the --fail-on gate. Now one precedence table decides it (documented under Exit codes): a blocked site with a failed explicit --publish exits 3 instead of 1. A failed automatic publish still exits 0, the documented choice, and is now visible: --format json has outcome (exit_code, status, publish with status and reason) and --format llm an <outcome> element. Both reports are written after publishing, so they can include its result. (#1657)
  • An expired or rejected login says so. Both used to read as signed out with no message, so a run quietly lost its cloud features, and a login the server had revoked was sent again on every command. Now the credential has a state (env, login, expired, rejected or none): the commands that run the session check print one stderr line naming squirrel auth login when the stored login is expired or rejected, a 401 from the production API on an account route (/v1/auth/*) is remembered for 24 hours so later commands do not send the login again, and squirrel auth status (and --json, as credentialState) reports the state. A 401 from another route, or from a server set with SQUIRREL_API_SERVER, is not remembered, and a login with an unreadable expiry reads as expired. (#1666)
  • Settings writes no longer lose each other, and one update state drives the banner, the prompt and doctor. Two commands saving different settings at the same moment (a background auto-update and a foreground squirrel login, for example) both persist: each writer changes only its own keys under a lock file beside settings.json, and a lock left by a dead process is taken over. Whether an update is pending, being attempted, failed (fallback), snoozed, dismissed or applied is now derived in one place, and squirrel self doctor shows it next to the pending version. (#1660)
  • squirrel audit --resume and squirrel crawl --resume say why they started a new crawl. The resume decision is now one function shared by both commands. When a requested resume does not happen (no previous crawl, the previous crawl finished or failed, its data was reclaimed, the include, exclude or URL settings changed, --refresh was passed, or another live process owns the crawl) the reason is printed on stderr, and --format json carries meta.resume with requested, resumed, and the reason code and message. The old "previous crawl completed" message never printed, because a finished audit is crawled or analyzed, not completed. (#1658)
  • images/offscreen-lazy names every image it reports and says how below the fold was decided. An <img> with an empty src (filled in later by script) no longer produces an item with an empty id: it is named by its data-src file, then its srcset or data-srcset file, then its selector path with a short snippet of its tag. The finding's details give the above_fold_count value and state that no viewport is measured in an HTTP-only audit. (#1644)
  • a11y/label-content-name-mismatch ignores symbol-only visible text and controls that are not rendered. <button aria-label="Schließen">✕</button> passes: visible text with no letter or digit (✕, ×, ☰, ›, emoji) is an icon, not a label a user would speak. A control under a hidden or display:none ancestor or in a closed <dialog> is skipped, and one a style may hide but squirrel cannot settle is reported as unconfirmed. Real visible text with an unrelated aria-label still fails. (#1645)
  • a11y/aria-required-parent no longer flags the items of a progressbar list. A native <li>, <option>, <tr>, <td>, <th> or table section inside an ancestor whose children are presentational (an explicit role of progressbar, img, button, checkbox, slider, tab and the others WAI-ARIA lists, or a native <progress> or <meter>) is not in the accessibility tree, so <ol role="progressbar"><li> no longer reports four listitems missing a list. A focusable element (tabindex, contenteditable) is still checked, and a genuine orphan <li> still fails. (#1640)
  • a11y/table-duplicate-name counts only tables in the accessibility tree. A table inside a closed <dialog>, or under a hidden (not hidden="until-found"), inert, aria-hidden="true" or display:none ancestor, is no longer compared with the exposed tables, so two "Size" tables in separate closed dialogs are not a duplicate. Excluded tables are counted as hiddenTables on the check's details, and a table a style may hide but squirrel cannot settle is reported on table-duplicate-name-unconfirmed instead of as a duplicate. Two exposed tables with the same name still warn. (#1637)

Update to this version

squirrelscan will auto-update, or run this command to update now:

$

Downloads

macOS

Detected
Intel
squirrel-0.0.174-darwin-x64 · 110.7 MB
Download
SHA-256
Apple Silicon (M-series)
squirrel-0.0.174-darwin-arm64 · 102.2 MB
Download
SHA-256

Linux

ARM64
squirrel-0.0.174-linux-arm64 · 121.1 MB
Download
SHA-256
ARM64 (musl/Alpine)
squirrel-0.0.174-linux-arm64-musl · 113.9 MB
Download
SHA-256
x64 (musl/Alpine)
squirrel-0.0.174-linux-x64-musl · 114.9 MB
Download
SHA-256
x64
squirrel-0.0.174-linux-x64 · 121.1 MB
Download
SHA-256

Windows

x64 (Intel/AMD)
squirrel-0.0.174-windows-x64.exe · 126.9 MB
Download
SHA-256

Audit your site in one command

SEO, performance, security, accessibility and agent experience issues, with exact fixes for your coding agent.

Install
$

No account needed for the CLI. Cloud audits include free monthly credits.