diff --git a/.claude/skills/docs/watermark b/.claude/skills/docs/watermark
index e9c6aac13..1608de7cd 100644
--- a/.claude/skills/docs/watermark
+++ b/.claude/skills/docs/watermark
@@ -1 +1 @@
-d1b06dede14cbafec1d98c3c6d0817d456d9b081
+15c638832e7049a64067818b3c955df33fb35903
diff --git a/docs/agents/hooks.mdx b/docs/agents/hooks.mdx
index 753de210a..1df694a3b 100644
--- a/docs/agents/hooks.mdx
+++ b/docs/agents/hooks.mdx
@@ -97,6 +97,9 @@ Each hook fires on a specific lifecycle event:
| `sandbox_ready` | After the sandbox environment is created | No |
| `sandbox_cleanup` | Before the sandbox is torn down | No |
| `checkpoint_saved` | After a checkpoint is written to disk | No |
+| `pre_tool_use` | Before an agent tool call executes | Yes |
+| `post_tool_use` | After an agent tool call succeeds | No |
+| `post_tool_use_failure` | After an agent tool call fails | No |
## Configuration
@@ -128,7 +131,7 @@ sandbox = false
Blocking hooks can affect workflow execution. Non-blocking hooks run for side effects only — their decisions are ignored.
-**Blocking by default:** `run_start`, `stage_start`, `edge_selected`. These events represent decision points where a hook can prevent or redirect execution.
+**Blocking by default:** `run_start`, `stage_start`, `edge_selected`, `pre_tool_use`. These events represent decision points where a hook can prevent or redirect execution.
**Non-blocking by default:** All other events. Override with `blocking = true` if needed.
@@ -188,6 +191,7 @@ The `matcher` field is a regex pattern that filters which stages trigger a hook.
- The **node ID** (e.g., `"implement"`, `"test"`)
- The **handler type** (e.g., `"agent"`, `"command"`, `"prompt"`)
- The **edge source** and **edge target** (for `edge_selected` events)
+- The **tool name** (for `pre_tool_use`, `post_tool_use`, `post_tool_use_failure` events)
If any of these fields match the regex, the hook fires. If `matcher` is omitted, the hook fires for all stages of the specified event.
@@ -254,6 +258,29 @@ The full event context is available as a JSON payload. For command hooks, it is
Fields vary by event — `edge_from`/`edge_to`/`edge_label` are only set for `edge_selected`, `failure_reason` for failure events, `attempt`/`max_attempts` for `stage_retrying`, etc. Null fields are omitted from the serialized JSON.
+For tool-level events (`pre_tool_use`, `post_tool_use`, `post_tool_use_failure`), the context includes additional fields:
+
+| Field | Events | Description |
+|---|---|---|
+| `tool_name` | All tool events | Name of the tool being called (e.g., `shell`, `write_file`) |
+| `tool_input` | `pre_tool_use` | JSON object with the tool's input arguments |
+| `tool_call_id` | `post_tool_use`, `post_tool_use_failure` | Unique identifier for the tool call |
+| `tool_output` | `post_tool_use` | The tool's output string |
+| `error_message` | `post_tool_use_failure` | The error message from the failed tool call |
+
+
+```json
+{
+ "event": "pre_tool_use",
+ "run_id": "run-abc123",
+ "workflow_name": "ci-pipeline",
+ "node_id": "implement",
+ "tool_name": "shell",
+ "tool_input": {"command": "rm -rf /tmp/build"}
+}
+```
+
+
## Timeouts
Each hook type has a default timeout:
@@ -324,4 +351,13 @@ model = "sonnet"
max_tool_rounds = 20
blocking = true
timeout_ms = 120000
+
+# Auto-format Rust files after writes
+[[hooks]]
+name = "cargo-fmt"
+event = "post_tool_use"
+command = "cargo fmt"
+matcher = "write_file"
+blocking = false
+sandbox = true
```
diff --git a/docs/execution/run-configuration.mdx b/docs/execution/run-configuration.mdx
index b3924ebb6..7785fab18 100644
--- a/docs/execution/run-configuration.mdx
+++ b/docs/execution/run-configuration.mdx
@@ -76,6 +76,9 @@ exclude_globs = ["**/node_modules/**", "**/.cache/**"]
repo_name = "arc"
repo_url = "https://github.com/qltysh/arc"
+[pull_request]
+enabled = true
+
[[hooks]]
event = "stage_start"
command = "./scripts/pre-check.sh"
@@ -234,6 +237,19 @@ digraph CI {
If a `$variable` in the DOT file has no matching entry in `[vars]`, Arc raises an error immediately. A bare `$` not followed by an identifier (e.g. `costs $5`) is left as-is.
+### `[pull_request]`
+
+Automatically open a GitHub pull request when the workflow run completes successfully. Requires a [GitHub App](/integrations/github) to be configured.
+
+```toml title="run.toml"
+[pull_request]
+enabled = true
+```
+
+| Field | Description |
+|---|---|
+| `enabled` | When `true`, Arc creates a PR from the agent's working branch after a successful run. Default: `false`. |
+
### `[[hooks]]`
Define hooks that run in response to lifecycle events. Each hook is a TOML array entry:
diff --git a/docs/integrations/github.mdx b/docs/integrations/github.mdx
index 6418a087c..9a204b4e3 100644
--- a/docs/integrations/github.mdx
+++ b/docs/integrations/github.mdx
@@ -12,6 +12,7 @@ Arc uses a [GitHub App](https://docs.github.com/en/apps/overview) to authenticat
| **OAuth login** | Users sign in to the web UI with their GitHub account |
| **Private repo cloning** | Daytona and Docker sandboxes clone private repositories using short-lived Installation Access Tokens |
| **Checkpoint pushing** | After each workflow stage, Arc pushes the run branch and metadata branch back to origin from inside the sandbox |
+| **Auto-PR** | When `[pull_request] enabled = true` in the [run config](/execution/run-configuration#pull_request), Arc opens a PR from the agent's working branch after a successful run |
## Setup
diff --git a/docs/reference/cli.mdx b/docs/reference/cli.mdx
index 118bd4d3b..7f6e45886 100644
--- a/docs/reference/cli.mdx
+++ b/docs/reference/cli.mdx
@@ -127,6 +127,8 @@ arc exec "Refactor the auth module" --permissions full --auto-approve
| `--verbose` | Print full LLM request/response JSON to stderr | — |
| `--skills-dir
` | Directory containing skill files (overrides default discovery) | — |
| `--output-format ` | Output format: `text` (human-readable) or `json` (NDJSON event stream) | `text` |
+| `--mode ` | `standalone` (default) or `server` — in server mode, routes through the Arc API's `/completions` endpoint | `standalone` |
+| `--server-url ` | Arc API server URL (overrides `server.base_url` from `cli.toml`) | — |
Permission levels control which tools are auto-approved: `read-only` allows read tools (`read_file`, `grep`, `glob`, `list_dir`) and subagent tools; `read-write` adds write tools (`write_file`, `edit_file`, `apply_patch`); `full` allows all tools including shell commands. Tools outside the permission level are either interactively prompted (if a TTY is present) or denied (with `--auto-approve`). See [default models by provider](/core-concepts/models#default-models).