squirrelscan
GitHub

Output Configuration

Configure audit report format and output destinations

The [output] section controls how squirrelscan formats and saves audit reports.

Configuration

[output]
format = "console"
path = ""

Options

format

Type: "console" | "text" | "json" | "html" | "markdown" | "llm" | "xml" Default: "console"

Output format for audit reports.

Available Formats:

  • "console" - Rich terminal output with colors and formatting
  • "text" - Plain text output without colors
  • "json" - Machine-readable JSON for automation
  • "html" - Interactive HTML report in browser
  • "markdown" - Markdown format for documentation
  • "llm" - Compact token-optimized XML/text for coding agents (40% smaller than xml)
  • "xml" - Verbose structured XML for enterprise integration

Note: The sarif format is only available via the report command, not during audit. See Reports documentation for details.


"console" (default)

Rich terminal output optimized for human readability.

Features:

  • Colored output (red/yellow/green for fail/warn/pass)
  • Progress indicators during crawl
  • Summary statistics
  • Issue grouping by rule
  • Interactive formatting

Example output:

πŸ” Crawling https://example.com...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
βœ“ Fetched 100 pages in 12.3s

πŸ“Š Running audit rules...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

ISSUES FOUND (23)

❌ core/meta-title (8 pages)
  /about - Missing page title
  /contact - Title too short (12 chars, min 30)

⚠️  links/broken-links (15 links)
  /blog/post-1 β†’ /missing-page (404)

βœ“ All other checks passed

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Health Score: 78/100 (Good)

Configuration:

[output]
format = "console"

Use cases:

  • Local development
  • Manual audits
  • Terminal-based workflows
  • Quick visual feedback

"json"

Machine-readable JSON output for automation and integrations.

Features:

  • Health score with group and category scores
  • Pass, warning, fail and skipped counts
  • Findings grouped by rule, with affected pages
  • Checks skipped because a scan limit was hit, listed apart from findings
  • Audit status and metadata

Example structure:

{
  "meta": {
    "version": "0.0.105",
    "baseUrl": "https://example.com",
    "timestamp": "2026-01-17T10:30:00.000Z",
    "totalPages": 100
  },
  "status": "completed",
  "score": {
    "overall": 78,
    "grade": "C",
    "groups": [],
    "categories": [{ "name": "Core SEO", "score": 82 }]
  },
  "summary": { "passed": 1204, "warnings": 15, "failed": 8, "skipped": 2 },
  "issues": [
    {
      "ruleId": "core/meta-title",
      "name": "Meta title",
      "description": "...",
      "category": "Core SEO",
      "group": "seo",
      "severity": "error",
      "checks": [
        {
          "name": "meta-title",
          "status": "fail",
          "message": "Missing page title",
          "affectedPages": ["https://example.com/about"],
          "affectedPagesCount": 1,
          "affectedPagesHasMore": false
        }
      ]
    }
  ],
  "skippedChecks": [
    {
      "ruleId": "content/hidden-text",
      "name": "Hidden text",
      "check": "hidden-text",
      "reason": "scan-truncated",
      "pages": ["https://example.com/huge-page"],
      "pagesCount": 1,
      "pagesHasMore": false
    }
  ]
}

See the JSON output schema for every key.

Configuration:

[output]
format = "json"
path = "reports/audit.json"  # Optional: save to file

Use cases:

  • CI/CD pipelines
  • Automated testing
  • Data analysis
  • Integration with other tools
  • API responses

CLI usage:

# Output to stdout
squirrel audit https://example.com --format json

# Save to file
squirrel audit https://example.com --format json --output report.json

# Pipe to jq for filtering
squirrel audit https://example.com --format json | jq '.score.overall'

"html"

Interactive HTML report that opens in your browser.

Includes:

  • Health score and the four group scores
  • Issues to fix first, and every finding banded by severity, filterable by group
  • A Copy fix prompt button on each finding for your coding agent
  • The same findings by page
  • Site profile, tech stack and the entity map

It is one standalone file that makes no network request when opened. See the html format for each section and the fix prompts.

Configuration:

[output]
format = "html"
path = "reports/audit.html"  # Optional: custom path

Default behavior:

If path is not specified, HTML report is saved to:

reports/audit-{timestamp}.html

