Diff
1diff --git a/README.md b/README.md
2index e49b04650a6799ba1512e1863fcc5907f75f787b..e2336261112ff30ff5d2b676f149d904e2185792 100644
3--- a/README.md
4+++ b/README.md
5@@ -8,37 +8,28 @@ An [OpenCode](https://opencode.ai) plugin that automatically evaluates tool perm
6
7 If the pipeline decides "allow" or "deny", it auto-replies to OpenCode. If "ask", it leaves the prompt for you to decide manually.
8
9-Also includes desktop notifications (Linux `notify-send`) for events that need your attention.
10+Also includes desktop notifications for events that need your attention.
11
12 ## Install
13
14-1. Create a loader file at `~/.config/opencode/plugins/policy-engine.ts`:
15-
16-```ts
17-import mod from "/path/to/policy-engine/src/index.ts"
18-export const PolicyEngine = mod.server
19-```
20-
21-2. Install dependencies:
22-
23-```sh
24-cd /path/to/policy-engine
25-bun install
26-```
27-
28-3. Set your OpenCode bash permissions to delegate to the plugin:
29+1. Add the plugin to your `opencode.json` config:
30
31 ```json
32 {
33- "permission": {
34- "bash": {
35- "*": "ask"
36- }
37- }
38+ "plugin": ["github:duneanalytics/opencode-policy-engine"],
39+ "permission": {
40+ "bash": {
41+ "*": "ask"
42+ }
43+ }
44 }
45 ```
46
47-4. Restart OpenCode.
48+Config file location: `~/.config/opencode/opencode.json`
49+
50+Or place `opencode.json` in your project root for per-project config. See [config docs](https://opencode.ai/docs/config/) for details.
51+
52+2. Restart OpenCode.
53
54 ## API key
55
56@@ -53,6 +44,7 @@ The LLM model defaults to `claude-haiku-4-5-20251001`. Override with the `ANTHRO
57 When OpenCode asks for permission (e.g., to run a bash command), the plugin intercepts the `permission.asked` event and runs the three-stage pipeline.
58
59 **Stage 1 — Deterministic rules** check the command against:
60+
61 - `HARD_ALLOW_PATTERNS`: strict anchored regexes for common safe commands (git status, ls, cat with relative paths, version checks, etc.)
62 - `CONFIG_ALLOW_PATTERNS`: broader prefix patterns for dev tooling (go test, make, bun, mvn, etc.) — still guarded by `SHELL_CONTROL_RE` which blocks shell operators (`;`, `&&`, `|`, `>`, etc.)
63 - `ASK_PATTERNS`: obviously dangerous patterns (sudo, curl|bash, rm -rf /)
64@@ -72,6 +64,7 @@ The plugin monitors user messages for permission-granting language ("you can pus
65 ### Notifications
66
67 Desktop notifications (Linux `notify-send`) fire for:
68+
69 - **Permission needed** — only when the policy engine decides "ask" (auto-handled permissions are silent)
70 - **Session complete** — debounced, main agent only (subagents are filtered out)
71 - **Session error/cancelled**
72@@ -93,6 +86,7 @@ bun tsc --noEmit # type check
73 ## Customizing rules
74
75 Edit `src/rules.ts`:
76+
77 - `HARD_ALLOW_PATTERNS` — strict regexes for instant allow
78 - `CONFIG_ALLOW_PATTERNS` — broader patterns for your dev tooling
79 - `ASK_PATTERNS` — dangerous patterns that always ask
80diff --git a/src/index.ts b/src/index.ts
81index ee62791d871b6343402807f25beaf3c4e7cfc775..2d7a8697ea0c5da6643d03e05158f5d672da8d9e 100644
82--- a/src/index.ts
83+++ b/src/index.ts
84@@ -179,7 +179,7 @@ const IDLE_DELAY_MS = 350
85 const idleTimers = new Map<string, ReturnType<typeof setTimeout>>()
86 const idleSeq = new Map<string, number>()
87
88-const server: Plugin = async (ctx, _options) => {
89+export const PolicyEngine: Plugin = async (ctx, _options) => {
90 const client = ctx.client
91 const projectName = ctx.directory ? basename(ctx.directory) : null
92 const title = projectName ? `OpenCode (${projectName})` : "OpenCode"
93@@ -269,5 +269,5 @@ const server: Plugin = async (ctx, _options) => {
94 }
95
96 export default {
97- server,
98+ server: PolicyEngine,
99 } satisfies PluginModule