
Company News
Free Business Plan Upgrades for Open Source Maintainers
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.
@whenlabs/stale
Advanced tools
Detect documentation drift in your codebase. Part of the WhenLabs toolkit.
Stale cross-references what your README, CONTRIBUTING.md, and docs say against what your code actually does -- and flags every discrepancy.
Part of the WhenLabs toolkit — install all 6 tools with one command:
npx @whenlabs/when install
Documentation rots silently. README says npm run dev but the script was renamed months ago. Docs reference src/config/database.js but the file was moved to TypeScript. Setup instructions say "requires Node 16+" but package.json has engines: ">=20". Stale catches all of this automatically.
| stale | Manual checking | Generic linters | |
|---|---|---|---|
| Detects semantic drift | Compares code behavior vs docs | Hope someone notices | Checks formatting, not accuracy |
| Cross-references code | Validates commands, paths, env vars, versions against source | Requires reading every file | No codebase awareness |
| AI-powered deep analysis | Claude finds subtle meaning mismatches | Not scalable | Not available |
| MCP / Claude Code native | Works as an MCP tool in your editor | N/A | N/A |
| Zero config | Works out of the box, optional .stale.yml | N/A | Requires rule configuration |
Seven built-in analyzers run deterministic checks against your codebase:
| Analyzer | What It Checks |
|---|---|
| Commands | npm run, yarn, make commands in docs vs package.json scripts and Makefile targets |
| File Paths | Referenced file paths vs actual filesystem (handles .js to .ts renames) |
| Env Vars | Documented env vars vs process.env / os.environ usage in code (bidirectional) |
| URLs | CI migration detection (Travis/CircleCI badge + GitHub Actions exists), broken relative links |
| Versions | "Requires Node X" claims vs engines, .nvmrc, .node-version, Dockerfile |
| Dependencies | "Requires Redis/Postgres" claims vs npm deps and docker-compose services |
| API Routes | Documented HTTP endpoints vs route definitions (Express, Fastify, Koa, Hono, Flask) |
With the --deep flag, Stale sends doc + code context to Claude for semantic analysis:
Beyond basic command and path checks, Stale detects higher-level drift patterns:
npm run test but package.json has no test script.env or config has PORT=8080src/config/db.js but it was renamed to src/config/db.tsredis as a prerequisite but it is not in package.json or docker-compose.ymlFlags documentation files that have not been updated in 30+ days when source files they reference have had commits since. Helps surface docs that are likely outdated after active development:
stale scan
⚠ README.md last updated 47 days ago; src/ has 12 commits since
Finds inline code comments that reference functions, classes, or variables that have been renamed or deleted:
stale scan
⚠ src/api.ts:42 — comment references `handleAuth()` but function was renamed to `authenticateRequest()`
Recommended: Install the full WhenLabs toolkit with
npx @whenlabs/when installto get stale plus 5 other tools in one step.
Requires Node.js >= 20. Bundles the TypeScript compiler at runtime (used by the AST extractor for JS/TS source parsing) — adds ~50 MB to the install footprint.
# Clone and install
git clone <repo-url> && cd stale-tool
npm install
# Build
npm run build
# Link globally (optional)
npm link
# Scan current directory
stale scan
# Scan a specific project
stale scan --path /path/to/project
# JSON output for CI
stale scan --format json
# Markdown output (for PR comments)
stale scan --format markdown
# SARIF output (for GitHub Code Scanning)
stale scan --format sarif
# AI-powered deep analysis
STALE_AI_KEY=your-key stale scan --deep
# Generate fix suggestions for detected drift
stale fix
# Show fixes in diff format
stale fix --format diff
# Apply high-confidence fixes automatically
stale fix --apply --no-dry-run
# Preview what --apply would change
stale fix --apply --dry-run
# Watch mode -- re-scans on file changes
stale watch
# Generate a .stale.yml config file
stale init
# Run without building (uses tsx)
npm run dev -- scan --path tests/fixtures/sample-project
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
stale scan| Flag | Description | Default |
|---|---|---|
-p, --path <path> | Project directory to scan | . (current directory) |
-f, --format <fmt> | Output format: terminal, json, markdown, sarif | terminal |
-d, --deep | Enable AI-powered analysis (requires STALE_AI_KEY env var) | off |
-c, --config <path> | Path to config file | auto-detect .stale.yml |
-v, --verbose | Verbose error output | off |
stale fixGenerate fix suggestions for drift issues found by stale scan, and optionally apply them.
| Flag | Description | Default |
|---|---|---|
-f, --format <fmt> | Output format: terminal, diff | terminal |
--apply | Apply high-confidence fixes to files | off |
--dry-run | Show what --apply would change without writing | on (when --apply used) |
--no-dry-run | Actually write changes when using --apply | off |
-p, --path <path> | Project directory | . |
-c, --config <path> | Path to config file | auto-detect |
-v, --verbose | Verbose output | off |
Create a .stale.yml (or .stale.yaml) in your project root, or run stale init to generate one with defaults.
# Which docs to scan (glob patterns)
docs:
- README.md
- CONTRIBUTING.md
- docs/**/*.md
# Paths to ignore
ignore:
- node_modules/**
- dist/**
- .git/**
# Toggle individual checks
checks:
commands: true
filePaths: true
envVars: true
urls: true # or { checkExternal: true } to verify external URLs
versions: true
dependencies: true
apiRoutes: true
# AI analysis settings
ai:
enabled: false
model: sonnet # sonnet (fast) or opus (thorough)
checks:
semantic: true
completeness: true
examples: true
# Customize severity levels
severity:
missingFile: error
deadCommand: error
undocumentedEnvVar: warning
staleEnvVar: error
brokenUrl: error
versionMismatch: error
missingDependency: warning
routeMismatch: error
# .github/workflows/stale.yml
name: Documentation Drift Check
on:
pull_request:
paths:
- '**.md'
- 'package.json'
- 'src/**'
- 'docs/**'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: your-org/stale-action@v1
with:
deep: true # Enable AI analysis
fail-on: error # Fail PR on errors (not warnings)
comment: true # Post results as PR comment
format: terminal # Output format
env:
STALE_AI_KEY: ${{ secrets.STALE_AI_KEY }}
| Input | Description | Default |
|---|---|---|
deep | Enable AI-powered analysis | false |
fail-on | Fail on: error, warning, or never | error |
comment | Post results as a PR comment | true |
config | Path to .stale.yml config file | auto-detect |
format | Output format | terminal |
stale-tool/
├── src/
│ ├── cli.ts # Entry point (Commander.js)
│ ├── types.ts # Shared types and interfaces
│ ├── config.ts # Config loader (.stale.yml + defaults)
│ ├── errors.ts # Custom error classes
│ ├── commands/
│ │ ├── scan.ts # Main scan pipeline
│ │ ├── fix.ts # Fix suggestions and auto-apply
│ │ ├── init.ts # Generate config file
│ │ └── watch.ts # Watch mode with debounce
│ ├── analyzers/
│ │ ├── registry.ts # Analyzer registry + parallel runner
│ │ ├── static/
│ │ │ ├── commands.ts # CLI command checker
│ │ │ ├── file-paths.ts # File path checker
│ │ │ ├── env-vars.ts # Env var checker
│ │ │ ├── urls.ts # URL/link checker
│ │ │ ├── versions.ts # Runtime version checker
│ │ │ ├── dependencies.ts # Dependency/prerequisite checker
│ │ │ └── api-routes.ts # API endpoint checker
│ │ └── ai/
│ │ ├── client.ts # AI SDK wrapper with retry
│ │ ├── semantic.ts # Semantic drift detection
│ │ ├── completeness.ts # Missing docs detection
│ │ └── examples.ts # Example freshness check
│ ├── parsers/
│ │ ├── markdown.ts # Markdown to structured data (remark/unified)
│ │ ├── codebase.ts # Extract facts from source code
│ │ └── config.ts # Parse package.json, docker-compose, etc.
│ ├── reporters/
│ │ ├── index.ts # Reporter registry
│ │ ├── terminal.ts # Colored terminal output
│ │ ├── json.ts # JSON output
│ │ ├── markdown.ts # GitHub-flavored markdown
│ │ └── sarif.ts # SARIF v2.1.0
│ └── utils/
│ ├── similarity.ts # Levenshtein fuzzy matching
│ ├── git.ts # Git history helpers
│ └── id.ts # Deterministic issue IDs
├── action/
│ ├── action.yml # GitHub Action definition
│ ├── index.ts # Action entry point
│ └── Dockerfile # Node 20 Alpine container
├── tests/
│ ├── fixtures/sample-project/ # Fake project with intentional drift
│ ├── analyzers/ # Unit tests per analyzer
│ └── integration/ # End-to-end scan tests
├── package.json
├── tsconfig.json
└── vitest.config.ts
Promise.allSettled. Each compares doc claims against codebase facts and produces DriftIssue objects with severity, location, message, and suggestions.DriftReport and rendered in the chosen output format. The CLI exits with code 1 if any errors are found.MIT
FAQs
Detect documentation drift in your codebase
The npm package @whenlabs/stale receives a total of 85 weekly downloads. As such, @whenlabs/stale popularity was classified as not popular.
We found that @whenlabs/stale demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.