README
ΒΆ
devloop: Intelligent Development Workflow Orchestrator
devloop is a generic, multi-variant tool designed to streamline development workflows, particularly within Multi-Component Projects (MCPs) (no not that MCP). It combines functionalities inspired by live-reloading tools like air (for Go) and build automation tools like make, focusing on simple, configuration-driven orchestration of tasks based on file system changes.
ποΈ Architecture Overview
Devloop operates in three distinct modes to support different development scenarios:
1. Standalone Mode (Default)
The simplest mode for individual projects. Devloop runs as a single daemon process with an embedded gRPC server.
βββββββββββββββββββββββ
β Your Project β
β βββββββββββββββββ β
β β .devloop.yamlβ β
β βββββββββ¬ββββββββ β
β β β
β βββββββββΌββββββββ β
β β devloop β β
β β (daemon) β β
β β βββββββββββββ β β
β β βgRPC Serverβ β β
β β βββββββββββββ β β
β βββββββββββββββββ β
βββββββββββββββββββββββ
Use Case: Single project development, simple setup.
2. Agent Mode
Devloop connects to a central gateway, ideal for multi-component projects where you want centralized monitoring.
Project A Project B
ββββββββββββββββ ββββββββββββββββ
β.devloop.yaml β β.devloop.yaml β
β β β β
β devloop β β devloop β
β (agent) β β (agent) β
ββββββββ¬ββββββββ ββββββββ¬ββββββββ
β β
βββββββββββ βββββββββββββββ
β β
ββββΌββββΌβββ
β Gateway β
β β
β :8080 β
βββββββββββ
Use Case: Microservices, monorepos, distributed development
3. Gateway Mode
Acts as a central hub accepting connections from multiple agents, providing a unified interface for monitoring and control.
devloop --mode gateway --gateway-port 8080
Features:
- Centralized logging and monitoring
- Unified HTTP/gRPC API for all connected projects
- Real-time status updates from all agents
- Cross-project orchestration capabilities
π Prerequisites
Before installing devloop, ensure you have:
- Go 1.20 or higher installed (Download Go)
- Git (for version control operations)
- $GOPATH/bin added to your system's PATH
Supported Platforms
- Linux (amd64, arm64)
- macOS (Intel, Apple Silicon)
- Windows (amd64) - Note: Process management may have limitations on Windows
π Getting Started
- Install
devloop:go install github.com/panyam/devloop@latest - Create a
.devloop.yamlfile in your project's root directory:rules: - name: "Go Backend Build and Run" watch: - action: "include" patterns: - "**/*.go" - "go.mod" - "go.sum" commands: - "echo 'Building backend...'" - "go build -o ./bin/server ./cmd/server" - "./bin/server" # This starts the server and is long running - Run
devloop:devloop -c .devloop.yaml
devloop will now watch your files and automatically rebuild and restart your backend server whenever you make changes to your Go code.
π Examples
Example 1: Full-Stack Web Application
# .devloop.yaml
settings:
prefix_logs: true
prefix_max_length: 12
rules:
- name: "Go API Server"
prefix: "api"
watch:
- action: "include"
patterns:
- "cmd/api/**/*.go"
- "internal/**/*.go"
- "go.mod"
- "go.sum"
commands:
- "go build -o bin/api ./cmd/api"
- "bin/api"
- name: "React Frontend"
prefix: "frontend"
watch:
- action: "include"
patterns:
- "web/src/**/*.{js,jsx,ts,tsx}"
- "web/src/**/*.css"
- "web/package.json"
commands:
- "cd web && npm run dev"
- name: "Database Migrations"
prefix: "db"
watch:
- action: "include"
patterns:
- "migrations/**/*.sql"
commands:
- "migrate -path ./migrations -database postgres://localhost/myapp up"
- name: "API Documentation"
prefix: "docs"
watch:
- action: "include"
patterns:
- "api/**/*.proto"
- "docs/**/*.md"
commands:
- "protoc --doc_out=./docs --doc_opt=markdown,api.md api/*.proto"
Example 2: Microservices with Agent/Gateway Mode
Gateway Server (central monitoring):
# Start the gateway on a dedicated server
devloop --mode gateway --gateway-port 8080
Service A (authentication service):
# auth-service/.devloop.yaml
rules:
- name: "Auth Service"
prefix: "auth"
watch:
- action: "include"
patterns:
- "**/*.go"
- "go.mod"
commands:
- "go build -o bin/auth ./cmd/auth"
- "bin/auth --port 3001"
# Connect to gateway
devloop --mode agent --gateway-url gateway.local:8080 -c .devloop.yaml
Service B (user service):
# user-service/.devloop.yaml
rules:
- name: "User Service"
prefix: "user"
watch:
- action: "include"
patterns:
- "**/*.go"
- "go.mod"
commands:
- "go build -o bin/user ./cmd/user"
- "bin/user --port 3002"
Example 3: Python Data Science Project
# .devloop.yaml
rules:
- name: "Jupyter Lab"
prefix: "jupyter"
watch:
- action: "include"
patterns:
- "notebooks/**/*.ipynb"
commands:
- "jupyter lab --no-browser --port=8888"
- name: "Model Training"
prefix: "train"
watch:
- action: "include"
patterns:
- "src/**/*.py"
- "configs/**/*.yaml"
- action: "exclude"
patterns:
- "**/__pycache__/**"
- "**/*.pyc"
commands:
- "python src/train.py --config configs/model.yaml"
- name: "Tests"
prefix: "test"
watch:
- action: "include"
patterns:
- "src/**/*.py"
- "tests/**/*.py"
commands:
- "pytest tests/ -v --color=yes"
Example 4: Rust Project with WASM
# .devloop.yaml
rules:
- name: "Rust Backend"
prefix: "rust"
watch:
- action: "include"
patterns:
- "src/**/*.rs"
- "Cargo.toml"
- "Cargo.lock"
commands:
- "cargo build --release"
- "cargo run --release"
- name: "WASM Build"
prefix: "wasm"
watch:
- action: "include"
patterns:
- "wasm/**/*.rs"
- "wasm/Cargo.toml"
commands:
- "cd wasm && wasm-pack build --target web --out-dir ../web/pkg"
- name: "Web Server"
prefix: "web"
watch:
- action: "include"
patterns:
- "web/**/*.{html,js,css}"
- "web/pkg/**/*"
commands:
- "cd web && python -m http.server 8000"
Example 5: Mobile App Development
# .devloop.yaml
settings:
prefix_logs: true
rules:
- name: "React Native Metro"
prefix: "metro"
watch:
- action: "include"
patterns:
- "src/**/*.{js,jsx,ts,tsx}"
- "package.json"
commands:
- "npx react-native start --reset-cache"
- name: "iOS Build"
prefix: "ios"
watch:
- action: "include"
patterns:
- "ios/**/*.{m,h,swift}"
- "ios/**/*.plist"
commands:
- "cd ios && pod install"
- "npx react-native run-ios --simulator='iPhone 14'"
- name: "Android Build"
prefix: "android"
watch:
- action: "include"
patterns:
- "android/**/*.{java,kt,xml}"
- "android/**/*.gradle"
commands:
- "cd android && ./gradlew clean"
- "npx react-native run-android"
Example 6: Docker Compose Integration
# .devloop.yaml
rules:
- name: "Docker Services"
prefix: "docker"
watch:
- action: "include"
patterns:
- "docker-compose.yml"
- "**/*.Dockerfile"
- ".env"
commands:
- "docker-compose down"
- "docker-compose build"
- "docker-compose up"
- name: "Go Service"
prefix: "go"
watch:
- action: "include"
patterns:
- "services/api/**/*.go"
commands:
- "docker-compose restart api"
π Comparison with Similar Tools
Quick Use Case Comparison
| Solution | Good For | Not Great For |
|---|---|---|
| Docker Compose | Production-like isolation | Fast local iteration |
| tmux/screen | Terminal management | Process lifecycle |
| Make -j | Build orchestration | Long-running services |
| Foreman/Overmind | Procfile-based apps | File watching & triggers |
| Shell scripts | Simple automation | Complex process management |
| devloop | Multi-service development | Production deployment |
Feature Comparison
| Feature | devloop | air | nodemon | watchexec |
|---|---|---|---|---|
| Language Focus | Multi-language | Go | Node.js | Any |
| Parallel Rules | β Yes | β No | β No | β No |
| Distributed Mode | β Agent/Gateway | β No | β No | β No |
| Process Groups | β Yes | β Yes | β Yes | β οΈ Limited |
| Log Prefixing | β Yes | β No | β No | β No |
| gRPC/HTTP API | β Yes | β No | β No | β No |
| Config Format | YAML | TOML | JSON | CLI args |
| Debouncing | β Yes | β Yes | β Yes | β Yes |
| Exclude Patterns | β Yes | β Yes | β Yes | β Yes |
| Build Tool | β No | β Yes | β No | β No |
| Live Reload | β Via commands | β Built-in | β No | β No |
| Multi-Project | β Yes | β No | β No | β No |
When to Use devloop
- Multi-component projects: When you need to orchestrate multiple services/components
- Microservices development: Centralized monitoring with agent/gateway mode
- Complex workflows: When you need multiple parallel build/watch tasks
- API monitoring: Built-in gRPC/HTTP endpoints for integration
- Cross-language projects: Not tied to a specific language ecosystem
When to Use Alternatives
- air: Go-only projects with built-in compilation and live-reload
- nodemon: Simple Node.js projects with minimal configuration
- watchexec: Single-command execution with complex file watching needs
β¨ Key Features
- Parallel & Concurrent Task Running: Define rules for different parts of your project (backend, frontend, etc.) and
devloopwill run them concurrently. - Intelligent Change Detection: Uses glob patterns to precisely match file changes, triggering only the necessary commands.
- Robust Process Management: Automatically terminates old processes before starting new ones, preventing zombie processes and ensuring a clean state.
- Debounced Execution: Rapid file changes trigger commands only once, preventing unnecessary builds and restarts.
- Command Log Prefixing: Prepends a customizable prefix to each line of your command's output, making it easy to distinguish logs from different processes.
- .air.toml Converter: Includes a built-in tool to convert your existing
.air.tomlconfiguration into adevlooprule.
βοΈ Configuration Reference
Complete Configuration Structure
# .devloop.yaml
settings: # Optional: Global settings
prefix_logs: boolean # Enable/disable log prefixing (default: true)
prefix_max_length: number # Max length for prefixes (default: unlimited)
rules: # Required: Array of rules
- name: string # Required: Unique rule identifier
prefix: string # Optional: Custom log prefix (defaults to name)
workdir: string # Optional: Working directory for commands
run_on_init: boolean # Optional: Run on startup (default: true)
env: # Optional: Environment variables
KEY: "value"
watch: # Required: File watch configuration
- action: string # Required: "include" or "exclude"
patterns: [string] # Required: Glob patterns
commands: [string] # Required: Shell commands to execute
Settings Options
| Option | Type | Default | Description |
|---|---|---|---|
prefix_logs |
boolean | true |
Prepend rule name/prefix to each output line |
prefix_max_length |
integer | unlimited | Truncate/pad prefixes to this length for alignment |
Rule Options
| Option | Type | Required | Description |
|---|---|---|---|
name |
string | β | Unique identifier for the rule |
prefix |
string | β | Custom prefix for log output (overrides name) |
workdir |
string | β | Working directory for command execution |
env |
map | β | Additional environment variables |
watch |
array | β | File patterns to monitor |
commands |
array | β | Commands to execute when files change |
run_on_init |
boolean | β | Run commands on startup (default: true) |
Watch Configuration
Each watch entry consists of:
| Field | Type | Values | Description |
|---|---|---|---|
action |
string | include, exclude |
Whether to trigger on or ignore matches |
patterns |
array | glob patterns | File patterns using doublestar syntax |
Glob Pattern Syntax
*- Matches any sequence of non-separator characters**- Matches any sequence of characters including path separators?- Matches any single non-separator character[abc]- Matches any character in the set[a-z]- Matches any character in the range{a,b}- Matches either pattern a or b
Example with All Options
settings:
prefix_logs: true
prefix_max_length: 12
rules:
- name: "Backend API"
prefix: "api"
workdir: "./backend"
env:
NODE_ENV: "development"
PORT: "3000"
DATABASE_URL: "postgres://localhost/myapp"
watch:
- action: "exclude"
patterns:
- "**/vendor/**"
- "**/*_test.go"
- "**/.*"
- action: "include"
patterns:
- "**/*.go"
- "go.mod"
- "go.sum"
commands:
- "echo 'Building API server...'"
- "go mod tidy"
- "go build -tags dev -o bin/api ./cmd/api"
- "./bin/api --dev"
Pattern Matching Examples
# Match all Go files
"**/*.go"
# Match Go files only in src directory
"src/**/*.go"
# Match test files
"**/*_test.go"
# Match multiple extensions
"**/*.{js,jsx,ts,tsx}"
# Match specific directory
"cmd/server/**/*"
# Match top-level files only
"*.go"
# Match hidden files
"**/.*"
Command Execution Behavior
- Sequential Execution: Commands run in the order specified
- Shell Execution: Each command runs via
bash -c - Process Groups: Commands run in separate process groups for clean termination
- Environment Inheritance: Commands inherit parent environment plus
envvariables - Working Directory: Commands execute in
workdir(or current directory if not set) - Process Termination: Previous instances receive SIGTERM when rule re-triggers
- Error Handling: Failed commands don't stop subsequent commands in the list
Environment Variable Precedence
- System environment variables
- Variables from
envconfiguration (overrides system vars) - devloop internal variables:
DEVLOOP_RULE_NAME- Current rule nameDEVLOOP_TRIGGER_FILE- File that triggered the rule
π¦ Installation
Via Go Install (Recommended)
go install github.com/panyam/devloop@latest
This command will compile the devloop executable and place it in your Go binary directory, making it globally accessible.
Alternatively, to build the executable locally:
go build -o devloop
π API Reference
Devloop provides both gRPC and REST APIs for monitoring and control. The REST API is available via gRPC-Gateway.
REST API Endpoints
Base URL: http://localhost:8080 (default gateway port)
List All Projects
GET /projects
Returns all registered devloop projects and their connection status.
Response:
{
"projects": [
{
"project_id": "auth-service",
"project_root": "/path/to/auth-service",
"connection_status": "CONNECTED"
}
]
}
Get Project Configuration
GET /projects/{projectId}/config
Returns the full configuration for a specific project.
Response:
{
"config_json": "{\"rules\":[{\"name\":\"backend\",\"commands\":[\"go run .\"]}]}"
}
Get Rule Status
GET /projects/{projectId}/status/{ruleName}
Returns the current status of a specific rule.
Response:
{
"rule_name": "backend",
"is_running": true,
"started_at": "1704092400000",
"last_build_time": "1704092400000",
"last_build_status": "SUCCESS"
}
Trigger Rule Manually
POST /projects/{projectId}/trigger/{ruleName}
Manually triggers a rule execution.
Response:
{
"success": true,
"message": "Rule 'backend' triggered successfully"
}
List Watched Paths
GET /projects/{projectId}/watched-paths
Returns all glob patterns being watched by the project.
Response:
{
"patterns": [
"**/*.go",
"go.mod",
"go.sum"
]
}
Read File Content
GET /projects/{projectId}/file-content?path={filePath}
Reads a file from the project directory.
Response:
{
"content": "package main\n\nfunc main() {\n // ...\n}"
}
Stream Real-time Logs
GET /projects/{projectId}/stream/logs/{ruleName}?filter={optional}
Server-sent events stream for real-time logs.
Response (SSE):
data: {"project_id":"backend","rule_name":"api","line":"Starting server...","timestamp":"1704092400000"}
data: {"project_id":"backend","rule_name":"api","line":"Server listening on :8080","timestamp":"1704092401000"}
Get Historical Logs
GET /projects/{projectId}/historical-logs/{ruleName}?filter={optional}&startTime={ms}&endTime={ms}
Retrieve historical logs with optional time range.
Parameters:
filter: Optional text filterstartTime: Start timestamp in millisecondsendTime: End timestamp in milliseconds
Response:
{
"logs": [
{
"project_id": "backend",
"rule_name": "api",
"line": "Request processed",
"timestamp": "1704092400000"
}
]
}
gRPC Interface
For direct gRPC access, use the following service definitions:
service GatewayClientService {
rpc ListProjects(ListProjectsRequest) returns (ListProjectsResponse);
rpc GetProjectConfig(GetProjectConfigRequest) returns (GetProjectConfigResponse);
rpc GetRuleStatus(GetRuleStatusRequest) returns (GetRuleStatusResponse);
rpc TriggerRule(TriggerRuleRequest) returns (TriggerRuleResponse);
rpc ListWatchedPaths(ListWatchedPathsRequest) returns (ListWatchedPathsResponse);
rpc ReadFileContent(ReadFileContentRequest) returns (ReadFileContentResponse);
rpc StreamLogs(StreamLogsRequest) returns (stream LogLine);
rpc GetHistoricalLogs(GetHistoricalLogsRequest) returns (GetHistoricalLogsResponse);
}
Client Examples
JavaScript/TypeScript
// Fetch all projects
const response = await fetch('http://localhost:8080/projects');
const data = await response.json();
// Stream logs using EventSource
const events = new EventSource('http://localhost:8080/projects/backend/stream/logs/api');
events.onmessage = (event) => {
const log = JSON.parse(event.data);
console.log(`[${log.rule_name}] ${log.line}`);
};
Python
import requests
import sseclient
# Get rule status
response = requests.get('http://localhost:8080/projects/backend/status/api')
status = response.json()
# Stream logs
response = requests.get('http://localhost:8080/projects/backend/stream/logs/api', stream=True)
client = sseclient.SSEClient(response)
for event in client.events():
log = json.loads(event.data)
print(f"[{log['rule_name']}] {log['line']}")
Go
// Using the generated gRPC client
conn, _ := grpc.Dial("localhost:8080", grpc.WithInsecure())
client := pb.NewGatewayClientServiceClient(conn)
// List projects
resp, _ := client.ListProjects(context.Background(), &pb.ListProjectsRequest{})
for _, project := range resp.Projects {
fmt.Printf("Project: %s (%s)\n", project.ProjectId, project.ConnectionStatus)
}
π€ MCP Server Integration
Devloop Gateway can act as a Model Context Protocol (MCP) server, enabling AI agents and LLMs (like Claude) to monitor and control your development workflows.
What is MCP?
Model Context Protocol (MCP) is a standard that allows AI assistants to interact with external tools and systems. By exposing devloop as an MCP server, you enable:
- AI-Assisted Development: LLMs can trigger builds, run tests, and analyze errors
- Automated Workflows: AI agents can respond to build failures and suggest fixes
- Intelligent Monitoring: Query project status and logs through natural language
Setting Up Devloop MCP Server
1. Start Gateway in MCP Mode
# Start gateway with MCP server enabled
devloop --mode gateway --gateway-port 8080 --mcp-port 3000
2. Configure MCP Server Settings
Create mcp-config.json:
{
"name": "devloop-mcp",
"version": "1.0.0",
"description": "Control and monitor development workflows",
"tools": {
"project_management": {
"enabled": true,
"allowed_operations": ["list", "status", "info"]
},
"build_control": {
"enabled": true,
"require_confirmation": false,
"timeout_seconds": 300
},
"log_access": {
"enabled": true,
"max_lines": 1000,
"allow_streaming": true
}
}
}
3. Connect Your Devloop Agents
# In each project directory
devloop --mode agent --gateway-url localhost:8080 \
--project-id "my-backend" \
-c .devloop.yaml
Using with Claude Desktop
Add to your Claude Desktop configuration (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"devloop": {
"command": "devloop",
"args": ["mcp-client", "--gateway-url", "localhost:3000"],
"env": {
"DEVLOOP_API_KEY": "your-optional-api-key"
}
}
}
}
Available MCP Tools
When connected, the following tools are available to AI agents:
Project Management
// List all projects
await use_mcp_tool("devloop", "list_projects", {
filter: "backend",
include_status: true
});
// Get project information
await use_mcp_tool("devloop", "project_info", {
project_id: "my-backend"
});
Build & Test Control
// Trigger a build
await use_mcp_tool("devloop", "trigger_build", {
project_id: "my-backend",
rule_name: "build",
wait_for_completion: true
});
// Run tests
await use_mcp_tool("devloop", "run_tests", {
project_id: "my-backend",
test_pattern: "**/*_test.go",
coverage: true
});
// Execute custom command
await use_mcp_tool("devloop", "execute_command", {
project_id: "my-backend",
command: "make lint",
timeout_seconds: 60
});
Monitoring & Logs
// Check status
await use_mcp_tool("devloop", "check_status", {
project_id: "my-backend",
rule_name: "api-server"
});
// Read recent logs
await use_mcp_tool("devloop", "read_logs", {
project_id: "my-backend",
lines: 100,
filter: "ERROR"
});
// Stream real-time output
await use_mcp_tool("devloop", "watch_output", {
project_id: "my-backend",
rule_name: "api-server",
duration_seconds: 30
});
Code Intelligence
// Read file content
await use_mcp_tool("devloop", "read_file", {
project_id: "my-backend",
file_path: "src/main.go"
});
// Search for files
await use_mcp_tool("devloop", "find_files", {
project_id: "my-backend",
pattern: "**/*_test.go"
});
// Analyze build errors
await use_mcp_tool("devloop", "analyze_errors", {
project_id: "my-backend",
include_suggestions: true
});
Example Workflows
1. AI-Assisted Debugging
# .devloop.yaml with MCP annotations
rules:
- name: "test"
mcp_exposed: true
mcp_description: "Run unit tests with coverage"
commands:
- "go test -v -coverprofile=coverage.out ./..."
- "go tool cover -html=coverage.out -o coverage.html"
AI Agent workflow:
- "Run the tests for the backend project"
- "Show me any failing tests"
- "Read the source file for the failing test"
- "Suggest a fix for the error"
2. Automated Build Pipeline
// AI agent can orchestrate complex workflows
const projects = await use_mcp_tool("devloop", "list_projects");
for (const project of projects) {
// Check if project needs rebuild
const status = await use_mcp_tool("devloop", "check_status", {
project_id: project.id
});
if (status.needs_rebuild) {
// Trigger build
const result = await use_mcp_tool("devloop", "trigger_build", {
project_id: project.id,
wait_for_completion: true
});
if (!result.success) {
// Analyze errors
const errors = await use_mcp_tool("devloop", "analyze_errors", {
project_id: project.id
});
console.log(`Build failed: ${errors.summary}`);
}
}
}
3. Multi-Project Coordination
# Gateway coordinates multiple services
# AI agent can manage the entire stack
// Start all services in correct order
const startOrder = ["database", "cache", "api", "frontend"];
for (const service of startOrder) {
await use_mcp_tool("devloop", "trigger_build", {
project_id: service,
rule_name: "start"
});
// Wait for service to be ready
let ready = false;
while (!ready) {
const status = await use_mcp_tool("devloop", "check_status", {
project_id: service
});
ready = status.is_running && status.health_check_passing;
await new Promise(r => setTimeout(r, 1000));
}
}
Security Considerations
Authentication
# mcp-config.json
{
"authentication": {
"required": true,
"type": "api_key",
"key_header": "X-Devloop-API-Key"
}
}
Access Control
{
"access_control": {
"allowed_projects": ["frontend", "backend"],
"forbidden_commands": ["rm", "sudo"],
"max_command_length": 500,
"rate_limit": {
"requests_per_minute": 60,
"concurrent_operations": 5
}
}
}
Troubleshooting MCP Integration
Connection Issues
# Test MCP server connectivity
curl http://localhost:3000/mcp/tools
# Check gateway logs
devloop logs --mode gateway --tail 100
Common Problems
-
"MCP server not found"
- Ensure gateway is running with
--mcp-portflag - Check firewall settings
- Ensure gateway is running with
-
"Tool execution failed"
- Verify project is connected as agent
- Check tool permissions in mcp-config.json
-
"Timeout waiting for response"
- Increase timeout in tool parameters
- Check if commands are hanging
Best Practices
- Use Descriptive Project IDs: Makes it easier for AI to identify projects
- Add MCP Descriptions: Document your rules for better AI understanding
- Set Reasonable Timeouts: Prevent long-running operations from blocking
- Monitor Rate Limits: Prevent AI from overwhelming your system
- Log AI Actions: Audit trail for debugging and security
π Usage
Running in Standalone Mode (Default)
Navigate to your project's root directory and execute:
devloop -c .devloop.yaml
- Use the
-cflag to specify the path to your.devloop.yamlconfiguration file. If omitted,devloopwill look for.devloop.yamlin the current directory.
Running in Agent Mode
To connect to a gateway:
devloop --mode agent --gateway-url localhost:8080 -c .devloop.yaml
Running in Gateway Mode
To start a central gateway:
devloop --mode gateway --gateway-port 8080
The gateway will accept connections from agents and provide a unified interface at http://localhost:8080.
Subcommands
devloop also supports subcommands for specific utilities:
convert
This subcommand allows you to convert an existing .air.toml configuration file (used by the air live-reloading tool) into a devloop-compatible .devloop.yaml rule.
devloop convert -i .air.toml
- Use the
-iflag to specify the path to the.air.tomlinput file. If omitted, it defaults to.air.tomlin the current directory. The converted output will be printed to standard output.
π Migrating from Air
If you're currently using Air for Go development, migrating to devloop is straightforward.
Quick Migration
-
Convert your .air.toml automatically:
devloop convert -i .air.toml > .devloop.yaml -
Review and adjust the generated configuration:
cat .devloop.yaml
Manual Migration Reference
Here's how Air configurations map to devloop:
| Air (.air.toml) | devloop (.devloop.yaml) |
|---|---|
root = "." |
workdir: "." |
tmp_dir = "tmp" |
Not needed (devloop doesn't use tmp) |
[build] section |
Single rule with commands |
bin = "./tmp/main" |
Part of commands |
cmd = "go build -o ./tmp/main ." |
commands: ["go build -o ./tmp/main ."] |
full_bin = "./tmp/main" |
commands: ["./tmp/main"] |
include_ext = ["go", "tpl"] |
patterns: ["**/*.go", "**/*.tpl"] |
exclude_dir = ["assets", "vendor"] |
action: "exclude" with patterns |
delay = 1000 |
Built-in debouncing |
[log] section |
Use settings.prefix_logs |
Example Migration
Before (air.toml):
root = "."
tmp_dir = "tmp"
[build]
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 1000
exclude_dir = ["assets", "tmp", "vendor"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = true
follow_symlink = false
full_bin = "./tmp/main"
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
kill_delay = "0s"
log = "build-errors.log"
send_interrupt = false
stop_on_error = true
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
time = false
[misc]
clean_on_exit = false
After (devloop.yaml):
settings:
prefix_logs: true
prefix_max_length: 10
rules:
- name: "Go App"
prefix: "go"
workdir: "."
watch:
- action: "exclude"
patterns:
- "assets/**"
- "tmp/**"
- "vendor/**"
- "**/*_test.go"
- action: "include"
patterns:
- "**/*.go"
- "**/*.tpl"
- "**/*.tmpl"
- "**/*.html"
commands:
- "go build -o ./tmp/main ."
- "./tmp/main"
Key Differences
- Multiple Rules: devloop supports multiple concurrent rules, while Air focuses on a single build process
- No Temp Directory: devloop doesn't require a temporary directory
- Better Process Management: devloop uses process groups for cleaner termination
- Distributed Mode: devloop supports agent/gateway architecture for multi-project setups
- API Access: devloop provides gRPC/REST APIs for monitoring and control
Advanced Migration Tips
-
For Complex Build Steps:
commands: - "go generate ./..." - "go mod tidy" - "go build -ldflags='-s -w' -o ./bin/app ./cmd/app" - "./bin/app" -
For Multiple Services:
rules: - name: "API Server" prefix: "api" watch: - action: "include" patterns: ["cmd/api/**/*.go", "internal/**/*.go"] commands: - "go build -o bin/api ./cmd/api" - "bin/api --port 8080" - name: "Worker" prefix: "worker" watch: - action: "include" patterns: ["cmd/worker/**/*.go", "internal/**/*.go"] commands: - "go build -o bin/worker ./cmd/worker" - "bin/worker" -
For Test Automation:
rules: - name: "Tests" prefix: "test" watch: - action: "include" patterns: ["**/*.go"] commands: - "go test -v ./..."
Migration Checklist
- Run
devloop convertto generate initial config - Review and adjust file patterns
- Update build commands if needed
- Test with
devloop -c .devloop.yaml - Remove
.air.tomland Air dependency - Update your README/documentation
- Update CI/CD scripts if applicable
Running the Orchestrator
devloop will start watching your files. When changes occur that match your defined rules, it will execute the corresponding commands. You will see log output indicating which rules are triggered and which commands are being run.
To stop devloop gracefully, press Ctrl+C (SIGINT). devloop will attempt to terminate any running child processes before exiting, ensuring a clean shutdown of your development environment.
π§ Troubleshooting
Common Issues and Solutions
1. Configuration File Not Found
Error: Failed to read config file: open .devloop.yaml: no such file or directory
Solutions:
- Ensure
.devloop.yamlexists in your current directory - Use
-cflag to specify the config path:devloop -c path/to/.devloop.yaml - Check file permissions:
ls -la .devloop.yaml
2. Commands Not Executing
Symptoms: File changes detected but commands don't run
Solutions:
- Verify glob patterns match your files:
# Test pattern matching find . -name "*.go" | grep -E "pattern" - Check command syntax - commands run via
bash -c - Ensure commands are in your PATH
- Add debug output to commands:
commands: - "echo 'Rule triggered for: $DEVLOOP_TRIGGER_FILE'" - "your-actual-command"
3. Process Won't Terminate
Symptoms: Old processes keep running after file changes
Solutions:
- Ensure your process handles SIGTERM properly
- For servers, implement graceful shutdown:
// Go example sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) <-sigChan server.Shutdown(context.Background()) - Use process managers that handle signals correctly
4. High CPU Usage
Symptoms: devloop consuming excessive CPU
Solutions:
- Reduce watch scope - avoid watching
node_modules,.git, etc.:watch: - action: "exclude" patterns: - "node_modules/**" - ".git/**" - "*.log" - action: "include" patterns: - "src/**/*.js" - Check for recursive file generation (logs writing to watched directories)
- Ensure proper debouncing is working
5. Port Already in Use
Error: listen tcp :8080: bind: address already in use
Solutions:
- Kill existing processes:
lsof -ti:8080 | xargs kill -9 - Use different ports for different rules
- Implement port checking in your startup scripts:
commands: - "kill $(lsof -ti:8080) || true" - "npm start"
6. Permission Denied Errors
Error: permission denied
Solutions:
- Check file permissions:
chmod +x your-script.sh - Run devloop with appropriate user permissions
- For privileged ports (<1024), use port forwarding or reverse proxy
7. Log Output Issues
Symptoms: Missing or garbled log output
Solutions:
- Enable log prefixing for clarity:
settings: prefix_logs: true prefix_max_length: 10 - Check if commands buffer output (use unbuffered mode):
commands: - "python -u script.py" # Unbuffered Python - "node --no-buffering app.js" # Unbuffered Node.js
8. Agent Can't Connect to Gateway
Error: Failed to connect to gateway
Solutions:
- Verify gateway is running:
curl http://gateway-host:8080/projects - Check network connectivity:
ping gateway-host - Ensure correct gateway URL format:
--gateway-url host:port - Check firewall rules allow connection
9. File Changes Not Detected
Symptoms: Modifying files doesn't trigger rules
Solutions:
- Verify file system supports inotify (Linux) or FSEvents (macOS)
- Check if you're editing files via network mount (may not trigger events)
- Ensure patterns are correct - use
**for recursive matching:# Wrong patterns: ["*.go"] # Only matches root directory # Correct patterns: ["**/*.go"] # Matches all subdirectories
10. Memory Leaks
Symptoms: Memory usage grows over time
Solutions:
- Ensure commands properly clean up resources
- Check for accumulating log files
- Monitor with:
ps aux | grep devloop - Restart devloop periodically if needed
Debug Mode
To get more detailed output for troubleshooting:
# Run with verbose logging (when implemented)
devloop -v -c .devloop.yaml
# Check devloop version
devloop --version
# Validate configuration
devloop validate -c .devloop.yaml
Getting Help
If you continue experiencing issues:
- Check existing issues: https://github.com/panyam/devloop/issues
- Create a minimal reproducible example
- Include your
.devloop.yamlconfiguration - Provide system information:
go version uname -a devloop --version
Log Interpretation
Understanding log prefixes:
[devloop]- Internal devloop operations[rule-name]- Output from your rule's commandsERROR- Critical errors requiring attentionWARN- Non-critical issuesINFO- General informationDEBUG- Detailed debugging information (verbose mode)
β FAQ
Why not just use Docker Compose?
Docker is excellent for production-like environments, but adds overhead for local development:
- Container rebuild times vs millisecond restarts
- File sync delays (especially on macOS)
- Resource consumption for multiple containers
- Debugging friction through container layers
- Configuration complexity for simple watch tasks
Devloop is designed for the tight feedback loop of development, where speed matters more than isolation.
Why not use tmux/screen with multiple panes?
Terminal multiplexers manage windows, not processes. Devloop provides:
- Unified logging with automatic prefixes
- Proper process lifecycle management
- Restart on file changes
- API access for monitoring and control
- Cross-platform consistency
How is this different from Foreman/Overmind?
Foreman and Overmind are great for Procfile-based applications. Devloop adds:
- File watching with automatic triggers
- Glob pattern matching for fine-grained control
- Multi-project orchestration via agent/gateway mode
- gRPC/REST API for programmatic access
- Integration with AI tools via MCP
Why not Make with parallel jobs?
Make is a build tool, not a process manager. It's not designed for:
- Long-running processes
- Restarting on file changes
- Managing process lifecycles
- Handling streaming logs from multiple sources
Can I use devloop in production?
Devloop is designed for development environments. For production, use proper orchestration tools like:
- Kubernetes for containerized workloads
- systemd for system services
- Docker Swarm or Nomad for distributed applications
How does devloop handle process cleanup?
Devloop uses process groups to ensure clean termination:
- Each rule's commands run in a separate process group
- On restart or shutdown, SIGTERM is sent to the entire group
- This prevents orphaned processes and ensures proper cleanup
Does devloop support Windows?
Yes, with some limitations:
- Process group management works differently on Windows
- Some signal handling features may behave differently
- File watching generally works well via fsnotify
Can I mix devloop with other tools?
Absolutely! Common patterns include:
- Using devloop to orchestrate multiple tools (air, nodemon, etc.)
- Running devloop alongside Docker for databases
- Combining with Make for complex build steps
β‘ Performance & Optimization
Performance Characteristics
devloop is designed for efficiency with minimal overhead:
- Memory Usage: ~10-20MB base + rule overhead
- CPU Usage: <1% when idle, scales with file system events
- Startup Time: <100ms for typical configurations
- File Watch Latency: <50ms from file change to command trigger
Optimization Tips
1. Minimize Watch Scope
# Bad - watches everything
watch:
- action: "include"
patterns: ["**/*"]
# Good - specific patterns
watch:
- action: "exclude"
patterns:
- "node_modules/**"
- ".git/**"
- "dist/**"
- "*.log"
- action: "include"
patterns:
- "src/**/*.js"
- "package.json"
2. Use Exclude Patterns First
Exclusions are processed before inclusions, making them more efficient:
watch:
- action: "exclude"
patterns: ["**/test/**", "**/*.test.js"]
- action: "include"
patterns: ["**/*.js"]
3. Optimize Commands
# Combine commands when possible
commands:
- "go build -o bin/app && ./bin/app"
# Use incremental builds
commands:
- "go build -i -o bin/app ./cmd/app"
4. Leverage Process Groups
devloop automatically manages process groups, but ensure your apps handle SIGTERM:
// Graceful shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigChan
cancel()
}()
5. Configure Debouncing
devloop has built-in debouncing to prevent command storms during rapid file changes.
Project Organization Tips
1. Monorepo Structure
rules:
- name: "Shared Libraries"
watch:
- action: "include"
patterns: ["packages/shared/**/*.ts"]
commands:
- "cd packages/shared && npm run build"
- name: "Service A"
watch:
- action: "include"
patterns:
- "services/service-a/**/*.go"
- "packages/shared/dist/**"
commands:
- "cd services/service-a && go run ."
2. Microservices with Gateway
# Central gateway
devloop --mode gateway --gateway-port 8080
# Each service
cd service-a && devloop --mode agent --gateway-url localhost:8080
cd service-b && devloop --mode agent --gateway-url localhost:8080
3. Development vs Production
# dev.devloop.yaml
rules:
- name: "Dev Server"
env:
NODE_ENV: "development"
commands:
- "npm run dev"
# prod.devloop.yaml
rules:
- name: "Prod Build"
env:
NODE_ENV: "production"
commands:
- "npm run build"
- "npm start"
4. Testing Strategy
rules:
- name: "Unit Tests"
watch:
- action: "include"
patterns: ["**/*.go"]
commands:
- "go test -short ./..."
- name: "Integration Tests"
watch:
- action: "include"
patterns: ["**/*.go", "docker-compose.yml"]
commands:
- "docker-compose up -d"
- "go test -tags=integration ./..."
- "docker-compose down"
Benchmarks
Typical performance for a medium-sized project (10K files, 5 rules):
| Operation | Time | Memory |
|---|---|---|
| Startup | 87ms | 15MB |
| File change detection | 12ms | +0.1MB |
| Rule trigger | 23ms | +0.5MB |
| Idle (watching) | - | 18MB |
Resource Limits
For large projects, consider system limits:
# Increase file watch limits (Linux)
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# Check current limits
cat /proc/sys/fs/inotify/max_user_watches
π οΈ Development Status & Roadmap
devloop is stable and ready for use. All core features are implemented and tested.
Future development will focus on:
- Enhanced User Experience: Improving error messages, logging, and providing more detailed feedback.
- Advanced Configuration: Exploring more powerful configuration options, such as rule dependencies or conditional execution.
- Plugin System: A potential plugin system to allow for custom extensions and integrations.
- Broader Community Adoption: Creating more examples and tutorials for different languages and frameworks.
π€ Contributing
Contributions are welcome! Please feel free to open issues or submit pull requests.
π License
This project is licensed under the Apache License - see the LICENSE file for details.
Documentation
ΒΆ
There is no documentation for this package.
Directories
ΒΆ
| Path | Synopsis |
|---|---|
|
gen
|
|
|
go/protos/devloop/v1
Package v1 is a reverse proxy.
|
Package v1 is a reverse proxy. |