The ghost that keeps your builds fresh 👻
A universal file watcher with auto-rebuild for any language or build system
Poltergeist is an AI-friendly universal file-watcher that auto-detects any project and rebuilds them as soon as a file has been changed. Think npm run dev
for native apps, with automatic configuration, notifications and a smart build queue. It stands on the shoulders of giants and fills the glue layer that's been missing.
Works on macOS, Linux, and Windows. Available as a standalone binary (no Node.js required) or npm package.
📖 Read the story behind Poltergeist: The Ghost That Keeps Your Builds Fresh - Learn how this tool was built using Claude Code and why it's designed to accelerate both human and AI development workflows.
brew tap steipete/tap
brew install poltergeist
npm install -g @steipete/poltergeist
Poltergeist requires Watchman to be installed:
- macOS:
brew install watchman
- Linux: Installation guide
- Windows: Chocolatey package or manual install
Poltergeist offers both a CLI tool for universal development and a native macOS app for enhanced monitoring (coming soon).
- Universal Target System: Support for anything you can build - executables, app bundles, libraries, frameworks, tests, Docker containers, ...
- Smart Execution Wrapper:
polter
command that waits for a build to complete, then starts it - Real-time Build Output: See build progress as it happens, no more waiting in the dark
- Inline Error Diagnostics: Build errors shown immediately with context and actionable suggestions
- Manual Build Command: Trigger builds explicitly with
poltergeist build [target]
- Automatic Recovery: Recent build failures trigger automatic rebuild attempts
- Efficient File Watching: Powered by Facebook's Watchman with smart exclusions and performance optimization
- Intelligent Build Prioritization: Having multiple projects that share code? Polgergeist will compile the right one first, based on which files you edited in the past
- Automatic Project Configuration: Just type
poltergeist init
and it'll parse your folder and set up the config. - Native Notifications: System notifications with customizable sounds and icon for build status
- Concurrent Build Protection: Intelligent locking prevents overlapping builds
- Advanced State Management: Process tracking, build history, and heartbeat monitoring
- Automatic Configuration Reloading: Changes to
poltergeist.config.json
are detected and applied without manual restart
Polgergeist has been designed with an agentic workflow in mind. As soon as your agent starts editing files, we'll start a background compile process. Further edits will cancel and re-compile as needed. Since agents are relatively slow, there's a good chance your project already finished compiling before the agent tries to even run it. Benefits:
- Agents don't have to call build manually anymore
- They call your executable directly with
polter
as prefix, which waits until the build is complete. - Build errors are shown inline with actionable suggestions
- Automatic rebuild attempts for recent failures
- Real-time build output available with
--verbose
flag - Faster loops, fewer wasted tokens
Commands have been designed with the least surprises, the cli works just like what agents expect, and there's plenty aliases so things will just work, even if your agent gets confused.
Examples:
haunt
is used to start the daemon, butstart
is also a valid alias- Commands that are executed in a non-tty environment have added helpful messages for agents
- Fuzzy matching will find targets even if they are misspelled
- Build time is tracked, so agents can set their timeout correctly for waiting
- Commands are token conservative by default and don't emit the full build log
Install globally via npm:
npm install -g @steipete/poltergeist
- Automatic Configuration - Let Poltergeist analyze your project:
poltergeist init
This automatically detects your project type (Swift, Node.js, Rust, Python, CMake, etc.) and creates an optimized configuration.
- Start Watching - Begin auto-building on file changes:
poltergeist haunt # Runs as background daemon (default)
poltergeist status # Check what's running
poltergeist status --verbose # Show detailed status with build stats
- Execute Fresh Builds - Use
polter
to ensure you never run stale code:
polter my-app # Waits for build, then runs fresh binary
polter my-app --help # All arguments passed through
That's it! Poltergeist now watches your files and rebuilds automatically.
Each project gets its own background process, but poltergeist status
shows everything through a shared state system in /tmp/poltergeist/
. One project crashing never affects others.
- Features
- Designed for Humans and Agents
- Quick Start
- Command Line Interface
- Configuration
- Advanced Features
- Architecture
- Examples
- Development
- License
Poltergeist provides two main commands: poltergeist
for managing the file watcher daemon, and polter
for executing fresh builds.
# Start watching (runs as background daemon by default)
poltergeist haunt
poltergeist start # Alias for haunt
# Check what's running
poltergeist status # Shows all active projects and their build status
# Manually trigger a build
poltergeist build # Build default target
poltergeist build my-app # Build specific target
poltergeist build --verbose # Show real-time build output
# View build logs
poltergeist logs # Recent logs
poltergeist logs -f # Follow logs in real-time
# Stop watching
poltergeist stop # Stop all targets
poltergeist stop --target my-app # Stop specific target
# Initialize configuration
poltergeist init # Auto-detect and create config
poltergeist init --cmake # Specialized CMake detection
# List configured targets
poltergeist list # Shows all targets and their status
# Clean up old state files
poltergeist clean # Remove stale state files
poltergeist clean --all # Remove all state files
The polter
command ensures you always run fresh builds:
# Basic usage
polter <target-name> [arguments...]
# Examples
polter my-app # Run after build completes
polter my-app --port 8080 # All arguments passed through
polter backend serve --watch # Complex commands work too
# Options
polter my-app --timeout 60000 # Wait up to 60 seconds
polter my-app --force # Run even if build failed
polter my-app --verbose # Show build progress
How it works:
- Checks if target is currently building
- Waits for build to complete (with progress updates)
- Fails immediately if build failed
- Executes the fresh binary with your arguments
Since v1.4.0, Poltergeist runs as a daemon by default:
- Non-blocking: Returns control immediately
- Background builds: Continues watching/building after terminal closes
- Multi-project: Each project runs independently
- Persistent logs: Access logs anytime with
poltergeist logs
To run in traditional foreground mode:
poltergeist haunt --foreground # Blocks terminal, shows output directly
Command | Description | Example |
---|---|---|
haunt / start |
Start watching and building | poltergeist haunt --target frontend --log-level debug |
stop / rest |
Stop the daemon | poltergeist stop |
restart |
Restart the daemon | poltergeist restart |
status |
Show build status | poltergeist status --verbose |
logs |
View daemon logs | poltergeist logs -f -n 100 |
list |
List all targets | poltergeist list |
init |
Create configuration | poltergeist init --cmake |
clean |
Clean state files | poltergeist clean --dry-run |
polter |
Execute fresh builds | polter my-app --help |
Poltergeist can automatically detect and configure most projects, but also supports detailed manual configuration.
Run poltergeist init
to automatically:
- Detect project type (Swift, Node.js, Rust, Python, CMake, etc.)
- Find build commands and output paths
- Configure optimal watch patterns
- Set up smart exclusions
- Generate
poltergeist.config.json
Project detection looks for:
Package.swift
→ Swift Package Managerpackage.json
→ Node.js/npmCargo.toml
→ Rust/CargoCMakeLists.txt
→ CMake projectspyproject.toml
→ Python projects- And more...
Essential configuration structure:
{
"version": "1.0",
"projectType": "swift|node|rust|python|cmake|mixed",
"targets": [
{
"name": "my-app",
"type": "executable|app-bundle|library|framework|test|docker|custom",
"buildCommand": "cargo build --release",
"outputPath": "./target/release/myapp",
"watchPaths": ["src/**/*.rs"]
}
],
"buildScheduling": { "parallelization": 2 },
"notifications": { "enabled": true },
"logging": { "level": "info", "file": ".poltergeist.log" }
}
Poltergeist uses a highly efficient target-specific logging system:
- Separate files per target: Each target gets its own log file
- Location:
/tmp/poltergeist/{projectName}-{hash}-{target}.log
- Plain text format: Simple, parseable:
timestamp level: message
- One log per build: Fresh log for each build session (no rotation)
- 80% smaller than JSON format
- Zero parsing overhead when reading logs
- Natural filtering - each target has its own file
- No redundancy - target name never repeated in logs
{
"logging": {
"level": "debug" // Options: debug, info, warn, error
}
}
You can control logging verbosity through multiple methods (in order of priority):
- CLI Flag:
poltergeist haunt --log-level debug
- Verbose Flag:
poltergeist haunt --verbose
(sets level to debug) - Configuration File: Set in
poltergeist.config.json
- Default: Falls back to
info
level
debug
: Detailed information for debugging (Watchman events, state changes, etc.)info
: Standard operational messages (default)warn
: Warning messages that don't prevent operationerror
: Error messages only
poltergeist logs # View recent logs for active target
poltergeist logs myapp # View logs for specific target
poltergeist logs -f # Follow logs in real-time
poltergeist logs -n 100 # Show last 100 lines
Poltergeist supports multiple target types with specific optimizations:
Type | Use Case | Key Properties |
---|---|---|
executable |
CLI tools, binaries | outputPath , standard execution |
app-bundle |
macOS/iOS apps | bundleId , autoRelaunch , app lifecycle |
library |
Static/dynamic libs | libraryType , linking optimization |
framework |
macOS/iOS frameworks | Platform-specific builds |
test |
Test suites | testCommand , coverageFile |
docker |
Containerized apps | imageName , dockerfile , tags |
custom |
Custom builds | Flexible config object |
cmake-executable |
CMake executables | targetName , generator , buildType |
cmake-library |
CMake libraries | targetName , libraryType , generator |
cmake-custom |
CMake custom targets | targetName , custom CMake commands |
Poltergeist supports glob patterns with brace expansion for more compact configurations:
"watchPaths": [
"src/**/*.swift", // All Swift files recursively
"**/*.{js,ts}", // All JavaScript and TypeScript files
"tests/**/*Test.swift" // Test files with specific naming
]
Reduce repetition with brace expansion patterns:
// Instead of:
"watchPaths": [
"src/**/*.c",
"src/**/*.cpp",
"src/**/*.h"
]
// Use:
"watchPaths": [
"src/**/*.{c,cpp,h}" // All C/C++ source and header files
]
"watchPaths": [
// Multiple extensions
"src/**/*.{swift,m,h}", // Swift and Objective-C
"{src,include}/**/*.{c,cpp,h}", // Multiple directories
"frontend/**/*.{ts,tsx,js,jsx,css}", // Web assets
// Multiple file patterns
"{CMakeLists.txt,CMakePresets.json}", // Specific files
"config/{package,tsconfig}.json", // Config files
"**/*.{yaml,yml}", // YAML files
// Complex patterns
"{src,test}/**/*.{c,cpp,h}", // Source and test dirs
"apps/{mac,ios}/**/*.swift", // Platform-specific
"**/{Makefile,*.mk}" // Make files
]
Poltergeist uses sensible defaults to keep configurations minimal. Only specify what differs from defaults:
enabled: true
- Targets are enabled by defaultsettlingDelay: 1000
- 1 second delay before buildingdebounceInterval: 3000
- 3 seconds between buildsuseDefaultExclusions: true
- Standard exclusions enabledprofile: "balanced"
- Balanced performance profileautoOptimize: true
- Performance optimization enablednotifications.enabled: true
- Notifications are onbuildStart: false
- No notification on build startbuildSuccess: true
- Notify on successful buildsbuildFailed: true
- Notify on failed builds
{
"targets": [{
"name": "my-app",
"buildCommand": "./build.sh",
"watchPaths": ["src/**/*.{c,h}"],
"settlingDelay": 2000 // Only if you need 2s instead of default 1s
}]
}
Example target configurations
{
"targets": [
{
"name": "cli-tool",
"type": "executable",
"buildCommand": "cargo build --release",
"outputPath": "./target/release/myapp"
},
{
"name": "mac-app",
"type": "app-bundle",
"buildCommand": "xcodebuild -scheme MyApp",
"bundleId": "com.example.myapp",
"autoRelaunch": true
},
{
"name": "tests",
"type": "test",
"testCommand": "npm test",
"watchPaths": ["src/**/*", "test/**/*"]
}
]
}
Full configuration options (with defaults shown for reference)
{
"version": "1.0",
"projectType": "swift|node|rust|python|cmake|mixed",
"targets": [/* target configs */],
"watchman": {
"useDefaultExclusions": true, // default: true
"excludeDirs": ["custom", "exclusions"],
"maxFileEvents": 10000, // default: 10000
"rules": [{"pattern": "**/test_output/**", "action": "ignore"}]
},
"performance": {
"profile": "balanced", // default: "balanced"
"autoOptimize": true // default: true
},
"buildScheduling": {
"parallelization": 2, // default: 2
"prioritization": {
"enabled": true, // default: true
"focusDetectionWindow": 300000 // default: 300000 (5 min)
}
},
"notifications": {
"enabled": true, // default: true
"successSound": "Glass",
"failureSound": "Basso"
},
"logging": {
"file": ".poltergeist.log", // default: ".poltergeist.log"
"level": "info" // default: "info"
}
}
Poltergeist includes comprehensive CMake support with automatic target detection:
# Automatically detect and configure all CMake targets
poltergeist init --cmake
# Options
poltergeist init --cmake --generator Ninja # Specify generator
poltergeist init --cmake --preset debug # Use CMake preset
poltergeist init --cmake --dry-run # Preview configuration
- Automatic Target Detection: Parses
CMakeLists.txt
to find all targets - Smart Reconfiguration: Automatically runs
cmake
when CMakeLists.txt changes - Multi-Generator Support: Works with Ninja, Make, Visual Studio, Xcode
- Build Type Management: Supports Debug, Release, RelWithDebInfo, MinSizeRel
- Preset Integration: Works with
CMakePresets.json
- Parallel Builds: Uses
cmake --build --parallel
by default
{
"version": "1.0",
"projectType": "cmake",
"targets": [
{
"name": "spine-c-debug",
"type": "cmake-executable",
"targetName": "spine-c",
"buildType": "Debug",
"watchPaths": [
"**/CMakeLists.txt",
"src/**/*.{c,cpp,h}",
"cmake/**/*.cmake"
]
}
]
}
Poltergeist automatically optimizes watch patterns using brace expansion and redundancy elimination:
- Brace Expansion: Consolidates similar paths (e.g.,
foo/{bar,baz}/**/*.c
) - Redundancy Elimination: Removes subdirectory patterns when parent is already watched
- Size Reduction: Typically reduces configuration size by 40-70%
// Before optimization:
"watchPaths": [
"spine-c-unit-tests/memory/**/*.{c,cpp,h}",
"spine-c-unit-tests/minicppunit/**/*.{c,cpp,h}",
"spine-c-unit-tests/tests/**/*.{c,cpp,h}",
"spine-c/include/**/*.{c,cpp,h}",
"spine-c/src/**/*.{c,cpp,h}"
]
// After optimization (automatic):
"watchPaths": [
"spine-c-unit-tests/**/*.{c,cpp,h}",
"spine-c/include/**/*.{c,cpp,h}",
"spine-c/src/**/*.{c,cpp,h}"
]
This happens automatically during poltergeist init
.
Optimize Poltergeist for your project size and needs:
{
"performance": {"profile": "conservative|balanced|aggressive", "autoOptimize": true}
}
- conservative: Maximum file coverage, small projects
- balanced: Default, good performance/coverage balance
- aggressive: Maximum performance, large projects
Automatically builds what you're working on first using focus detection and priority scoring:
{
"buildScheduling": {
"parallelization": 2,
"prioritization": {"enabled": true, "focusDetectionWindow": 300000}
}
}
How it works: Analyzes file change patterns → prioritizes active targets → builds in smart order → faster feedback loops
Poltergeist includes 70+ optimized exclusion patterns:
- Version Control:
.git
,.svn
,.hg
- Build Artifacts:
node_modules
,DerivedData
,target/
,build/
- IDE Files:
.vscode
,.idea
,*.xcworkspace
- OS Files:
.DS_Store
,Thumbs.db
- Project-specific: Language and framework-specific patterns
Custom exclusions, environment variables, timeouts
{
"watchman": {
"rules": [{"pattern": "**/test_results/**", "action": "ignore"}]
},
"targets": [
{
"name": "backend",
"buildCommand": "npm run build:prod",
"environment": {"NODE_ENV": "production", "API_URL": "https://api.com"},
"maxRetries": 3,
"settlingDelay": 2000
}
]
}
Poltergeist loads configuration once at startup. Configuration changes require a restart to take effect:
# Restart to apply configuration changes
poltergeist restart
# Or restart specific target only
poltergeist restart --target my-app
When to Restart:
- After changing build commands or watch paths
- After modifying notification settings
- After adjusting performance profiles
- After updating exclusion rules
Project configuration examples
{
"version": "1.0",
"projectType": "swift",
"targets": [
{
"name": "cli-tool",
"type": "executable",
"buildCommand": "swift build -c release",
"outputPath": "./.build/release/MyTool",
"watchPaths": ["{Sources,Tests}/**/*.swift", "Package.swift"]
},
{
"name": "tests",
"type": "test",
"testCommand": "swift test",
"watchPaths": ["{Sources,Tests}/**/*.swift", "Package.swift"]
}
]
}
{
"version": "1.0",
"projectType": "mixed",
"targets": [
{
"name": "swift-backend",
"type": "executable",
"buildCommand": "./scripts/build-swift.sh",
"outputPath": "./bin/backend",
"watchPaths": ["{Backend,Shared}/**/*.swift"]
},
{
"name": "react-frontend",
"type": "executable",
"buildCommand": "npm run build",
"outputPath": "./frontend/dist",
"watchPaths": ["frontend/src/**/*.{ts,tsx,js,jsx,css,scss}"]
},
{
"name": "mac-app",
"type": "app-bundle",
"bundleId": "com.example.myapp",
"buildCommand": "xcodebuild -scheme MyApp",
"autoRelaunch": true,
"watchPaths": ["{MacApp,Shared}/**/*.{swift,xib,storyboard}"]
}
]
}
{
"version": "1.0",
"projectType": "node",
"targets": [
{
"name": "api-dev",
"type": "docker",
"imageName": "myapp/api",
"buildCommand": "docker build -f docker/Dockerfile.dev -t myapp/api:dev .",
"watchPaths": ["src/**/*.{js,ts}", "{package,package-lock}.json", "docker/Dockerfile.dev"]
}
]
}
{
"version": "1.0",
"projectType": "mixed",
"targets": [
{
"name": "libspine-debug",
"type": "library",
"buildCommand": "./build.sh",
"outputPath": "./build/libspine-c.a",
"watchPaths": [
"{src,include}/**/*.{c,cpp,h}",
"{CMakeLists.txt,CMakePresets.json}"
],
"environment": { "CMAKE_BUILD_TYPE": "Debug" }
}
],
"watchman": {
"excludeDirs": ["build", "target"]
}
}
{
"version": "1.0",
"projectType": "swift",
"targets": [
{
"name": "universal-app",
"type": "app-bundle",
"buildCommand": "xcodebuild -scheme UniversalApp -sdk macosx",
"watchPaths": [
"**/*.{swift,m,h}",
"**/*.{xcodeproj,xcconfig,entitlements,plist}",
"**/*.{xib,storyboard,xcassets}"
],
"settlingDelay": 1500 // Only if needed, default is 1000ms
}
]
}
{
"version": "1.0",
"projectType": "mixed",
"targets": [
{
"name": "peekaboo-cli",
"type": "executable",
"buildCommand": "./scripts/build-swift-debug.sh",
"outputPath": "./peekaboo",
"watchPaths": [
"{Core,Apps/CLI}/**/*.swift"
],
"icon": "./assets/icon_512x512@2x.png"
},
{
"name": "peekaboo-mac",
"type": "app-bundle",
"platform": "macos",
"buildCommand": "./scripts/build-mac-debug.sh",
"bundleId": "boo.peekaboo.mac.debug",
"autoRelaunch": true,
"watchPaths": [
"Apps/Mac/**/*.{swift,storyboard,xib}",
"Core/**/*.swift"
]
}
]
}
Poltergeist uses a distributed architecture where each project runs its own independent background process:
# Terminal 1 - Project A
cd ~/projects/my-app
poltergeist haunt # Starts separate background process for my-app
# Terminal 2 - Project B
cd ~/projects/spine-c
poltergeist haunt # Starts separate background process for spine-c
# Terminal 3 - From anywhere
cd ~
poltergeist status # Shows ALL projects: my-app + spine-c
- Isolation: Each
poltergeist haunt
spawns an independent Node.js process - State Discovery: Commands scan
/tmp/poltergeist/
to find all active projects - Global Commands:
status
,clean
, etc. work across all projects simultaneously - Per-Project Commands:
stop --target
,restart --target
affect specific targets
- Reliability: One project crashing doesn't affect others
- Flexibility: Start/stop projects independently
- Performance: No single bottleneck across all projects
- Cross-Terminal: Start in one terminal, manage from another
- Scalability: Handle 10+ projects without performance degradation
The Node.js CLI and macOS app communicate through shared state files, not direct IPC:
Node.js CLI Process macOS Native App
↓ ↓
Builds targets Monitors state
Updates state Shows notifications
↓ ↓
┌─────────────────────────────┐
│ /tmp/poltergeist/ │
│ ├── project-a.state │
│ ├── project-b.state │
│ └── project-c.state │
└─────────────────────────────┘
Shared State Files
This design enables:
- Platform Independence: CLI works without macOS app
- Real-time Sync: macOS app instantly reflects CLI changes
- Crash Resilience: Either component can restart independently
Poltergeist uses a lock-free state management system with atomic file operations to ensure data consistency across multiple processes and tools.
- Unix/Linux/macOS:
/tmp/poltergeist/
- Windows:
%TEMP%\poltergeist\
- File Pattern:
{projectName}-{hash}-{target}.state
/tmp/poltergeist/
├── my-app-abc123-frontend.state # Frontend target
├── my-app-abc123-backend.state # Backend target
├── spine-c-def456-debug.state # CMake debug build
├── another-project-ghi789-main.state # Main executable
└── ...
All state updates use atomic writes to prevent corruption:
- Write to temp file:
{target}.state.tmp.{pid}
- Atomic rename:
mv temp → {target}.state
- Lock-free: No file locking, no deadlocks
This ensures state files are never partially written and can be safely read by multiple processes simultaneously.
{
"version": "1.0",
"projectPath": "/Users/dev/my-project",
"projectName": "my-project",
"target": "frontend",
"process": {
"pid": 12345,
"hostname": "MacBook-Pro.local",
"isActive": true,
"startTime": "2025-08-05T20:15:30.000Z",
"lastHeartbeat": "2025-08-05T20:16:00.000Z"
},
"lastBuild": {
"status": "success|failure|building|idle",
"timestamp": "2025-08-05T20:15:47.500Z",
"duration": 2500,
"gitHash": "abc123f",
"builder": "CMake-Executable/Ninja",
"errorSummary": "Optional error message"
},
"appInfo": {
"bundleId": "com.example.myapp",
"outputPath": "./build/Debug/MyApp.app",
"iconPath": "./assets/icon.png"
}
}
Each Poltergeist process updates its heartbeat every 30 seconds:
- Active Process:
lastHeartbeat
within 30 seconds →isActive: true
- Stale Process:
lastHeartbeat
older than 30 seconds →isActive: false
- Automatic Cleanup:
poltergeist clean
removes stale state files
Poltergeist uses structured JSON logs with Winston for machine-readable output:
{"timestamp":"2025-08-05T20:15:30.123Z","level":"info","message":"Build completed successfully","target":"frontend","duration":2500,"gitHash":"abc123f"}
{"timestamp":"2025-08-05T20:15:35.456Z","level":"error","message":"Build failed","target":"backend","exitCode":1,"errorSummary":"Compilation error in main.cpp:42"}
- Location:
.poltergeist.log
in project root (configurable vialogging.file
) - Rotation: Automatic rotation prevents unlimited growth
- Multi-Target: Single log file contains all targets with filtering support
- Real-time:
poltergeist logs --follow
for live monitoring - Build Observation: Log files provide detailed build progress and error details beyond state files
# View recent logs
poltergeist logs # Last 50 lines, all targets
poltergeist logs --target frontend # Filter by target
poltergeist logs --lines 100 # Show more lines
# Follow logs in real-time
poltergeist logs --follow # All targets
poltergeist logs --follow --target backend
# JSON output for processing
poltergeist logs --json | jq '.level == "error"'
Poltergeist provides multiple layers for observing build status and progress:
# Quick status check - current build state only
jq -r '.lastBuild.status' /tmp/poltergeist/my-project-*-frontend.state
# Output: "success" | "failure" | "building" | "idle"
# Get build duration and error summary
jq -r '.lastBuild | "\(.status) - \(.duration)ms - \(.errorSummary // "no errors")"' /tmp/poltergeist/*.state
# Watch build progress in real-time
poltergeist logs --follow --target frontend
# Find recent build failures with details
poltergeist logs --json | jq 'select(.level == "error" and .target == "frontend")'
# Monitor build times over time
poltergeist logs --json | jq 'select(.message | contains("Build completed")) | {target, duration, timestamp}'
# Terminal 1: Watch logs for detailed progress
poltergeist logs --follow
# Terminal 2: Check current status across all projects
watch -n 2 'poltergeist status'
# Terminal 3: Monitor specific build metrics
watch -n 5 'find /tmp/poltergeist -name "*.state" -exec jq -r "\"\\(.target): \\(.lastBuild.status) (\\(.lastBuild.duration // 0)ms)\"" {} \;'
Key Differences:
- State Files: Current snapshot, fast access, minimal details
- Log Files: Complete history, detailed errors, build output, timestamps
- Combined: State files for quick checks, logs for debugging and analysis
The state files are designed for external tool integration:
# Check if project is building
if jq -r '.lastBuild.status' /tmp/poltergeist/my-project-*.state | grep -q "building"; then
echo "Build in progress..."
fi
// VS Code extension example
const stateFiles = glob('/tmp/poltergeist/*.state');
const buildStatuses = stateFiles.map(file => JSON.parse(fs.readFileSync(file)));
# GitHub Actions example
- name: Wait for Poltergeist build
run: |
while [[ $(jq -r '.lastBuild.status' /tmp/poltergeist/*-main.state) == "building" ]]; do
sleep 5
done
This architecture enables rich integrations while maintaining simplicity and reliability across all supported platforms.
- Node.js 20+ for CLI development
- Xcode 15+ for macOS app development
- Watchman for file watching
# Build from source
git clone https://github.com/steipete/poltergeist.git
cd poltergeist && npm install && npm run build
# Development commands
npm test # Run tests
npm run dev # Auto-rebuild mode
npm run lint # Code quality checks
npm run typecheck # Type validation
# Navigate to macOS app
cd apps/mac
# Build and run
xcodebuild -project Poltergeist.xcodeproj -scheme Poltergeist build
open Poltergeist.xcodeproj
# Code quality
./scripts/lint.sh # SwiftLint checks
./scripts/format.sh # swift-format fixes
Our comprehensive CI/CD pipeline ensures code quality across both platforms:
- Multi-platform testing: Node.js 20/22 on Ubuntu, macOS, and Windows
- Swift 6 validation: Strict concurrency checking and modern Swift practices
- Code quality: SwiftLint, swift-format, Biome, and TypeScript checks
- Automated releases: Dual-platform releases with both CLI (.tgz) and macOS app (.dmg/.zip)
- Test coverage: Comprehensive coverage reporting with Codecov
Project structure and contributing guidelines
poltergeist/
├── src/
│ ├── builders/ # Target-specific builders
│ ├── cli.ts # Command line interface
│ ├── poltergeist.ts # Core application logic
│ ├── priority-engine.ts # Intelligent priority scoring
│ ├── build-queue.ts # Smart build queue management
│ ├── state.ts # State management system
│ └── watchman.ts # Watchman file watching
├── test/ # Vitest test files
└── dist/ # Compiled JavaScript output
Contributions welcome! Requirements:
- Tests pass:
npm test
- Code formatted:
npm run format
- Linting passes:
npm run lint
- Types check:
npm run typecheck
- No backwards compatibility: Clean breaks over legacy support
- Type safety first: Compile-time safety over runtime flexibility
- Performance over features: Optimize for large projects
- Simple over complex: Clean APIs over extensive configuration
For detailed information about releases, bug fixes, and improvements, see CHANGELOG.md.
MIT License - see LICENSE file for details.
Created and maintained by Peter Steinberger
Built with these excellent open source projects:
- Watchman - Facebook's efficient file watching service
- Commander.js - Complete CLI framework
- Zod - TypeScript-first schema validation with static type inference
- Winston - Universal logging library with support for multiple transports
- TypeScript - JavaScript with syntax for types
- Vitest - Blazing fast unit test framework
- Biome - Fast formatter and linter for JavaScript, TypeScript, and more
- TSX - TypeScript execute and REPL for Node.js
- TypeDoc - Documentation generator for TypeScript projects
- Chalk - Terminal string styling done right
- Ora - Elegant terminal spinners
- Node Notifier - Cross-platform native notifications
- Picomatch - Blazing fast and accurate glob matcher
- Write File Atomic - Write files atomically and reliably
- fb-watchman - JavaScript client for Facebook's Watchman service
- All contributors and users who have helped shape Poltergeist
- The open source community for creating these amazing tools