Back to Skills

broken-links

sgcarstrends
Updated Today
21 views
9
1
9
View on GitHub
Documentationwordai

About

The broken-links skill helps developers identify and fix broken links in documentation, markdown files, and website pages. It's particularly useful for maintenance tasks, pre-release checks, and investigating 404 errors. The skill can check various link types including internal, external, anchor, and image links across documentation systems.

Documentation

Broken Links Skill

This skill helps you find and fix broken links in documentation and web pages.

When to Use This Skill

  • Before documentation releases
  • After refactoring documentation
  • Investigating 404 errors
  • Migrating documentation
  • Updating external references
  • Checking API documentation
  • Verifying cross-references

Link Types to Check

  • Internal links: /docs/api, ./components/button.md
  • External links: https://nextjs.org, https://github.com/...
  • Anchor links: #installation, /docs/api#methods
  • Image links: ![Logo](./logo.png)
  • API references: Links in OpenAPI specs

Mintlify Link Checking

Built-in Command

# Check broken links in Mintlify docs
cd apps/docs
pnpm mintlify broken-links

# Output:
# Checking for broken links...
# ✓ docs/architecture/system.md
# ✓ docs/architecture/workflows.md
# ✗ docs/api/cars.md
#   - Line 15: https://example.com/broken-link (404)
#   - Line 23: ./missing-file.md (File not found)

# Check specific directory
pnpm mintlify broken-links docs/architecture

# Check with verbose output
pnpm mintlify broken-links --verbose

Configuration

// apps/docs/mint.json
{
  "openapi": ["/api/openapi.json"],
  "navigation": [
    {
      "group": "Documentation",
      "pages": ["docs/introduction", "docs/getting-started"]
    }
  ],
  // Broken link checking configuration
  "validation": {
    "checkLinks": true,
    "ignorePatterns": [
      "https://localhost:*",
      "https://127.0.0.1:*"
    ]
  }
}

Markdown Link Check

Installation

# Install markdown-link-check
pnpm add -D markdown-link-check

# Or use globally
npm install -g markdown-link-check

Basic Usage

# Check single file
npx markdown-link-check README.md

# Check all markdown files
find . -name "*.md" -not -path "*/node_modules/*" | xargs -n1 markdown-link-check

# Save results
find . -name "*.md" | xargs -n1 markdown-link-check > link-check-results.txt

# Check only specific directory
find apps/docs -name "*.md" | xargs -n1 markdown-link-check

Configuration

// .markdown-link-check.json
{
  "ignorePatterns": [
    {
      "pattern": "^http://localhost"
    },
    {
      "pattern": "^https://127.0.0.1"
    }
  ],
  "replacementPatterns": [
    {
      "pattern": "^/docs",
      "replacement": "https://docs.sgcarstrends.com/docs"
    }
  ],
  "httpHeaders": [
    {
      "urls": ["https://api.sgcarstrends.com"],
      "headers": {
        "Authorization": "Bearer ${DOCS_TOKEN}"
      }
    }
  ],
  "timeout": "10s",
  "retryOn429": true,
  "retryCount": 3,
  "aliveStatusCodes": [200, 206, 301, 302]
}

Usage with Config

# Use configuration file
npx markdown-link-check README.md -c .markdown-link-check.json

# Check all files with config
find . -name "*.md" -not -path "*/node_modules/*" | \
  xargs -n1 markdown-link-check -c .markdown-link-check.json

Broken Link Checker (Web)

Installation

# Install broken-link-checker
pnpm add -D broken-link-checker

# Or use globally
npm install -g broken-link-checker

Check Website

# Check production website
npx blc https://sgcarstrends.com -ro

# Flags:
# -r: Recursive (follow links)
# -o: Check external links
# -v: Verbose output

# Check staging
npx blc https://staging.sgcarstrends.com -ro

# Check local dev server
npx blc http://localhost:3000 -ro