Use cases:

  • Client presentations
  • Team sharing
  • Visual analysis
  • Stakeholder reports
  • Archive audits

CLI usage:

# Generate and open in browser
squirrel audit https://example.com --format html

# Save to custom location
squirrel audit https://example.com --format html --output reports/my-audit.html

"llm"

Compact token-optimized format for coding agents - hybrid XML/text structure designed to minimize tokens while maintaining structured data.

Features:

  • 40-70% smaller than verbose XML format
  • 1-space indentation for token efficiency
  • Inline attributes instead of nested elements
  • Text prefixes (Desc:, Fix:, Pages:, Items:)
  • Comma-separated lists for pages and URLs
  • Hybrid XML/text structure for easy parsing

Size Comparison:

For a 51-page audit:

  • XML format: 209KB
  • LLM format: 125KB (40% reduction)

Format Structure:

<?xml version="1.0" encoding="UTF-8"?>
<audit version="0.0.13">
<site url="https://example.com" crawled="42" date="2025-01-18T10:30:00Z"/>
<score overall="87" grade="B">
 <cat name="Core SEO" score="100"/>
 <cat name="Links" score="85"/>
</score>
<summary passed="83" warnings="13" failed="9"/>
<issues>
 <rule id="content/word-count" severity="warning" category="Content" group="seo" status="warn">
  Low word count detected
  Desc: Pages should have sufficient content for good SEO
  Fix: Add more relevant content to improve page depth
  Pages (3): /blog/post-1, /blog/post-2, /about
 </rule>
</issues>
</audit>

Use Cases:

  • Piping to Claude Code, Cursor, GPT, or other coding agents
  • Token-limited API contexts (cost optimization)
  • Agent-based workflows requiring structured output
  • AI code review and analysis

CLI usage:

# Direct output during audit
squirrel audit https://example.com --format llm

# Or export from stored audit
squirrel report <audit-id> --format llm

# Pipe directly to a coding agent
squirrel audit https://example.com --format llm | claude "analyze and prioritize fixes"

See OUTPUT-FORMAT.md for complete format specification.


"xml"

Verbose structured XML format for enterprise integration, archival, and strict schema validation.

Features:

  • 2-space indentation for readability
  • Fully nested elements (all metadata in dedicated tags)
  • Explicit structure with strict schema compliance
  • All information preserved without abbreviation
  • Enterprise-ready for XML parsers and validators

Format Structure:

<?xml version="1.0" encoding="UTF-8"?>
<squirrelscan-audit version="0.0.117">
  <site>
    <url>https://example.com</url>
    <pages-crawled>42</pages-crawled>
    <audit-date>2025-01-18T10:30:00.000Z</audit-date>
    <scan-scope origin="cli" crawled="42" max-pages="100" capped="false"/>
  </site>
  <health-score overall="87" grade="B">
    <category name="Core SEO" score="100"/>
  </health-score>
  <summary passed="180" warnings="6" failed="2"/>
  <issues>
    <rule id="content/word-count" severity="warning" category="Content" group="seo">
      <name>Word Count</name>
      <description>Pages should have sufficient content</description>
      <solution>Add more relevant content</solution>
      <check name="word-count" status="warn">
        <message>Low word count: 150 words (min: 300)</message>
        <affected-pages count="2" examples="2" has-more="false">
          <page url="https://example.com/blog/post-1"/>
          <page url="https://example.com/blog/post-2"/>
        </affected-pages>
      </check>
    </rule>
  </issues>
</squirrelscan-audit>

<scan-scope> says where the audit ran (origin: cli, ci or cloud), how many pages it crawled, the page limit in force (max-pages), and whether that limit stopped the crawl (capped). requested-max-pages appears only when the limit you asked for was lowered, and stop-reason="time" only when the crawl time budget ended the crawl. unfetched appears when the crawl stopped with in-scope URLs still queued: pages it found (in a sitemap, linked from a crawled page, or carried from an earlier audit) but never fetched. crawled plus unfetched is how many pages the crawl knew of. It is a floor: the links on the pages still queued are unknown, so the site may have more. On a rate-limited run, the unfetched count on <rate-limited> already includes these. A report from before scan scope existed has no <scan-scope>. A white-label report’s root element is <audit>.

Comparison: LLM vs XML

