Skip to content

Latest commit

 

History

153 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Stepdown Rule Analyzer

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.

Installation

Trust model

  • Binaries publish only on tagged GitHub Releases (v*) with SHA256SUMS.
  • 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.

From GitHub Releases (recommended)

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.

Preferred (download → inspect → run)

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.sh

Windows (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.ps1

Pin a version: VERSION=v0.2.0 bash install.sh or $env:VERSION = 'v0.2.0'; powershell -File .\install.ps1.

Convenience pipe-to-shell (higher risk)

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 | bash

Windows:

irm https://graffhyrum.github.io/stepdown-rule/install.ps1 | iex

Pages 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"

From source (development)

git clone https://github.com/graffhyrum/stepdown-rule.git
cd stepdown-rule
bun install
bun run build

bun link registers the JS CLI (dist/cli.js) via Bun’s global bin dir:

bun link
stepdown-rule --version

Host-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:releasedist/release/.

Programmatic API (linked package)

cd path/to/your-project
bun link @stepdown/analyzer
import { 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);

Usage

CLI

# 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/**/*"

Agents and automation

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 rules

Programmatic

import { 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);

What is the Stepdown Rule?

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.

How It Works

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.

What Gets Reported

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

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.

Configuration

Create a .stepdownrc.json file (optional):

{
  "$schema": "./stepdown-schema.json",
  "ignore": ["node_modules/**", "dist/**", "*.test.ts", "*.spec.ts"]
}

Configuration Options

Option Type Default Description
ignore string[] [] Additional glob patterns to ignore when analyzing files

CLI Options

  • 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)

Development

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 + test

License

MIT

About

A TS CLI to apply the Stepdown rule to TS code

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages