A TypeScript AST analyzer that enforces the stepdown rule for function organization in codebases. The stepdown rule organizes code from high-level concepts at the top to low-level implementation details at the bottom.
- Binaries publish only on tagged GitHub Releases (
v*) withSHA256SUMS. - Installers (
install.sh/install.ps1) download the Release asset and verify SHA256 before copying/executing the binary. Mismatch → abort. - Preferred path: download the installer from a Release asset, inspect it, then run locally. That separates “fetch script” from “run script”.
- Pipe-to-shell (
curl|bash,irm|iex) executes remote content in one step — higher supply-chain risk. Use only if you accept trusting the URL host at fetch time. - No GPG/cosign signatures yet; integrity is checksum-based against GitHub Release assets.
Channels (Release vs Pages): docs/install-channels.md.
Standalone binaries (~98 MiB, Bun runtime embedded — no Bun install required at runtime). Tagged releases publish linux/darwin (x64 + arm64) and windows-x64, plus installers and SHA256SUMS.
Unix (macOS / Linux):
curl -fsSL -o install.sh https://github.com/graffhyrum/stepdown-rule/releases/latest/download/install.sh
less install.sh # review, then:
bash install.shWindows (PowerShell):
Invoke-WebRequest -Uri https://github.com/graffhyrum/stepdown-rule/releases/latest/download/install.ps1 -OutFile install.ps1
# review install.ps1, then:
powershell -File .\install.ps1Pin a version: VERSION=v0.2.0 bash install.sh or $env:VERSION = 'v0.2.0'; powershell -File .\install.ps1.
Warning: pipes remote script straight into a shell. Prefer the download → inspect → run flow above.
Unix (Pages short URL, same script as the tagged Release):
curl -fsSL https://graffhyrum.github.io/stepdown-rule/install | bashWindows:
irm https://graffhyrum.github.io/stepdown-rule/install.ps1 | iexPages deploys only on v* tags (not main HEAD), so short URLs stay aligned with Release-attached installers. Repo Settings → Pages → Source: GitHub Actions (once).
Then:
stepdown-rule --version
stepdown-rule "src/**/*.ts"git clone https://github.com/graffhyrum/stepdown-rule.git
cd stepdown-rule
bun install
bun run buildbun link registers the JS CLI (dist/cli.js) via Bun’s global bin dir:
bun link
stepdown-rule --versionHost-native binary after build (~98 MiB with --minify): dist/stepdown-rule (Unix) or dist/stepdown-rule.exe (Windows). Cross-compile all release targets: bun run compile:release → dist/release/.
cd path/to/your-project
bun link @stepdown/analyzerimport { analyzeFiles, fixFiles } from "@stepdown/analyzer";
import { FileService } from "@stepdown/analyzer/services/FileService";
const fileService = new FileService();
const config = { ignore: [], json: false };
const results = await analyzeFiles(["src/**/*.ts"], config, fileService);
const fixes = await fixFiles({ patterns: ["src/**/*.ts"], config, fileService });
console.log(results, fixes);# Analyze default (src/**/*.ts)
stepdown-rule
# Analyze specific files/globs
stepdown-rule analyze "src/**/*.ts" "lib/**/*.ts"
# Analyze a directory (auto-expands to **/*.ts)
stepdown-rule analyze src/
# Auto-fix violations
stepdown-rule fix
# Fix specific files
stepdown-rule fix "src/**/*.ts"
# Show circular dependencies (verbose mode)
stepdown-rule analyze --verbose
# JSON output for CI
stepdown-rule analyze --json
# Only run specific rules
stepdown-rule analyze --rules stepdown,nested
# Custom ignore patterns
stepdown-rule analyze --ignore "test/**/*" "generated/**/*"For coding agents and CI parsers, use the agents subcommand (stable JSON envelope on stdout). See SKILL.md for workflows, exit codes, and decision trees.
stepdown-rule agents analyze 'src/**/*.ts'
stepdown-rule agents fix 'src/**/*.ts' --dry-run
stepdown-rule agents schema rulesimport { analyzeFiles, fixFiles } from "@stepdown/analyzer";
import { FileService } from "@stepdown/analyzer/services/FileService";
const fileService = new FileService();
const config = { ignore: [], json: false };
const results = await analyzeFiles(["src/**/*.ts"], config, fileService);
const fixes = await fixFiles({ patterns: ["src/**/*.ts"], config, fileService });
console.log(results, fixes);The stepdown rule organizes functions in a file from high-level to low-level:
// ✅ Good: Stepdown rule followed
function main() {
const user = createUser("John", "john@example.com", "password123");
console.log("User created:", user);
}
function createUser(name: string, email: string, password: string): User {
if (!validateEmail(email)) throw new Error("Invalid email");
const hashedPassword = hashPassword(password);
return { id: Math.random().toString(36), name, email, password: hashedPassword };
}
function validateEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function hashPassword(password: string): string {
return crypto.createHash("sha256").update(password).digest("hex");
}The rule: within a scope, scope logic comes before subfunction declarations, and if function A calls function B, then A should appear before B in the file.
The analyzer reports only actionable violations - violations that can be fixed by reordering code. Violations involving functions in circular dependency cycles are excluded from reporting because reordering cannot fix them; they require refactoring.
✅ Reported: Functions that call other functions appearing below them
- These can be fixed by moving the caller after the callee
❌ Not Reported: Functions involved in circular dependencies
- Example:
funcA → funcB → funcA(mutual recursion) - These require architectural changes, not reordering
- Often appear in tree traversal algorithms, mutual recursion patterns, or interconnected systems
Circular dependencies are always detected and reported separately. To understand what's creating cycles in your code:
stepdown-rule src/analyzer.ts
# Output shows both violations (fixable) and circular dependencies (architectural)Circular dependencies do NOT prevent the fixer from running, but files with circular dependencies cannot be auto-fixed since reordering won't resolve them.
Create a .stepdownrc.json file (optional):
{
"$schema": "./stepdown-schema.json",
"ignore": ["node_modules/**", "dist/**", "*.test.ts", "*.spec.ts"]
}| Option | Type | Default | Description |
|---|---|---|---|
ignore |
string[] |
[] |
Additional glob patterns to ignore when analyzing files |
patterns- File patterns or directories to analyze (default:src/**/*.ts)--verbose- Show circular dependencies in output--json- Output results in JSON format--rules <ids>- Comma-separated rule IDs to run (available:stepdown,nested; default: all)--ignore <patterns...>- Additional ignore patterns--config <file>- Configuration file path (default:.stepdownrc.json)
bun install
bun run dev # Run CLI from source
bun run build # Build
bun test # Test
bun run check # Lint + format check (oxlint + oxfmt)
bun run vet # Full pipeline: build + typecheck + lint + testMIT