# Exclude specific URLs
npx blc https://sgcarstrends.com -ro \
  --exclude "https://twitter.com/*" \
  --exclude "https://linkedin.com/*"

# Save results
npx blc https://sgcarstrends.com -ro > broken-links.txt

Configuration File

// .blcrc
{
  "filterLevel": 3,
  "recursive": true,
  "excludeExternalLinks": false,
  "excludeInternalLinks": false,
  "excludeLinksToSamePage": true,
  "honorRobotExclusions": true,
  "maxSocketsPerHost": 1,
  "rateLimit": 500,
  "excludedKeywords": [
    "localhost",
    "127.0.0.1",
    "twitter.com",
    "linkedin.com"
  ]
}

CI Integration

GitHub Actions

# .github/workflows/check-links.yml
name: Check Links

on:
  pull_request:
    paths:
      - "apps/docs/**"
      - "**/*.md"
  schedule:
    - cron: "0 0 * * 0"  # Weekly on Sunday

jobs:
  check-markdown-links:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Check markdown links
        uses: gaurav-nelson/github-action-markdown-link-check@v1
        with:
          config-file: ".markdown-link-check.json"
          folder-path: "apps/docs"
          file-extension: ".md"

  check-mintlify-links:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "pnpm"

      - name: Install dependencies
        run: pnpm install

      - name: Check Mintlify broken links
        run: |
          cd apps/docs
          pnpm mintlify broken-links

  check-website-links:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "pnpm"

      - name: Install dependencies
        run: pnpm install

      - name: Build website
        run: pnpm -F @sgcarstrends/web build

      - name: Start website
        run: |
          cd apps/web
          pnpm start &
          sleep 10

      - name: Check website links
        run: npx blc http://localhost:3000 -ro --exclude "*/twitter.com/*"

      - name: Stop website
        run: pkill -f "next start"

Common Link Issues

1. Relative Path Errors

<!-- ❌ Wrong relative path -->
[API Docs](../api/cars.md)  <!-- File doesn't exist -->

<!-- ✅ Correct relative path -->
[API Docs](./api/cars.md)

<!-- ✅ Absolute path from root -->
[API Docs](/docs/api/cars.md)

2. Case Sensitivity

<!-- ❌ Case mismatch (on Linux/macOS) -->
[README](./readme.md)  <!-- File is README.md -->

<!-- ✅ Correct case -->
[README](./README.md)

3. Missing Anchors

