Centralized Swift code quality tooling with smart configuration discovery. Provides unified interface for SwiftFormat, SwiftLint, and custom SwiftSyntax-based rules.
This package provides three command-line tools:
- swiftformat-smart - SwiftFormat with intelligent config discovery
- swiftlint-smart - SwiftLint with intelligent config discovery
- swiftlintcustom-smart - Custom SwiftSyntax-based linting rules
All tools feature smart configuration discovery that walks up the directory tree to find project-specific configs, with fallback to shared configurations.
Use the automated build script to build all components:
cd ~/Developer/swift-quality-tools
./Scripts/build-all.shThis builds:
- Main package (swiftformat-smart, swiftlint-smart, swiftlintcustom-smart)
- Custom rule engine
Manual build:
swift build -c releaseBinaries will be available at:
~/Developer/swift-quality-tools/.build/release/
βββ swiftformat-smart
βββ swiftlint-smart
βββ swiftlintcustom-smart
- SwiftFormat:
brew install swiftformat - SwiftLint:
brew install swiftlint - Swift 5.9+: For building the tools
Format Swift code with automatic config discovery:
# Format current directory
swiftformat-smart
# Format specific file
swiftformat-smart Sources/MyFile.swift
# Format directory
swiftformat-smart Sources/
# Use explicit config
swiftformat-smart --config path/to/.swiftformat.ymlConfig Discovery Order:
--configparameter if provided.swiftformat.ymlor.swiftformatin current directory (error if both)- Walk up directories until config found
- Fall back to
~/Developer/swift-quality-tools/Configs/shared-swiftformat.yml
Lint Swift code with automatic config discovery:
# Lint current directory
swiftlint-smart
# Lint specific file
swiftlint-smart Sources/MyFile.swift
# Lint directory
swiftlint-smart Sources/
# Use explicit config
swiftlint-smart --config path/to/.swiftlint.ymlConfig Discovery Order:
--configparameter if provided.swiftlint.ymlor.swiftlint.yamlin current directory (error if both)- Walk up directories until config found
- Fall back to
~/Developer/swift-quality-tools/Configs/shared-swiftlint.yml
Run custom SwiftSyntax-based rules with parallel processing:
# Check current directory (parallel - uses all CPU cores)
swiftlintcustom-smart
# Check specific file
swiftlintcustom-smart Sources/MyFile.swift
# Check directory
swiftlintcustom-smart Sources/
# Sequential mode (for debugging)
swiftlintcustom-smart --sequential Sources/Performance:
- β‘ Parallel processing enabled by default (uses all available CPU cores)
- π 2-6x faster than sequential on multi-core systems
- π Scales automatically with available hardware
- π§ Use
--sequentialflag to disable for debugging
Custom Rules:
skimmable_body: SwiftUI Viewbodyproperties limited to 15 lines maximumno_group_body: SwiftUI Viewbodyproperties must have exactly one top-level view (never Group)one_top_level_view: SwiftUI Viewbodymust have exactly one top-level viewexcessive_nesting: Indentation depth limited to 4 levels maximum for all functions, closures, and initializers
First Run: The custom rule engine auto-builds on first use if not already built.
The hook system at ~/.claude/hooks/ automatically uses these tools:
# ~/.claude/hooks/formatters/swift_formatter.py
SWIFT_TOOLS_PATH = Path.home() / "Developer" / "swift-quality-tools" / ".build" / "release"When you edit Swift files in Claude Code, these tools run automatically.
Add to your Xcode project's Build Phases to show lint warnings in the IDE.
Note: SwiftFormat runs on edit via Claude Code hooks, not in build phases. Only linters (SwiftLint + custom rules) should be in build phases.
Recommended: Single Script Approach
Add a "Run Script" build phase with:
"${HOME}/Developer/swift-quality-tools/Scripts/xcode-lint.sh"This script runs both linters and works around Xcode's subprocess output suppression by parsing and re-echoing warnings.
Known Limitation: Xcode's build phase sandbox suppresses subprocess output. The script works around this by running swiftlintcustom-smart in terminal mode, parsing the output, and re-echoing warnings directly.
Alternative: Direct Tool Invocation
If you prefer explicit control:
if [ -f "${HOME}/Developer/swift-quality-tools/.build/release/swiftlint-smart" ]; then
"${HOME}/Developer/swift-quality-tools/.build/release/swiftlint-smart" "${SRCROOT}" || true
fi
if [ -f "${HOME}/Developer/swift-quality-tools/.build/release/swiftlintcustom-smart" ]; then
"${HOME}/Developer/swift-quality-tools/.build/release/swiftlintcustom-smart" "${SRCROOT}"
fiAuto-Detection: swiftlintcustom-smart automatically detects when running in Xcode (via XCODE_VERSION_ACTUAL environment variable) and formats violations as clickable warnings for the Issue Navigator.
This provides:
- β Clickable lint warnings in Xcode Issue Navigator
- β Consistent quality checks across all projects
- β Automatic config discovery per project
- β Auto-detection of Xcode environment (no flags needed)
- β Single script for easy maintenance
Add to your PATH for easy access:
# In ~/.zshrc
export PATH="$HOME/Developer/swift-quality-tools/.build/release:$PATH"
# Then use anywhere:
swiftformat-smart .
swiftlint-smart .
swiftlintcustom-smart .Located in Configs/:
shared-swiftformat.yml- Global SwiftFormat rules (100+ rules, Swift 6.0 target)shared-swiftlint.yml- Global SwiftLint rules (custom SwiftUI rules, performance optimizations)
Place in project root to override shared configs:
.swiftformat.ymlor.swiftformat.swiftlint.ymlor.swiftlint.yaml
The tools will automatically discover and use project-specific configs when present.
SwiftSyntax-based rules in CustomRules/swiftlint-swiftsyntax-integration/:
Current Rules:
skimmable_body- SwiftUI View body line count limit (15 lines max)no_group_body- Prohibit top-level Group in View bodies (unless it has modifiers)one_top_level_view- Enforce single top-level view in View bodiesexcessive_indentation- Physical indentation limit (16 spaces / 4 tabs max)stack_minimum_children- VStack/HStack/ZStack must have at least 2 children (ForEach allowed; if/else allowed if any branch has 2+ views)view_structure_order- Enforce View property ordering (DISABLED - has bugs)no_wrapper_body- Detect pointless wrapper body propertiesblank_line_import_separation- Enforce blank line between regular and @testable importspreview_required- Every file with View/ViewModifier/Shape must have at least one #Previewno_exported_import- Prohibit@_exported import(underscore prefix = internal Swift API, not stable)prefer_swift_testing- Prefer Swift Testing framework over XCTest (detectsimport XCTestand XCTAssert* calls)prefer_shorthand_optional_binding- Detect optional binding renames (if let foo = bar) - use shorthand with original name instead
To add new rules:
- Edit
CustomRules/swiftlint-swiftsyntax-integration/rule-engine/test-custom-rule.swift - Rebuild:
./Scripts/build-all.shor manually:cd CustomRules/swiftlint-swiftsyntax-integration/rule-engine swift build
Custom rules support line-specific suppression using the swiftlintcustom: prefix:
// swiftlintcustom:disable:next excessive_nesting
var computed: String {
// Deep nesting allowed here
}
// Or inline
func deep() { // swiftlintcustom:disable:this excessive_nesting
// ...
}Note: Use swiftlint: for standard rules and swiftlintcustom: for custom rules. This keeps SwiftLint's superfluous_disable_command safety check working for standard rules.
See docs/swiftlint-directive-support.md for full documentation.
swift-quality-tools/
βββ Package.swift # Swift Package definition
βββ Scripts/ # Build and test scripts
β βββ build-all.sh # Build all components
β βββ test-tools.sh # Validate installation
βββ Sources/
β βββ SwiftFormatSmart/ # swiftformat-smart executable
β βββ SwiftLintSmart/ # swiftlint-smart executable
β βββ SwiftLintCustomSmart/ # swiftlintcustom-smart executable
β βββ SharedUtilities/ # Shared code
β βββ ConfigDiscovery.swift # Smart config finding
β βββ ColoredOutput.swift # Terminal colors
β βββ ProcessRunner.swift # Process execution
β βββ ErrorFormatter.swift # Self-healing error messages
βββ Configs/ # Shared configurations
β βββ shared-swiftformat.yml
β βββ shared-swiftlint.yml
βββ CustomRules/ # Custom SwiftSyntax rules
β βββ swiftlint-swiftsyntax-integration/
β βββ rule-engine/
β βββ Package.swift
β βββ test-custom-rule.swift
βββ .build/release/ # Compiled binaries
β Smart Config Discovery - Automatically finds project configs, falls back to shared β Centralized Tooling - Single source of truth for all Swift projects β Xcode Integration - In-IDE warnings and errors β Hook Integration - Auto-format on every edit in Claude Code β Fast Native Binaries - No shell/Python overhead β Type-Safe Swift - Robust error handling and CLI parsing β Self-Healing Errors - Actionable error messages for LLM auto-repair β Consistent Quality - Same rules across all projects
cd ~/Developer/swift-quality-tools
./Scripts/build-all.shRun the complete test suite (unit tests + integration tests):
./Scripts/run-tests.shTest individual components:
# Unit tests only (Swift Testing framework)
swift test
# Integration tests only (tool validation)
./Scripts/test-tools.shManual testing:
# Test on this repo
./.build/release/swiftformat-smart Sources/
./.build/release/swiftlint-smart Sources/
./.build/release/swiftlintcustom-smart Sources/
# Test on other projects
cd ~/Developer/gravity-well
~/Developer/swift-quality-tools/.build/release/swiftformat-smart .Test Coverage:
- ErrorFormatter - Self-healing error message formatting
- ConfigDiscovery - Smart config file discovery and validation
- Integration - All three tools executable and working correctly
All tools provide self-healing error messages following the pattern:
π¨ [Tool] Error: [ErrorType]
Problem: [Clear description]
Context: [What was being attempted]
Fix: [Actionable suggestion]
This enables LLM auto-repair when errors occur in Claude Code hooks.
This repo consolidates Swift quality tooling previously scattered in synodic-tools/:
- β
Moved
shared-swiftformat.ymlβConfigs/ - β
Moved
shared-swiftlint.ymlβConfigs/ - β
Moved
swiftlint-swiftsyntax-integration/βCustomRules/ - β Converted ZSH functions β Swift binaries
- β Better separation of concerns (library vs tools)
Personal tooling for Bryan Costanza's Swift projects.