AspectLLM FormatXML Format
Size (51 pages)125KB209KB
Indentation1 space2 spaces
StructureHybrid XML/textPure XML
MetadataInline attributesNested elements
Best ForCoding agents, tokensEnterprise, archival

Use Cases:

  • Enterprise data integration and ETL pipelines
  • Long-term audit archival with full metadata
  • Schema validation and compliance requirements
  • XML processing tools and libraries
  • Integration with enterprise systems (SAP, Oracle, etc.)

CLI usage:

# Direct output during audit
squirrel audit https://example.com --format xml --output audit.xml

# Or export from stored audit
squirrel report <audit-id> --format xml --output audit.xml

# Validate with xmllint
squirrel audit https://example.com --format xml | xmllint --format -

path

Type: string Default: "" (empty = stdout for console/json, auto-generate for html) Required: No

Output file path for saving reports.

Behavior:

Empty (default):

  • console format β†’ stdout (terminal)
  • json format β†’ stdout
  • html format β†’ reports/audit-{timestamp}.html

Specified:

  • Saves to exact path provided
  • Creates directories if needed
  • Overwrites existing files

Examples:

Save JSON to file:

[output]
format = "json"
path = "reports/latest.json"

Save HTML with custom name:

[output]
format = "html"
path = "audit-reports/example-com-2026-01-17.html"

Console to file (unusual):

[output]
format = "console"
path = "logs/audit.log"

Path expansion:

Relative paths are relative to current working directory:

[output]
path = "reports/audit.json"
# Resolves to: /path/to/project/reports/audit.json

Absolute paths work too:

[output]
path = "/Users/you/audits/report.json"

Directory creation:

squirrelscan automatically creates parent directories:

[output]
path = "reports/2026/january/audit.json"
# Creates reports/2026/january/ if needed

Configuration examples

console output (default)

Terminal output with colors:

[output]
format = "console"

Run:

squirrel audit https://example.com

JSON for CI/CD

Save JSON report for automated testing:

[output]
format = "json"
path = "reports/audit.json"

Then in CI:

squirrel audit https://staging.example.com
cat reports/audit.json | jq '.score.overall'

HTML for sharing

Generate shareable HTML report:

[output]
format = "html"
path = "reports/client-audit.html"

Share the file:

squirrel audit https://client.com
# Send reports/client-audit.html to client

Multiple outputs

Use CLI to generate multiple formats:

# Console output
squirrel audit https://example.com

# JSON for automation
squirrel audit https://example.com --format json --output reports/data.json

# HTML for sharing
squirrel audit https://example.com --format html --output reports/visual.html

CLI override

CLI flags override config file:

Config file:

[output]
format = "console"
path = "reports/default.json"

CLI override:

squirrel audit https://example.com --format json --output custom.json
# Uses: format=json, path=custom.json

Precedence:

  1. CLI flags (--format, --output) - Highest
  2. Config file ([output] section)
  3. Built-in defaults - Lowest

Format comparison

FeatureConsoleJSONHTML
Human readableβœ“βœ—βœ“
Machine readableβœ—βœ“βœ—
Colorsβœ“βœ—βœ“
Interactiveβœ—βœ—βœ“
Shareableβœ—βœ“βœ“
CI/CD friendlyβœ—βœ“βœ—
Charts/graphsβœ—βœ—βœ“
File sizeSmallMediumLarge
Parse complexityN/AEasyMedium

Output schema

JSON output schema

Top-level keys. Keys marked optional are omitted when they do not apply.

