Skip to main content

.agnt.kdl Configuration

.agnt.kdl is a single file in your project root that tells agnt which scripts to run, how to link proxies to them, and what behavior to enable. It uses KDL — a node-based document format that's easier to read and write than JSON or YAML for configuration.

agnt looks for .agnt.kdl in your project directory and walks up to the filesystem root if needed. If no file exists, agnt falls back to its defaults (no scripts, no proxies).

File Structure​

project {
// optional metadata
}

scripts {
// one or more named script blocks
}

proxies {
// one or more named proxy blocks
}

hooks {
on-response {
// notification behavior when AI responds
}
}

toast {
// notification display settings
}

alerts {
// process output monitoring
}

ai {
// AI agent configuration
}

project​

Optional metadata block. None of these fields change behavior — they're informational.

project {
name "my-api"
type "go"
port-conflict "prompt"
}
PropertyTypeDefaultDescription
namestring—Display name for the project
typestring—Project type hint (go, node, python, dotnet-vite, etc.)
port-conflictstringpromptPolicy when a declared port is already in use. One of prompt, auto-kill, skip, fail

port-conflict policies​

When agnt starts autostart scripts, it scans all declared ports for unmanaged processes already listening on them. The policy determines what happens:

PolicyBehavior
promptReturns conflicts to the client and waits for instructions
auto-killKills the blocking process tree automatically
skipLogs a warning and starts the script anyway
failAborts autostart entirely

scripts​

Each named block under scripts defines a runnable process.

scripts {
dev {
run "npm run dev"
autostart true
url-matchers "Local:\\s+{url}"
ports 5173
cwd "packages/frontend"
}
}

Command properties​

PropertyTypeDescription
runstringShell command to execute. Run via sh -c on Unix, cmd.exe /c on Windows
commandstringExecutable name (used with args instead of run)
argsstring[]Arguments for command
shellstringOverride the default shell (bash, powershell, cmd.exe, etc.)
shell-argsstring[]Override the default shell arguments
cwdstringWorking directory for the process. Relative to the .agnt.kdl file location
envmapEnvironment variables set for this process

Use run for shell commands. Use command + args when you need to invoke an executable directly without a shell wrapper.

// Shell command (most common)
dev { run "npm run dev" }

// Direct executable — no shell involved
server { command "node"; args "server.js" "--port" "4000" }

// Custom shell (Git Bash on Windows)
dev { run "npm run dev"; shell "C:\\Program Files\\Git\\bin\\bash.exe" }

Startup properties​

PropertyTypeDefaultDescription
autostartboolfalseStart this script automatically when a session opens
depends-onstring or block—Scripts that must be ready before this one starts
auto-restartboolfalseRestart the process automatically when it exits

Readiness properties​

These control how agnt knows a script is ready to serve requests. Readiness matters for depends-on ordering — a dependent script won't start until its dependency signals readiness.

There are two mutually exclusive mechanisms:

url-matchers — Pattern-match the process output for a URL. When a line matches, the extracted URL is used for proxy linking and the script is marked ready. This is the preferred approach for any server that prints a startup message.

probe-port — Poll a TCP port every 500ms until it accepts a connection. Use this for servers that bind a port but don't print a recognizable URL to stdout. Requires ports to be set. Mutually exclusive with url-matchers.

If neither is set, the script is marked ready when it starts or exits — no readiness gating occurs.

PropertyTypeDescription
url-matchersstring[]Regex patterns to match against process output. Use {url} as a placeholder for localhost URLs
probe-portboolEnable TCP port polling for readiness. Requires ports
// Preferred: match the server's startup message
backend {
run "dotnet watch run --project src/App.csproj"
url-matchers "Now listening on {url}"
ports 5000
}

// Fallback: server binds port silently, no startup message
worker {
run "./background-worker"
ports 8080
probe-port true
}

Port properties​

PropertyTypeDescription
portsint[]Ports used by this process. Used for pre-flight conflict detection and EADDRINUSE recovery

