
Security News
AGENTS.md Gains Traction as an Open Format for AI Coding Agents
AGENTS.md is a fast-growing open format giving AI coding agents a shared, predictable way to understand project setup, style, and workflows.
github.com/panyam/devloop
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.
Devloop operates in three distinct modes to support different development scenarios:
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.
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.
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.
Before installing devloop, ensure you have:
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.
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 serverUsage 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
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 |
Each example includes:
.devloop.yaml
configurationsmake run
, make deps
, make clean
)To try any example:
# Navigate to an example
cd examples/01-fullstack-web
# Install dependencies
make deps
# Start development environment
make run
Each example builds upon the concepts from previous ones, so we recommend exploring them in order if you're new to devloop.
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 | 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 |
devloop
will run them concurrently.cmd /c
), macOS, and Linux (bash -c
with sh -c
fallback)..air.toml
configuration into a devloop
rule.# .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
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.) |
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 ) |
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 |
*
- 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 bdevloop 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:
exit_on_failed_init: true
for critical rules that must succeedRetry 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
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"
# 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
"**/.*"
bash -c
on Unix, cmd /c
on Windows)env
variablesworkdir
(or directory containing the config file if not set)env
configuration (overrides system vars)DEVLOOP_RULE_NAME
- Current rule nameDEVLOOP_TRIGGER_FILE
- File that triggered the rulego 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
Devloop provides both gRPC and REST APIs for monitoring and control. The REST API is available via gRPC-Gateway.
Base URL: http://localhost:9999
(default gateway port)
GET /projects
Returns all registered devloop projects and their connection status.
Response:
{
"projects": [
{
"projectId": "auth-service",
"projectRoot": "/path/to/auth-service",
"status": "CONNECTED"
}
]
}
GET /projects/{projectId}/config
Returns the full configuration for a specific project.
Response:
{
"configJson": "{\"rules\":[{\"name\":\"backend\",\"commands\":[\"go run .\"]}]}"
}
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"
}
}
POST /projects/{projectId}/trigger/{ruleName}
Manually triggers a rule execution.
Response:
{
"success": true,
"message": "Rule 'backend' triggered successfully"
}
GET /projects/{projectId}/watched-paths
Returns all glob patterns being watched by the project.
Response:
{
"paths": [
"**/*.go",
"go.mod",
"go.sum"
]
}
GET /projects/{projectId}/file-content?path={filePath}
Reads a file from the project directory.
Response:
{
"content": "package main\n\nfunc main() {\n // ...\n}"
}
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 /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 millisecondsResponse:
{
"logs": [
{
"projectId": "backend",
"ruleName": "api",
"line": "Request processed",
"timestamp": "1704092400000"
}
]
}
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);
}
// 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}`);
};
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']}")
// 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)
}
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.
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:
# 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.
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
}
}
}
The MCP server is available at the /mcp
HTTP endpoint:
# MCP endpoint (when --enable-mcp is used)
http://localhost:9999/mcp/
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/"
}
}
}
When connected, the following tools are available to AI agents (auto-generated from the Agent Service protobuf definitions):
// 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", {});
// 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"
// });
// 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
});
// 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
});
# .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:
// 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}`);
}
}
}
# 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));
}
}
# mcp-config.json
{
"authentication": {
"required": true,
"type": "api_key",
"key_header": "X-Devloop-API-Key"
}
}
{
"access_control": {
"allowed_projects": ["frontend", "backend"],
"forbidden_commands": ["rm", "sudo"],
"max_command_length": 500,
"rate_limit": {
"requests_per_minute": 60,
"concurrent_operations": 5
}
}
}
Different devloop modes provide different endpoints and capabilities:
--mode standalone
)devloop --mode standalone --http-port 9999 --grpc-port 55555 --enable-mcp
http://localhost:9999/api/projects
localhost:55555
http://localhost:9999/mcp/
(if --enable-mcp
)--mode gateway
)devloop --mode gateway --http-port 9999 --grpc-port 55555 --enable-mcp
http://localhost:9999/api/projects
localhost:55555
http://localhost:9999/mcp/
(if --enable-mcp
)--mode agent --gateway-addr localhost:55555
)devloop --mode agent --gateway-addr localhost:55555
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
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)
"MCP server not found"
--enable-mcp
flag and --http-port
specified"Tool execution failed"
--grpc-port
)"Timeout waiting for response"
Navigate to your project's root directory and execute:
devloop -c .devloop.yaml
-c
flag to specify the path to your .devloop.yaml
configuration file. If omitted, devloop
will look for .devloop.yaml
in the current directory.To connect to a gateway:
devloop --mode agent --gateway-addr localhost:55555 -c .devloop.yaml
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
.
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 serverExamples:
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 fileconvert
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
-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.If you're currently using Air for Go development, migrating to devloop is straightforward.
Convert your .air.toml automatically:
devloop convert -i .air.toml > .devloop.yaml
Review and adjust the generated configuration:
cat .devloop.yaml
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 |
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"
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 ./..."
devloop convert
to generate initial configdevloop -c .devloop.yaml
.air.toml
and Air dependencydevloop
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.
# 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
The project maintains 57% test coverage with testing for:
All test artifacts are organized in the reports/
directory:
reports/agent_coverage.out
- Coverage datareports/agent_coverage.html
- Interactive HTML reportmake coverage-help
to see all available coverage targetsDevloop 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:
Error: Failed to read config file: open .devloop.yaml: no such file or directory
Solutions:
.devloop.yaml
exists in your current directory-c
flag to specify the config path: devloop -c path/to/.devloop.yaml
ls -la .devloop.yaml
Symptoms: File changes detected but commands don't run
Solutions:
# Test pattern matching
find . -name "*.go" | grep -E "pattern"
bash -c
commands:
- "echo 'Rule triggered for: $DEVLOOP_TRIGGER_FILE'"
- "your-actual-command"
Symptoms: Old processes keep running after file changes
Solutions:
// Go example
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan
server.Shutdown(context.Background())
Symptoms: devloop consuming excessive CPU
Solutions:
node_modules
, .git
, etc.:
watch:
- action: "exclude"
patterns:
- "node_modules/**"
- ".git/**"
- "*.log"
- action: "include"
patterns:
- "src/**/*.js"
Error: listen tcp :9999: bind: address already in use
Solutions:
lsof -ti:9999 | xargs kill -9
commands:
- "kill $(lsof -ti:9999) || true"
- "npm start"
Error: permission denied
Solutions:
chmod +x your-script.sh
Symptoms: Missing or garbled log output
Solutions:
settings:
prefix_logs: true
prefix_max_length: 10
commands:
- "python -u script.py" # Unbuffered Python
- "node --no-buffering app.js" # Unbuffered Node.js
Error: Failed to connect to gateway
Solutions:
curl http://gateway-host:9999/projects
ping gateway-host
--gateway-addr host:port
(note: it's gateway-addr
, not gateway-url
)Symptoms: Modifying files doesn't trigger rules
Solutions:
**
for recursive matching:
# Wrong
patterns: ["*.go"] # Only matches root directory
# Correct
patterns: ["**/*.go"] # Matches all subdirectories
Symptoms: Memory usage grows over time
Solutions:
ps aux | grep devloop
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
If you continue experiencing issues:
.devloop.yaml
configurationgo version
uname -a
devloop --version
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)Docker is excellent for production-like environments, but adds overhead for local development:
Devloop is designed for the tight feedback loop of development, where speed matters more than isolation.
Terminal multiplexers manage windows, not processes. Devloop provides:
Foreman and Overmind are great for Procfile-based applications. Devloop adds:
Make is a build tool, not a process manager. It's not designed for:
Devloop is designed for development environments. For production, use proper orchestration tools like:
Devloop uses process groups to ensure clean termination:
Yes, devloop is fully cross-platform:
cmd /c
on Windows, bash -c
(or sh -c
) on UnixMinor differences:
Absolutely! Common patterns include:
devloop is designed for efficiency with minimal overhead:
# 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"
Exclusions are processed before inclusions, making them more efficient:
watch:
- action: "exclude"
patterns: ["**/test/**", "**/*.test.js"]
- action: "include"
patterns: ["**/*.js"]
# Combine commands when possible
commands:
- "go build -o bin/app && ./bin/app"
# Use incremental builds
commands:
- "go build -i -o bin/app ./cmd/app"
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()
}()
devloop has built-in debouncing to prevent command storms during rapid file changes.
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:
1
): Isolate rule chains and prevent overlapping outputN
): Prevent overwhelming system resources0
): Maximum performance with unlimited concurrencyrules:
- 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 ."
# 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
# 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"
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"
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 |
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
devloop
is stable and ready for use. All core features are implemented and tested.
Future development will focus on:
Contributions are welcome! Please feel free to open issues or submit pull requests.
This project is licensed under the Apache License - see the LICENSE file for details.
FAQs
Unknown package
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
AGENTS.md is a fast-growing open format giving AI coding agents a shared, predictable way to understand project setup, style, and workflows.
Security News
/Research
Malicious npm package impersonates Nodemailer and drains wallets by hijacking crypto transactions across multiple blockchains.
Security News
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.