{
  "meta": {
    "version": "string",
    "baseUrl": "string",
    "timestamp": "ISO 8601 date",
    "totalPages": 0,
    "maxPages": 0,
    "requestedMaxPages": 0,
    "stopReason": "time",
    "unfetched": 0,
    "seedRedirect": {},
    "coverage": {},
    "auditLevel": {},
    "rulesCache": {}
  },
  "status": "completed|failed|blocked",
  "statusReason": "string",
  "statusReasonCode": "string",
  "rateLimited": { "pages": 0, "hosts": [] },
  "refusedFetches": [],
  "score": {
    "overall": 0,
    "grade": "A|B|C|D|F|N/A",
    "groups": [
      { "group": "seo", "name": "string", "score": 0, "passed": 0, "warnings": 0, "failed": 0, "total": 0 }
    ],
    "categories": [{ "name": "string", "score": 0 }]
  },
  "summary": { "passed": 0, "warnings": 0, "failed": 0, "skipped": 0 },
  "issues": [
    {
      "ruleId": "core/meta-title",
      "name": "string",
      "description": "string",
      "solution": "string",
      "category": "string",
      "group": "string",
      "subcategory": "string",
      "severity": "error|warning|info",
      "checks": [
        {
          "name": "string",
          "status": "fail|warn",
          "message": "string",
          "affectedPages": ["string"],
          "affectedPagesCount": 0,
          "affectedPagesHasMore": false,
          "items": [],
          "details": {}
        }
      ]
    }
  ],
  "skippedChecks": [
    {
      "ruleId": "content/hidden-text",
      "name": "string",
      "check": "hidden-text",
      "reason": "scan-truncated",
      "pages": ["string"],
      "pagesCount": 0,
      "pagesHasMore": false
    }
  ],
  "notChecked": [
    {
      "ruleId": "links/broken-external-links",
      "name": "string",
      "check": "broken-external-links",
      "reason": "external-links-not-checked",
      "message": "string"
    }
  ],
  "passedRules": [
    {
      "ruleId": "core/canonical",
      "name": "string",
      "category": "string",
      "scope": "page|site",
      "pages": 0
    }
  ],
  "editorSummary": {},
  "technologies": {},
  "entities": {},
  "siteProfile": {}
}

Always present: meta, status, score, summary, issues. The rest are optional. statusReason and statusReasonCode appear when status is failed or blocked. meta.maxPages, meta.requestedMaxPages, meta.stopReason, meta.unfetched, meta.seedRedirect, meta.coverage, meta.auditLevel and meta.rulesCache appear when they apply. meta.unfetched counts the in-scope URLs the crawl had queued but never fetched when it stopped. Added to the pages this run crawled (meta.coverage.auditedPages on a smart audit, meta.totalPages otherwise) it is a lower bound on the pages the crawl found. On a rate-limited run, rateLimited.unfetched already includes it. meta.auditLevel is the audit level the audit ran at: level (a level’s name, or custom), basedOn, changes (the settings changed from the level) and settings (every setting’s value). rateLimited, refusedFetches, editorSummary, technologies, entities and siteProfile appear when the audit produced that data. editorSummary.priorities is the editor’s summary ranked to-do list: each entry has title, why, action, effort (quick-win, moderate or significant), ruleIds and technologies.

Issues: issues holds findings only. Every check in it has status fail or warn. affectedPages is a sample, and affectedPagesCount is the true total, so compare the two (or read affectedPagesHasMore) before treating the list as complete.

Skipped checks: skippedChecks lists checks a rule could not finish evaluating, so an absence of issues on those pages does not mean they are clean. It appears only when at least one check was skipped. Skipped checks are never part of issues, and do not count toward passed, warnings, failed or the score.

FieldMeaning
ruleIdRule that skipped, e.g. content/hidden-text
nameRule display name
checkCheck name within the rule
reasonWhy it was skipped. Currently only scan-truncated: the page hit the rule’s scan limit and was only partly scanned (content/hidden-text, content/dev-leakage). More reasons may be added, so treat unknown values as a skip
pagesSkipped page URLs. A sample when pagesHasMore is true
pagesCountTrue number of skipped pages
pagesHasMorepagesCount is larger than pages.length

summary.skipped is the sum of pagesCount across skippedChecks, and is 0 when none were skipped.

A consumer that closes a finding when a rule stops reporting it should leave the pages in skippedChecks for that rule open, because the rule did not finish looking at them.

Not checked: notChecked lists rules whose check did not run for this audit, so an absence of issues for them means nothing was checked. It appears only when at least one rule was not checked. Like skipped checks, these are never part of issues and never scored.

FieldMeaning
ruleIdRule that was not checked, e.g. links/broken-external-links
nameRule display name
checkCheck name within the rule
reasonexternal-links-not-checked: external link checking was off (--offline, --no-external-links, [external_links] enabled = false, or switched off for the website or run). external-links-over-cap: more external links than the audit’s limit ([external_links] max_links, by default the page budget or 200, whichever is more); the most-linked were checked. external-links-out-of-time: a cloud audit’s link check ran out of time, or waited too long on one request, before these links were answered. link-targets-not-fetched: the crawl did not follow links (Quick level, Sitemap crawl strategy) and some internal link targets were fetched neither by the crawl nor by the internal link check (one HEAD per target, on by default; [internal_links] enabled = false or --no-internal-links turns it off)
messageThe line every report format shows, e.g. Not checked: Broken External Links. External link checking was off for this audit, so 12 external link(s) on the audited pages were not checked. Turn external link checks on to check them: in the CLI pass --external-links (or drop --no-external-links, --offline, or external_links.enabled = false); in the cloud they are a website setting.