ports serves two purposes regardless of the readiness mechanism:

  • Pre-flight: Before starting, agnt checks if something else is already listening on these ports
  • EADDRINUSE recovery: If the process fails to start because a port is in use, agnt can retry after clearing it

Health monitoring properties​

PropertyTypeDescription
error-patternstringRegex that flags the process as unhealthy when matched in output
healthy-patternstringRegex that clears the unhealthy flag when matched in output

If neither is set, agnt uses built-in patterns that cover common frameworks (Go, Node, Python, .NET).

depends-on​

Controls startup order between scripts. A script with depends-on waits for all listed dependencies to reach a ready state before starting.

scripts {
backend {
run "go run ./cmd/server"
url-matchers "Listening on {url}"
autostart true
}
frontend {
run "npm run dev"
autostart true
depends-on "backend"
url-matchers "Local:\\s+{url}"
}
}

The dependency is satisfied when the backend script either prints a URL matching its url-matchers or reaches a port probe success. If the backend crashes or exits before becoming ready, the frontend starts anyway with a warning.

Per-dependency timeout — default is 120 seconds, configurable per dependency:

// All dependencies get the same timeout
frontend {
depends-on "backend" "redis" timeout=60
}

// Per-dependency timeout
frontend {
depends-on {
api timeout=30
worker timeout=120
}
}

proxies​

Each named block under proxies defines a reverse proxy into a dev server.

proxies {
app {
script "dev"
fallback-port 5173
}
}

Proxy properties​

PropertyTypeDefaultDescription
autostartboolfalseStart automatically when the session opens
scriptstring—Link to a script — proxy target is auto-detected from script's URL output
url-patternstring—Regex filter for which detected URLs to use. Filters on the full URL string
fallback-portint—Port to use if URL detection from the script fails
urlstring—Direct target URL (e.g., http://localhost:3000)
portint—Direct target port (shorthand for http://localhost:PORT)
targetstring—Deprecated. Use url instead
hoststringlocalhostTarget hostname
bindstring127.0.0.1Listen address. "tailscale" serves on this node's tailnet address only, over HTTPS with the node's certificate when the tailnet issues one. Any other non-loopback address needs allow-external true
listen-portint—Fixed port for the proxy itself (stable URLs for OAuth redirect URIs and bookmarks)
cloudflare-tunnelblock—Named Cloudflare tunnel at a hostname you own, behind Cloudflare Access. See Dev Sign-in & Protected Sharing
websocketboolfalseEnable WebSocket proxying
max-log-sizeint1000Maximum traffic log entries to keep

Script-linked proxies​

When a proxy has script set, agnt creates it automatically when the linked script prints a URL matching its url-matchers. This is the most common setup — no hardcoded ports.

scripts {
dev {
run "npm run dev"
url-matchers "Local:\\s+{url}"
autostart true
}
}

proxies {
app {
script "dev"
fallback-port 5173
}
}

fallback-port is a safety net. If the dev server starts but its URL output doesn't match the pattern (or the process doesn't print a URL at all), agnt creates the proxy targeting localhost:fallback-port after a delay.

Explicit target proxies​

When a proxy has a direct url or port set and no script, it starts on its own:

proxies {
external-api {
url "http://localhost:8080"
autostart true
}
}

URL pattern filtering​

Some servers print multiple URLs (e.g., Wails prints both a Vite frontend URL and a backend URL). url-pattern filters which one the proxy targets:

proxies {
// Only proxy the Wails backend, not the Vite frontend
wails-app {
script "wails-dev"
url-pattern ":34115"
}
}

dev-oidc​

A dev-only OIDC issuer served at /__agnt/oidc/ on every proxy of the project, with test users (personas) you switch between from the indicator, :as <persona> in the overlay, or the devauth MCP tool.

dev-oidc {
clients {
my-app {
redirect-uri "http://localhost:*/api/auth/callback/dev-oidc"
secret "dev-only"
login-path "/auth/signin"
}
}
personas {
standard {
email "std@example.com"
}
admin {
email "admin@example.com"
roles "admin"
}
}
default-persona "standard"
}
PropertyDescription
issuerToken iss and the app's authority. Default: the proxy's own origin + /__agnt/oidc
clients.<id>redirect-uri (exact, or a whole-port * on localhost), secret (omit for a PKCE public client), audience, login-path, session-cookies
personas.<name>email (required), name, roles, claims { key "value" }
default-personaSigns local logins in without the picker
allowPersonas granted to a verified remote identity (Tailscale login or Cloudflare Access email). No entry, no persona

Write nested blocks one node per line: the parser rejects } } on one line. See Dev Sign-in & Protected Sharing for who gets which persona over loopback, the tailnet, and a Cloudflare tunnel.