<!-- ❌ Anchor doesn't exist -->
[Installation](#instalation)  <!-- Typo: instalation -->

<!-- ✅ Correct anchor -->
[Installation](#installation)

<!-- ✅ Check anchor exists in target file -->
[API Reference](./api.md#get-cars)

4. External Link Issues

# Check if external links are accessible
curl -I https://example.com/api/docs

# If 404, update or remove link

# ❌ Broken external link
[Docs](https://old-site.com/docs)  # Site moved

# ✅ Updated link
[Docs](https://new-site.com/docs)

Fix Broken Links

Find and Replace

# Find all occurrences of broken link
grep -r "old-domain.com" apps/docs/

# Replace in all files
find apps/docs -name "*.md" -exec sed -i '' 's/old-domain.com/new-domain.com/g' {} +

# Or use modern tools
fd -e md -x sd 'old-domain.com' 'new-domain.com'

Automated Fix Script

#!/bin/bash
# fix-links.sh

# Check if file has broken links
check_links() {
  local file=$1
  echo "Checking $file..."

  # Run markdown-link-check
  if ! markdown-link-check "$file" -c .markdown-link-check.json; then
    echo "Broken links found in $file"
    return 1
  fi

  return 0
}

# Fix common issues
fix_common_issues() {
  local file=$1

  # Fix relative paths
  sed -i '' 's|\.\./\.\./docs|/docs|g' "$file"

  # Fix case sensitivity
  sed -i '' 's|readme\.md|README.md|g' "$file"

  # Update old domain
  sed -i '' 's|old-site\.com|new-site.com|g' "$file"
}

# Main
for file in $(find apps/docs -name "*.md"); do
  if ! check_links "$file"; then
    echo "Attempting to fix $file..."
    fix_common_issues "$file"
    check_links "$file"
  fi
done

Link Validation in Code

Validate Links in Tests

// apps/docs/__tests__/links.test.ts
import { describe, it, expect } from "vitest";
import fs from "node:fs";
import path from "node:path";

describe("Documentation Links", () => {
  const docsDir = path.join(process.cwd(), "apps/docs");

  it("should have no broken internal links", async () => {
    const files = getAllMarkdownFiles(docsDir);
    const brokenLinks: string[] = [];

    for (const file of files) {
      const content = fs.readFileSync(file, "utf-8");
      const links = extractInternalLinks(content);

      for (const link of links) {
        const targetPath = resolveLink(file, link);
        if (!fs.existsSync(targetPath)) {
          brokenLinks.push(`${file}: ${link} -> ${targetPath}`);
        }
      }
    }

    expect(brokenLinks).toEqual([]);
  });

  it("should have valid anchor links", async () => {
    const files = getAllMarkdownFiles(docsDir);
    const brokenAnchors: string[] = [];

    for (const file of files) {
      const content = fs.readFileSync(file, "utf-8");
      const anchorLinks = extractAnchorLinks(content);
      const headings = extractHeadings(content);

      for (const anchor of anchorLinks) {
        const anchorId = anchor.replace("#", "").toLowerCase();
        if (!headings.includes(anchorId)) {
          brokenAnchors.push(`${file}: ${anchor}`);
        }
      }
    }

    expect(brokenAnchors).toEqual([]);
  });
});

function getAllMarkdownFiles(dir: string): string[] {
  // Recursively get all .md files
  const files: string[] = [];
  const entries = fs.readdirSync(dir, { withFileTypes: true });

  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory() && entry.name !== "node_modules") {
      files.push(...getAllMarkdownFiles(fullPath));
    } else if (entry.isFile() && entry.name.endsWith(".md")) {
      files.push(fullPath);
    }
  }

  return files;
}

function extractInternalLinks(content: string): string[] {
  const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
  const links: string[] = [];
  let match;

  while ((match = linkRegex.exec(content)) !== null) {
    const url = match[2];
    // Internal links start with ./ or / or are relative
    if (!url.startsWith("http://") && !url.startsWith("https://")) {
      links.push(url.split("#")[0]); // Remove anchor
    }
  }

  return links;
}

function extractAnchorLinks(content: string): string[] {
  const linkRegex = /\[([^\]]+)\]\((#[^)]+)\)/g;
  const anchors: string[] = [];
  let match;

  while ((match = linkRegex.exec(content)) !== null) {
    anchors.push(match[2]);
  }

  return anchors;
}

function extractHeadings(content: string): string[] {
  const headingRegex = /^#+\s+(.+)$/gm;
  const headings: string[] = [];
  let match;

  while ((match = headingRegex.exec(content)) !== null) {
    const heading = match[1]
      .toLowerCase()
      .replace(/[^\w\s-]/g, "")
      .replace(/\s+/g, "-");
    headings.push(heading);
  }

  return headings;
}

function resolveLink(fromFile: string, link: string): string {
  const dir = path.dirname(fromFile);
  return path.resolve(dir, link);
}

OpenAPI Link Validation

Check API Documentation Links

# Validate OpenAPI spec links
npx @redocly/cli lint apps/docs/api/openapi.json

# Check for broken API endpoints
curl -f https://api.sgcarstrends.com/v1/cars || echo "Endpoint broken"

# Validate all endpoints in spec
jq -r '.paths | keys[]' apps/docs/api/openapi.json | while read endpoint; do
  echo "Checking $endpoint..."
  curl -f "https://api.sgcarstrends.com$endpoint" || echo "Broken: $endpoint"
done

Best Practices

1. Regular Checks

# ✅ Check links weekly
# Set up CI schedule or local cron job

# Weekly check
0 0 * * 0 cd /path/to/repo && make check-links

2. Use Absolute Paths

<!-- ❌ Relative paths break when moving files -->
[API](../../api/cars.md)

<!-- ✅ Absolute paths from docs root -->
[API](/docs/api/cars.md)

<!-- ✅ Or use full URL -->
[API](https://docs.sgcarstrends.com/api/cars)

3. Test Before Committing

# ✅ Pre-commit hook
# .husky/pre-commit
#!/bin/sh

# Check links in staged markdown files
git diff --cached --name-only --diff-filter=ACMR | grep "\.md$" | \
  xargs -I {} markdown-link-check {} -c .markdown-link-check.json

4. Document External Dependencies

<!-- ✅ Note when external links are critical -->
<!-- IMPORTANT: This link is referenced in production -->
[API Reference](https://api.sgcarstrends.com/docs)

<!-- ✅ Or use link checker config to monitor -->

Troubleshooting

False Positives

// .markdown-link-check.json
{
  "ignorePatterns": [
    {
      "pattern": "^http://localhost"
    },
    {
      "pattern": "^https://example.com"  // Ignore example URLs
    }
  ],
  "aliveStatusCodes": [200, 206, 301, 302, 403]  // 403 might be valid
}

Rate Limiting

# Issue: Rate limited by external sites
# Solution: Slow down checks

# Use rate limit in config
{
  "timeout": "10s",
  "retryOn429": true,
  "retryCount": 3
}

# Or check with delay
for file in *.md; do
  markdown-link-check "$file"
  sleep 2
done

Private Links

# Issue: Private URLs return 401/403
# Solution: Add authentication

# Use httpHeaders in config
{
  "httpHeaders": [
    {
      "urls": ["https://private-api.com"],
      "headers": {
        "Authorization": "Bearer ${API_TOKEN}"
      }
    }
  ]
}

References

Best Practices Summary

  1. Check Regularly: Run link checks weekly or before releases
  2. Automate: Use CI to catch broken links early
  3. Absolute Paths: Prefer absolute paths for documentation links
  4. Configure Ignores: Exclude localhost and example URLs
  5. Monitor External: Track critical external dependencies
  6. Test Locally: Validate links before committing
  7. Fix Quickly: Address broken links as soon as detected
  8. Document: Note critical external links in config

Quick Install

/plugin add https://github.com/sgcarstrends/sgcarstrends/tree/main/broken-links

Copy and paste this command in Claude Code to install this skill

GitHub 仓库

sgcarstrends/sgcarstrends
Path: .claude/skills/broken-links
apiaws-lambdabackendhonojob-schedulerneon-postgres

Related Skills

sglang

Meta

SGLang is a high-performance LLM serving framework that specializes in fast, structured generation for JSON, regex, and agentic workflows using its RadixAttention prefix caching. It delivers significantly faster inference, especially for tasks with repeated prefixes, making it ideal for complex, structured outputs and multi-turn conversations. Choose SGLang over alternatives like vLLM when you need constrained decoding or are building applications with extensive prefix sharing.

View skill

evaluating-llms-harness

Testing

This Claude Skill runs the lm-evaluation-harness to benchmark LLMs across 60+ standardized academic tasks like MMLU and GSM8K. It's designed for developers to compare model quality, track training progress, or report academic results. The tool supports various backends including HuggingFace and vLLM models.

View skill

llamaguard

Other

LlamaGuard is Meta's 7-8B parameter model for moderating LLM inputs and outputs across six safety categories like violence and hate speech. It offers 94-95% accuracy and can be deployed using vLLM, Hugging Face, or Amazon SageMaker. Use this skill to easily integrate content filtering and safety guardrails into your AI applications.

View skill

langchain

Meta

LangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.

View skill