The text, Markdown and HTML reports print the same Not checked: line under the score, and the LLM and XML reports have a <not-checked> element with one <rule> per entry. A consumer that closes a finding when a rule stops reporting it should leave findings for these rules open.

Passed rules: passedRules appears only with --include-passed, and only when at least one rule passed. Each entry is a rule with at least one passing check and no warning or error. scope is the rule’s own (page or site). pages counts the distinct pages with a passing check. It is usually 0 for a site rule, and 0 for a page rule whose passing checks carry no page, which the other formats show as pages not recorded. A failed or blocked audit lists none.

Use for:

  • Automated analysis
  • Data warehousing
  • Custom reporting
  • Integration testing

HTML report structure

HTML reports include:

1. Overview Section

  • Health score gauge
  • Total pages/links/images
  • Crawl duration
  • Issue count by severity

2. Issues Section

  • Grouped by rule
  • Sorted by severity
  • Expandable details
  • Quick filters

3. Pages Section

  • Full page list
  • Status codes
  • Title/description
  • Check results per page

4. Rules Section

  • All rules run
  • Pass/fail counts
  • Category breakdown

5. Links Section

  • Internal link graph
  • Broken links list
  • Redirect chains
  • External links

6. Images Section

  • Image list with previews
  • Missing alt text
  • File size analysis
  • Format recommendations

Automation examples

CI/CD pipeline

Extract health score in CI:

# .github/workflows/audit.yml
- name: Run Audit
  run: squirrel audit https://staging.example.com --format json --output audit.json

- name: Check Score
  run: |
    SCORE=$(cat audit.json | jq '.score.overall')
    if [ "$SCORE" -lt 70 ]; then
      echo "Health score too low: $SCORE"
      exit 1
    fi

Compare audits

Compare two audits using JSON:

# Before changes
squirrel audit https://example.com --format json --output before.json

# Make changes...

# After changes
squirrel audit https://example.com --format json --output after.json

# Compare scores
echo "Before: $(cat before.json | jq '.score.overall')"
echo "After:  $(cat after.json | jq '.score.overall')"

Generate multiple reports

Run once, output multiple formats:

#!/bin/bash
URL="https://example.com"
DATE=$(date +%Y-%m-%d)

# JSON for data
squirrel audit $URL --format json --output "reports/$DATE/data.json"

# HTML for sharing
squirrel audit $URL --format html --output "reports/$DATE/report.html"

# Console for logs
squirrel audit $URL > "logs/$DATE.log"

Troubleshooting

HTML not opening in browser

Solution: Open manually:

squirrel audit https://example.com --format html --output report.html
open report.html  # macOS
xdg-open report.html  # Linux

JSON output too large

Cause: Large sites generate large JSON files

Solution: Use console format or filter JSON:

# Get only the score block
squirrel audit https://example.com --format json | jq '.score'

# Get only error-severity issues
squirrel audit https://example.com --format json | jq '.issues[] | select(.severity == "error")'

Path permission denied

Cause: No write permission to output directory

Solution: Use writable directory or fix permissions:

[output]
path = "~/reports/audit.json"  # Home directory

Or:

mkdir -p reports
chmod 755 reports

Complete example

[project]
name = "mysite"

[crawler]
max_pages = 500

[rules]
enable = ["*"]
disable = ["ai/*"]

[output]
# Default to console for development
format = "console"

# Override with CLI for CI/CD:
# --format json --output reports/audit.json

Local development:

squirrel audit https://localhost:3000
# Console output to terminal

CI/CD:

squirrel audit https://staging.example.com --format json --output audit.json
# JSON saved to file for analysis

Client report:

squirrel audit https://client.com --format html --output reports/client-audit.html
# HTML report for sharing

Type to search…

↑↓ navigate↡ openesc close