hooks​

Controls what happens when the AI agent responds.

hooks {
on-response {
toast true
indicator true
sound false
}
}
PropertyTypeDefaultDescription
toastbooltrueShow a toast notification in the browser
indicatorbooltrueUpdate the floating indicator
soundboolfalsePlay a notification sound

toast​

Controls toast notification display.

toast {
duration 4000
position "bottom-right"
max-visible 3
}
PropertyTypeDefaultDescription
durationint4000Display duration in milliseconds
positionstringbottom-rightOne of top-left, top-right, bottom-left, bottom-right
max-visibleint3Maximum simultaneous toasts

alerts​

Controls process output monitoring. agnt scans process stdout/stderr for error patterns and surfaces them through get_incidents.

alerts {
enabled true
batch-window 3
dedupe-window 60

patterns {
"db-connection" {
pattern "ECONNREFUSED"
severity "error"
}
}

disable "connection-refused"
}
PropertyTypeDefaultDescription
enabledbooltrueEnable or disable alert monitoring entirely
batch-windowint3Seconds to collect alerts before delivering (reduces noise)
dedupe-windowint60Seconds to suppress duplicate alerts
patternsmap—Custom alert patterns keyed by ID
disablestring[]—Built-in pattern IDs to disable

Each custom pattern has:

PropertyTypeDescription
patternstringRegex matched against each output line
severitystringerror, warning, or info

ai​

Controls how agnt configures AI agent sessions started with agnt run or agnt ai.

ai {
skill "code-review"
env {
ANTHROPIC_API_KEY "sk-..."
}
append-system-prompt "This project uses GraphQL. Always check the schema before writing queries."
}
PropertyTypeDescription
skillstringSkill/persona name to apply to the session
envmapEnvironment variables set for AI commands
system-promptstringFull system prompt replacement
append-system-promptstringText appended to the default agnt system prompt

Use append-system-prompt over system-prompt — the default prompt includes tool documentation for agnt's MCP tools, which system-prompt would discard entirely.

Complete Examples​

Single Vite app​

scripts {
dev {
run "npm run dev"
autostart true
url-matchers "(Local|Network):\\s*{url}"
}
}

proxies {
app {
script "dev"
}
}

hooks {
on-response {
toast true
indicator true
}
}

Full-stack with separate backend and frontend​

scripts {
backend {
run "go run ./cmd/server"
autostart true
url-matchers "Listening on {url}"
ports 4000
}
frontend {
run "npm run dev"
cwd "web"
autostart true
depends-on "backend"
url-matchers "Local:\\s+{url}"
ports 5173
}
}

proxies {
app {
script "frontend"
fallback-port 5173
}
}

hooks {
on-response {
toast true
indicator true
}
}

.NET backend with silent worker process​

scripts {
api {
run "dotnet watch run --project src/Api.csproj"
autostart true
url-matchers "Now listening on {url}"
ports 5000
}
worker {
run "./worker --port 8080"
autostart true
ports 8080
probe-port true
}
}

proxies {
app {
script "api"
fallback-port 5000
}
}