README
ΒΆ
devloop: Multi-Component Project Watcher
devloop
is a versatile tool designed to streamline your inner development loop, 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)
Like Air but for multiple components. Devloop runs as a local file watcher that monitors multiple file types and components within a single project directory. No network servers or connectivity.
Use Case: Single project development, local development only, no external API needed.
2. Agent Mode
Like Standalone mode but with an embedded gRPC server. Devloop watches multiple components in a single project while also providing API access for external clients to connect directly to this agent. Includes MCP integration via HTTP handler when enabled.
Use Case: Single project that needs API access, AI integration, remote monitoring.
3. Gateway Mode
Multiple separate daemon processes (each watching their own project components) connect to a central gateway hub. Clients connect only to the gateway, which aggregates information from all connected daemons.
# Start the central gateway
devloop --mode gateway --http-port 9999 --grpc-port 55555
# Connect individual project daemons to the gateway
devloop --mode agent --gateway-addr localhost:55555 -c project-a/.devloop.yaml
devloop --mode agent --gateway-addr localhost:55555 -c project-b/.devloop.yaml
Note: Gateway mode is temporarily removed and will be reimplemented using the grpcrouter library for simplified proxy and reverse tunnel functionality.
Use Case: Microservices, monorepos, multi-project development with centralized monitoring.
π 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)
π Getting Started
-
Install
devloop
:go install github.com/panyam/devloop@latest
-
Initialize a new configuration:
# Create basic configuration devloop init # Or create with specific project profiles devloop init golang # Go project devloop init ts python # TypeScript + Python projects devloop init go ts py # Multiple projects with aliases
-
Or manually create a
.devloop.yaml
file 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.
Quick Configuration with devloop init
The init
command provides pre-configured templates for common project types:
Available profiles:
golang
(alias:go
) - Go project with build and run commandstypescript
(aliases:ts
,node
,nodejs
) - TypeScript/Node.js project with build and servepython
(aliases:py
,flask
) - Python Flask project with development server
Usage examples:
devloop init # Basic "Hello World" configuration
devloop init golang # Go project configuration
devloop init ts python # TypeScript and Python configurations
devloop init --output custom.yaml golang # Custom output location
devloop init --force golang # Overwrite existing configuration
π Examples
The examples/
directory contains comprehensive real-world examples demonstrating devloop's capabilities across different development scenarios:
Example | Description | Key Technologies | Use Case |
---|---|---|---|
01-fullstack-web | Complete web application with backend, frontend, database, and docs | Go, JavaScript, SQL, HTTP | Traditional web development with multiple parallel services |
02-microservices | Distributed microservices architecture with gateway pattern | Go, API Gateway, JWT, HTTP | Service-oriented architecture with centralized monitoring |
03-python-datascience | Data science workflow with notebooks, training, and testing | Python, Jupyter, pytest, ML | Data analysis and machine learning development |
04-docker-compose | Multi-container development with orchestrated services | Docker, PostgreSQL, Redis, React, Go, Python | Containerized application development |
05-frontend-framework | Modern frontend development with multiple frameworks | React, Vue.js, TypeScript, Storybook, Vite | Component-driven frontend development |
Quick Start with Examples
Each example includes:
- Complete setup instructions in their respective README files
- Working
.devloop.yaml
configurations - Makefile with common commands (
make run
,make deps
,make clean
) - Sample code and realistic project structure
- Testing and deployment guidance
To try any example:
# Navigate to an example
cd examples/01-fullstack-web
# Install dependencies
make deps
# Start development environment
make run
Example Complexity Levels
- Beginner: Examples 1-3 demonstrate core devloop concepts
- Intermediate: Examples 4-5 show advanced orchestration patterns
Each example builds upon the concepts from previous ones, so we recommend exploring them in order if you're new to devloop.
π 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
devloop
will 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.
- Cross-Platform Support: Commands execute natively on Windows (
cmd /c
), macOS, and Linux (bash -c
withsh -c
fallback). - Color-Coded Output: Automatically assigns consistent colors to each rule's output, making it easy to distinguish logs in busy development environments. Preserves native colors from subprocess tools like npm, go test, etc.
- Channel-Based Event Handling: Clean Go concurrency patterns with file changes routed through channels for reliable debouncing and execution.
- Command Log Prefixing: Prepends a customizable prefix to each line of your command's output, with optional color coding for enhanced readability.
- .air.toml Converter: Includes a built-in tool to convert your existing
.air.toml
configuration into adevloop
rule.
βοΈ 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)
color_logs: boolean # Enable colored output (default: true)
color_scheme: string # Color scheme: "auto", "dark", "light" (default: "auto")
custom_colors: # Optional: Custom color mappings
rule_name: "color" # Map rule names to specific colors
verbose: boolean # Global verbose logging (default: false)
default_debounce_delay: duration # Global debounce delay (default: 500ms)
max_parallel_rules: number # Maximum concurrent rules (default: 0 = unlimited)
rules: # Required: Array of rules
- name: string # Required: Unique rule identifier
prefix: string # Optional: Custom log prefix (defaults to name)
color: string # Optional: Custom color for this rule's output
workdir: string # Optional: Working directory for commands (defaults to config dir)
run_on_init: boolean # Optional: Run on startup (default: true)
verbose: boolean # Optional: Per-rule verbose logging
debounce_delay: duration # Optional: Per-rule debounce delay (e.g., "200ms")
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 |
color_logs |
boolean | true |
Enable colored output to distinguish different rules |
color_scheme |
string | "auto" |
Color palette: "auto" (detect), "dark" , "light" , or "custom" |
custom_colors |
map | {} |
Map rule names to specific colors (e.g., rule_name: "blue" ) |
verbose |
boolean | false |
Enable verbose logging globally |
default_debounce_delay |
duration | "500ms" |
Default delay before executing commands after file changes |
suppress_subprocess_colors |
boolean | false |
Suppress colors from subprocess output (npm, go test, etc.) |
Rule Options
Option | Type | Required | Description |
---|---|---|---|
name |
string | β | Unique identifier for the rule |
prefix |
string | β | Custom prefix for log output (overrides name) |
color |
string | β | Custom color for this rule's output (e.g., "blue" , "red" , "bold-green" ) |
workdir |
string | β | Working directory for command execution (defaults to config file directory) |
verbose |
boolean | β | Enable verbose logging for this rule only |
debounce_delay |
duration | β | Delay before executing after file changes (e.g., "200ms" ) |
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 ) |
exit_on_failed_init |
boolean | β | Exit devloop when this rule fails startup (default: false ) |
max_init_retries |
integer | β | Maximum retry attempts for failed startup (default: 10 ) |
init_retry_backoff_base |
integer | β | Base backoff duration in ms for startup retries (default: 3000 ) |
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
Startup Behavior and Retry Logic
devloop includes robust startup retry logic to handle transient failures during initialization:
rules:
- name: "Database"
commands:
- "docker-compose up -d postgres"
- "wait-for-postgres.sh"
# Critical service - exit devloop if this fails after retries
exit_on_failed_init: true
max_init_retries: 5
init_retry_backoff_base: 5000 # 5s, 10s, 20s, 40s, 80s backoff
- name: "API Server"
commands:
- "go build -o bin/api ./cmd/api"
- "./bin/api"
# Default behavior: retry failures but don't exit devloop
# exit_on_failed_init: false (default)
# max_init_retries: 10 (default)
# init_retry_backoff_base: 3000 (default - 3s, 6s, 12s, 24s...)
Startup Retry Behavior:
- By default, failed rules retry up to 10 times with exponential backoff
- Backoff starts at 3 seconds and doubles each attempt (3s, 6s, 12s, 24s, etc.)
- devloop continues running and watching files even if some rules fail startup
- Set
exit_on_failed_init: true
for critical rules that must succeed
Retry Configuration:
exit_on_failed_init
: Whether to exit devloop when this rule fails startup (default:false
)max_init_retries
: Maximum retry attempts (default:10
)init_retry_backoff_base
: Base backoff duration in milliseconds (default:3000
)
Example Log Output:
[api] Rule "api" execution failed (attempt 1/11): exit status 1
[api] Rule "api" failed, retrying in 3s (attempt 2/11) at 15:04:08
[api] Rule "api" execution failed (attempt 2/11): exit status 1
[api] Rule "api" failed, retrying in 6s (attempt 3/11) at 15:04:14
[api] Rule "api" succeeded on attempt 3/11
Example with All Options
settings:
prefix_logs: true
prefix_max_length: 12
color_logs: true
suppress_subprocess_colors: false # Preserve colors from npm, go test, etc.
rules:
- name: "Backend API"
prefix: "api"
workdir: "./backend"
exit_on_failed_init: false
max_init_retries: 5
init_retry_backoff_base: 2000
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
- Cross-Platform Shell Execution: Commands run via platform-specific shells (
bash -c
on Unix,cmd /c
on Windows) - Process Groups: Commands run in separate process groups for clean termination
- Environment Inheritance: Commands inherit parent environment plus
env
variables - Working Directory: Commands execute in
workdir
(or directory containing the config file 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
env
configuration (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:9999
(default gateway port)
List All Projects
GET /projects
Returns all registered devloop projects and their connection status.
Response:
{
"projects": [
{
"projectId": "auth-service",
"projectRoot": "/path/to/auth-service",
"status": "CONNECTED"
}
]
}
Get Project Configuration
GET /projects/{projectId}/config
Returns the full configuration for a specific project.
Response:
{
"configJson": "{\"rules\":[{\"name\":\"backend\",\"commands\":[\"go run .\"]}]}"
}
Get Rule Status
GET /projects/{projectId}/status/{ruleName}
Returns the current status of a specific rule.
Response:
{
"ruleStatus": {
"projectId": "backend",
"ruleName": "backend",
"isRunning": true,
"startTime": "1704092400000",
"lastBuildTime": "1704092400000",
"lastBuildStatus": "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:
{
"paths": [
"**/*.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: {"projectId":"backend","ruleName":"api","line":"Starting server...","timestamp":"1704092400000"}
data: {"projectId":"backend","ruleName":"api","line":"Server listening on :9999","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": [
{
"projectId": "backend",
"ruleName": "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:9999/api/projects');
const data = await response.json();
// Stream logs using EventSource
const events = new EventSource('http://localhost:9999/api/projects/backend/stream/logs/api');
events.onmessage = (event) => {
const log = JSON.parse(event.data);
console.log(`[${log.ruleName}] ${log.line}`);
};
Python
import requests
import sseclient
# Get rule status
response = requests.get('http://localhost:9999/api/projects/backend/status/api')
status = response.json()
# Stream logs
response = requests.get('http://localhost:9999/api/projects/backend/stream/logs/api', stream=True)
client = sseclient.SSEClient(response)
for event in client.events():
log = json.loads(event.data)
print(f"[{log['ruleName']}] {log['line']}")
Go
// Using the generated gRPC client
conn, _ := grpc.NewClient("localhost:55555", grpc.WithTransportCredentials(insecure.NewCredentials()))
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.Status)
}
π€ MCP Server Integration
Devloop can act as a Model Context Protocol (MCP) server, enabling AI agents and LLMs (like Claude) to monitor and control your development workflows. The MCP server runs as an HTTP handler alongside the gRPC API when enabled.
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 Devloop with MCP Enabled
# Start standalone mode with MCP server enabled
devloop --grpc-port 5555 --http-port 9999 --enable-mcp -c .devloop.yaml
The MCP server runs as an HTTP handler on the /mcp
endpoint, using the same Agent Service that provides the gRPC API.
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. Access MCP Endpoint
The MCP server is available at the /mcp
HTTP endpoint:
# MCP endpoint (when --enable-mcp is used)
http://localhost:9999/mcp/
Using with Claude Desktop
Add to your Claude Desktop configuration (~/Library/Application Support/Claude/claude_desktop_config.json
on macOS):
{
"mcpServers": {
"devloop": {
"type": "http",
"url": "http://localhost:9999/mcp/"
}
}
}
Available MCP Tools
When connected, the following tools are available to AI agents (auto-generated from the Agent Service protobuf definitions):
Project Management
// Get project configuration
await use_mcp_tool("devloop", "GetConfig", {});
// Get specific rule information
await use_mcp_tool("devloop", "GetRule", {
ruleName: "backend-build"
});
// List watched file paths
await use_mcp_tool("devloop", "ListWatchedPaths", {});
Rule Control
// Trigger a rule manually
await use_mcp_tool("devloop", "TriggerRule", {
ruleName: "backend-build"
});
// Stream logs (placeholder for future implementation)
// await use_mcp_tool("devloop", "StreamLogs", {
// ruleName: "backend-build"
// });
Legacy Example (for reference)
// 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
});
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
}
}
}
π‘ Available Endpoints by Mode
Different devloop modes provide different endpoints and capabilities:
Standalone Mode (--mode standalone
)
devloop --mode standalone --http-port 9999 --grpc-port 55555 --enable-mcp
- β
HTTP API:
http://localhost:9999/api/projects
- β
gRPC API:
localhost:55555
- β
MCP via HTTP:
http://localhost:9999/mcp/
(if--enable-mcp
) - β File watching and rule execution
Gateway Mode (--mode gateway
)
devloop --mode gateway --http-port 9999 --grpc-port 55555 --enable-mcp
- β
HTTP API:
http://localhost:9999/api/projects
- β
gRPC API:
localhost:55555
- β Agent management and coordination
- β
MCP via HTTP:
http://localhost:9999/mcp/
(if--enable-mcp
) - β No direct file watching (agents do the watching)
- β οΈ Note: Gateway mode temporarily removed, being reimplemented with grpcrouter
Agent Mode (--mode agent --gateway-addr localhost:55555
)
devloop --mode agent --gateway-addr localhost:55555
- β No HTTP API endpoints (connects to gateway)
- β No direct gRPC API (uses gateway's API)
- β No MCP server (MCP only available in standalone/gateway modes)
- β File watching and rule execution
- β Reports to gateway
Quick Test Commands:
# Test standalone/gateway HTTP API
curl http://localhost:9999/api/projects
# Test MCP HTTP endpoints (when --enable-mcp)
curl http://localhost:9999/mcp/
# Test if any devloop process is running
ps aux | grep devloop
Troubleshooting MCP Integration
Connection Issues
Note: MCP uses StreamableHTTP transport for universal compatibility.
# Check if devloop is running with MCP enabled
ps aux | grep devloop
# Test MCP HTTP endpoint (when --enable-mcp is specified)
curl http://localhost:9999/mcp/
# Test regular HTTP API endpoints
curl http://localhost:9999/api/
# MCP communication:
# - stdio: For process-launched clients (Claude Desktop)
# - HTTP: For network clients (web tools, external integrations)
Common Problems
-
"MCP server not found"
- Ensure devloop is running with
--enable-mcp
flag and--http-port
specified - Check firewall settings
- Ensure devloop is running with
-
"Tool execution failed"
- Verify the rule name exists in your .devloop.yaml
- Check the gRPC server is running (requires
--grpc-port
)
-
"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
- Use Descriptive Rule Names: Makes it easier for AI to identify and trigger rules
- 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
-c
flag to specify the path to your.devloop.yaml
configuration file. If omitted,devloop
will look for.devloop.yaml
in the current directory.
Running in Agent Mode
To connect to a gateway:
devloop --mode agent --gateway-addr localhost:55555 -c .devloop.yaml
Running in Gateway Mode
To start a central gateway:
devloop --mode gateway --gateway-port 9999
The gateway will accept connections from agents and provide a unified interface at http://localhost:9999
.
Subcommands
devloop
also supports subcommands for specific utilities:
init
Initialize a new .devloop.yaml
configuration file with predefined project profiles.
devloop init [profiles...]
Available profiles:
golang
(alias:go
) - Go project with build and run commandstypescript
(aliases:ts
,node
,nodejs
) - TypeScript/Node.js projectpython
(aliases:py
,flask
) - Python Flask development server
Examples:
devloop init # Basic "Hello World" configuration
devloop init golang # Go project configuration
devloop init ts python # Multiple profiles
devloop init -o config.yaml go # Custom output file
devloop init --force golang # Overwrite existing file
Flags:
-o, --output
- Output file path (default:.devloop.yaml
)-f, --force
- Overwrite existing configuration file
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
-i
flag to specify the path to the.air.toml
input file. If omitted, it defaults to.air.toml
in 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 9999" - 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 convert
to generate initial config - Review and adjust file patterns
- Update build commands if needed
- Test with
devloop -c .devloop.yaml
- Remove
.air.toml
and 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.
Development & Testing
Running Tests
# Run all tests
make test
# Run tests with coverage
make coverage-agent
# Generate HTML coverage report
make coverage-agent-html
# Open coverage report in browser
make coverage-agent-open
Test Coverage
The project maintains 57% test coverage with testing for:
- WorkerPool Process Management: Process lifecycle, termination, job killing, debounce logic
- Scheduler Integration: Event-driven routing, mixed workload scenarios
- WorkerPool: Unified job execution, global parallelism, process management
- File Watching: Per-rule watchers, pattern matching, dynamic directories
- Configuration: YAML parsing, validation, rule loading
Coverage Reports
All test artifacts are organized in the reports/
directory:
reports/agent_coverage.out
- Coverage datareports/agent_coverage.html
- Interactive HTML report- Use
make coverage-help
to see all available coverage targets
Process Management
Devloop intelligently handles all types of processes with unified execution:
settings:
max_parallel_rules: 5 # Global worker pool size for all jobs
rules:
- name: "build" # Short-running - completes quickly
commands:
- "go build -o bin/server"
- name: "dev-server" # Long-running - will be killed/restarted on changes
commands:
- "./bin/server --dev"
Process Management Benefits:
- Unified Execution: All jobs use the same worker pool and process management
- Smart Killing: File changes respect debounce window; manual triggers restart immediately
- Global Concurrency: Configure worker pool size once for all job types
- Graceful Termination: SIGTERM to SIGKILL progression with process group handling
π§ 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.yaml
exists in your current directory - Use
-c
flag 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 :9999: bind: address already in use
Solutions:
- Kill existing processes:
lsof -ti:9999 | xargs kill -9
- Use different ports for different rules
- Implement port checking in your startup scripts:
commands: - "kill $(lsof -ti:9999) || 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:9999/projects
- Check network connectivity:
ping gateway-host
- Ensure correct gateway address format:
--gateway-addr host:port
(note: it'sgateway-addr
, notgateway-url
) - Check firewall rules allow connection
- Verify gateway is in "gateway" mode, not "agent" mode
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.yaml
configuration - 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, devloop is fully cross-platform:
- Command execution: Uses
cmd /c
on Windows,bash -c
(orsh -c
) on Unix - Process management: Adapted for Windows process group differences
- File watching: Works reliably via fsnotify on all platforms
- Signal handling: Platform-appropriate termination signals
Minor differences:
- Process group management implementation varies by OS
- Some signal handling features may behave slightly differently
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.
6. Control Rule Parallelism
Use max_parallel_rules
to control concurrent rule execution for debugging or resource management:
settings:
max_parallel_rules: 1 # Sequential execution for debugging
# Or unlimited (default)
settings:
max_parallel_rules: 0 # All rules can run concurrently
Use Cases:
- Sequential debugging (
1
): Isolate rule chains and prevent overlapping output - Resource limited (
N
): Prevent overwhelming system resources - Default (
0
): Maximum performance with unlimited concurrency
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 9999
# Each service
cd service-a && devloop --mode agent --gateway-url localhost:9999
cd service-b && devloop --mode agent --gateway-url localhost:9999
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
ΒΆ
Overview ΒΆ
Package main provides the devloop command-line interface for development workflow automation.
Devloop is a versatile tool designed to streamline your inner development loop, particularly within Multi-Component Projects. 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.
Installation ΒΆ
go install github.com/panyam/devloop@latest
Quick Start ΒΆ
Create a .devloop.yaml file:
rules: - name: "Go Backend Build and Run" watch: - action: "include" patterns: - "**/*.go" - "go.mod" - "go.sum" commands: - "go build -o ./bin/server ./cmd/server" - "./bin/server"
Run devloop:
devloop -c .devloop.yaml
Architecture Modes ΒΆ
Devloop operates in three distinct modes:
1. Standalone Mode (Default): Local file watcher for single project development. 2. Agent Mode: Connects to a central gateway for multi-project management. 3. Gateway Mode: Central hub for managing multiple devloop agents.
Examples ΒΆ
See the examples/ directory for comprehensive real-world examples including: - Full-stack web applications - Microservices architectures - Data science workflows - Docker compose setups - Frontend framework development
Directories
ΒΆ
Path | Synopsis |
---|---|
Package agent provides the core orchestration engine for devloop.
|
Package agent provides the core orchestration engine for devloop. |
gen
|
|
go/devloop/v1
Package v1 is a reverse proxy.
|
Package v1 is a reverse proxy. |
Package testhelpers provides common testing utilities for devloop.
|
Package testhelpers provides common testing utilities for devloop. |
Package utils provides common utilities for devloop.
|
Package utils provides common utilities for devloop. |