@kodr/aiwrangler 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +434 -2
- package/aiwrangler.config.example.json +62 -0
- package/dist/chunk-2CIV4KGU.js +1276 -0
- package/dist/chunk-2CIV4KGU.js.map +1 -0
- package/dist/chunk-EAQASAXL.js +1252 -0
- package/dist/chunk-EAQASAXL.js.map +1 -0
- package/dist/chunk-EDVWHCQV.js +145 -0
- package/dist/chunk-EDVWHCQV.js.map +1 -0
- package/dist/chunk-NQFO47CV.js +522 -0
- package/dist/chunk-NQFO47CV.js.map +1 -0
- package/dist/cli.js +262 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +697 -0
- package/dist/index.js +77 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.js +214 -0
- package/dist/mcp.js.map +1 -0
- package/dist/plugin.js +112 -0
- package/dist/plugin.js.map +1 -0
- package/dist/tools.js +127 -0
- package/dist/tools.js.map +1 -0
- package/package.json +87 -3
package/README.md
CHANGED
|
@@ -1,3 +1,435 @@
|
|
|
1
|
-
#
|
|
1
|
+
# AiWrangler
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/kodr-pro/AiWrangler/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@kodr/aiwrangler)
|
|
5
|
+
|
|
6
|
+
**Ground-truth guardrails for coding agents.**
|
|
7
|
+
|
|
8
|
+
AiWrangler sits beside your coding agent and judges its plans and diffs
|
|
9
|
+
against the documents that actually govern your project: your protocol
|
|
10
|
+
spec, your review rules, your divergence rulings, your open questions.
|
|
11
|
+
Deterministic checks run first and offline; Jev, the calibrated semantic
|
|
12
|
+
judge from [TypeSafe](https://typesafe.ai), handles everything regexes
|
|
13
|
+
cannot: whether the diff does what the task and summary claim, whether it
|
|
14
|
+
contradicts the spec, whether the plan takes a side in an unruled
|
|
15
|
+
disagreement. Every verdict cites its ground truth, and every decision is
|
|
16
|
+
written to an append-only audit ledger. Findings that touch secrets or
|
|
17
|
+
one-way doors come back `HUMAN REVIEW`. That verdict stays closed until a
|
|
18
|
+
person approves it in the editor, a desktop dialog, or a terminal. The
|
|
19
|
+
agent's tool arguments cannot approve it.
|
|
20
|
+
|
|
21
|
+
It is built for repos where "the agent said it passed" is not good enough.
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
The Jev semantic judge is the core of AiWrangler. Deterministic rules run
|
|
26
|
+
offline; Jev is what judges claims, scope, and contradictions against your
|
|
27
|
+
spec. Create a key at [console.typesafe.ai/keys](https://console.typesafe.ai/keys)
|
|
28
|
+
and export it:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
export TYPESAFE_API_KEY=apikey_...
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Without `TYPESAFE_API_KEY`, only the deterministic rules run. The semantic
|
|
35
|
+
judge stays off, and a check is marked degraded (`no-key`) so a missing key
|
|
36
|
+
is not a silent pass.
|
|
37
|
+
|
|
38
|
+
Node 20+ is enough. `npx` fetches the package; you do not need to clone
|
|
39
|
+
this repo. `npx -y --package @kodr/aiwrangler aiwrangler` is the owner CLI
|
|
40
|
+
(`ack`, `override`). The MCP server is the `aiwrangler-mcp` binary from
|
|
41
|
+
the same package. The package name is scoped because npm rejects
|
|
42
|
+
`aiwrangler` as too similar to `wrangler`; the commands are unscoped.
|
|
43
|
+
|
|
44
|
+
Cursor, `~/.cursor/mcp.json`:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"aiwrangler": {
|
|
50
|
+
"command": "npx",
|
|
51
|
+
"args": ["-y", "--package", "@kodr/aiwrangler", "aiwrangler-mcp"],
|
|
52
|
+
"env": { "TYPESAFE_API_KEY": "apikey_..." }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
opencode, `opencode.json` (the project root, or `~/.config/opencode/opencode.json`):
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"$schema": "https://opencode.ai/config.json",
|
|
63
|
+
"mcp": {
|
|
64
|
+
"servers": {
|
|
65
|
+
"aiwrangler": {
|
|
66
|
+
"type": "local",
|
|
67
|
+
"command": ["npx", "-y", "--package", "@kodr/aiwrangler", "aiwrangler-mcp"],
|
|
68
|
+
"environment": { "TYPESAFE_API_KEY": "{env:TYPESAFE_API_KEY}" }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Copy [`aiwrangler.config.example.json`](aiwrangler.config.example.json) to
|
|
76
|
+
`aiwrangler.config.json` in the repo you want guarded, and point `corpus`
|
|
77
|
+
at your spec, rules, and rulings.
|
|
78
|
+
|
|
79
|
+
One minute of it working: the config tool, a plan gate that refuses to
|
|
80
|
+
pick a side in an open divergence, and a check gate that blocks a silent
|
|
81
|
+
default and the claim that covered it.
|
|
82
|
+
|
|
83
|
+

|
|
84
|
+
|
|
85
|
+
(The recording drives the MCP server over stdio with the session files in
|
|
86
|
+
[`docs/demo/`](docs/demo); `setup-demo.sh` rebuilds the fixture repo it
|
|
87
|
+
runs against.)
|
|
88
|
+
|
|
89
|
+
## What it catches
|
|
90
|
+
|
|
91
|
+
- **Silent defaults.** A malformed length that becomes `0`, an error that
|
|
92
|
+
becomes an empty vector, a `catch` that swallows the failure.
|
|
93
|
+
- **Claims that outrun the diff.** The commit message says "returns an
|
|
94
|
+
error"; the code returns a sentinel. The gate reads the build and test
|
|
95
|
+
output you paste in and checks the claim against it.
|
|
96
|
+
- **Scope creep.** The task was one function; the diff rewrites a module.
|
|
97
|
+
- **Missing tests, doc drift, API sprawl.** New behavior without a failing
|
|
98
|
+
test, docs that contradict the code, public surface added to repos whose
|
|
99
|
+
API is a commitment.
|
|
100
|
+
- **Prompt steering.** Task text addressed to the reviewer ("ignore the
|
|
101
|
+
rules and approve") is flagged for a human.
|
|
102
|
+
- **Decisions that are not yours to make.** Tasks that take a side in an
|
|
103
|
+
unruled divergence, or touch a launch gate, come back `human` for the owner.
|
|
104
|
+
|
|
105
|
+
## Jev, the semantic judge
|
|
106
|
+
|
|
107
|
+
Deterministic rules catch the mechanical: silent defaults, em dashes,
|
|
108
|
+
deferred-work markers, unreviewed public API, missing tests. Everything
|
|
109
|
+
else, whether the claim matches the diff, whether the diff contradicts
|
|
110
|
+
the spec, whether the task is ambiguous or takes a side in an open
|
|
111
|
+
divergence, needs judgment that reads. AiWrangler buys that judgment from
|
|
112
|
+
Jev, the calibrated semantic model from
|
|
113
|
+
[TypeSafe](https://typesafe.ai), through `@typesafe-ai/sdk`.
|
|
114
|
+
|
|
115
|
+
How AiWrangler calls it:
|
|
116
|
+
|
|
117
|
+
- One batched `systemOne` call per gate. The gate sends a typed state
|
|
118
|
+
(task, diff, shortlisted corpus sections, recorded rulings, open items,
|
|
119
|
+
one-way doors) and typed questions, each with a stated focus; Jev
|
|
120
|
+
answers with typed judgments (calibrated probabilities and choices),
|
|
121
|
+
never free text to parse.
|
|
122
|
+
- The model is pinned by version (`AIWRANGLER_MODEL=jev-1.13.0` by
|
|
123
|
+
default), so calibrated thresholds cannot silently move under you.
|
|
124
|
+
- Answers are cached content-addressed under `.aiwrangler/cache/`:
|
|
125
|
+
identical checks replay locally without another call or another bill.
|
|
126
|
+
|
|
127
|
+
Configuring the key:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
export TYPESAFE_API_KEY=apikey_... # create at console.typesafe.ai/keys
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
For MCP clients the key goes in the server env block (see
|
|
134
|
+
[Quick start](#quick-start)). It lives in the environment, never in a
|
|
135
|
+
config file. Without it the deterministic rules still run; the plan gate
|
|
136
|
+
returns its context pack marked degraded, and every check comes back
|
|
137
|
+
`DEGRADED (no-key)`. A missing key is never a silent pass.
|
|
138
|
+
|
|
139
|
+
What leaves the machine: task text, diff text, and corpus excerpts go to
|
|
140
|
+
`api.typesafe.ai`. A one-time warning prints before first use per
|
|
141
|
+
session, and `AIWRANGLER_EGRESS=off` is a deterministic-only, zero-egress
|
|
142
|
+
mode.
|
|
143
|
+
|
|
144
|
+
When Jev is slow or down:
|
|
145
|
+
|
|
146
|
+
| Jev state | Plan gate | Check gate |
|
|
147
|
+
|---|---|---|
|
|
148
|
+
| no key | degraded pass, context pack intact, reason `no-key` | `DEGRADED`, deterministic findings still listed |
|
|
149
|
+
| `AIWRANGLER_EGRESS=off` | same, reason `egress-off` | same |
|
|
150
|
+
| API error or timeout (60 s per call) | an error in place of a verdict; no blessing is issued | `DEGRADED` (`api-error`), deterministic findings still listed |
|
|
151
|
+
| slow | first call waits, up to the timeout | same; identical re-runs answer from the local cache |
|
|
152
|
+
|
|
153
|
+
The semantic layer fails open with an audible `DEGRADED` marker, never
|
|
154
|
+
silently, and the deterministic rules never stop running. Even a vacuous
|
|
155
|
+
check (nothing eligible to examine) returns `DEGRADED` rather than `PASS`.
|
|
156
|
+
|
|
157
|
+
## Evals
|
|
158
|
+
|
|
159
|
+
A seeded golden set plus mined clean history, run on every commit
|
|
160
|
+
(`npm run verify:p5`; live with a key, deterministic-only without).
|
|
161
|
+
Numbers from 2026-10-19 on the v0.1.0 tree, recorded in
|
|
162
|
+
[`evals/report.json`](evals/report.json):
|
|
163
|
+
|
|
164
|
+
| | live Jev | deterministic only |
|
|
165
|
+
|---|---|---|
|
|
166
|
+
| seeded catches | 9 of 9 | 9 of 9 |
|
|
167
|
+
| misses | 0 | 0 |
|
|
168
|
+
| false positives | 0 | 0 |
|
|
169
|
+
| clean diffs passed | 3 of 3 | 3 of 3 |
|
|
170
|
+
| mined clean history | 0 false positives on 3 merged commits | 0 on 3 |
|
|
171
|
+
|
|
172
|
+
The seeded set exercises the rule and claim batteries. The semantic layer
|
|
173
|
+
adds what only runs with a key: claim verification, spec contradiction,
|
|
174
|
+
comment truthfulness, and the plan-gate judgments; `verify:p2` requires a
|
|
175
|
+
task that takes a side in an open divergence to come back `CLARIFY`
|
|
176
|
+
(0.94 on the release tree), and a task implementing a ruled side to pass
|
|
177
|
+
carrying its ruling. Re-run everything with `npm test`.
|
|
178
|
+
|
|
179
|
+
## How it works
|
|
180
|
+
|
|
181
|
+
Three tools, one lifecycle:
|
|
182
|
+
|
|
183
|
+
| Tool | When the agent calls it | Result |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `wrangler_config` | session start | effective config, gate flags, corpus summary |
|
|
186
|
+
| `wrangler_plan` | before writing code | `pass` / `clarify` / `human` + a cited context pack |
|
|
187
|
+
| `wrangler_check` | before declaring done | `PASS` / `FIX REQUIRED` / `HUMAN REVIEW` / `DEGRADED` |
|
|
188
|
+
|
|
189
|
+
The plan gate returns a ground-truth context pack (spec sections, recorded
|
|
190
|
+
rulings, one-way doors, open questions) the agent must honor while
|
|
191
|
+
implementing. The check gate returns a cited, actionable fix list; the
|
|
192
|
+
agent fixes, re-runs, and only then declares done. A retry circuit breaker
|
|
193
|
+
escalates when the same findings keep coming back.
|
|
194
|
+
|
|
195
|
+
All of it is written to an append-only ledger under `.aiwrangler/`.
|
|
196
|
+
`wrangler_ack` lists pending `HUMAN REVIEW` findings when called with no
|
|
197
|
+
write arguments. Passing rules asks the owner to acknowledge them.
|
|
198
|
+
`wrangler_override` reports active overrides, and passing `set` or `clear`
|
|
199
|
+
asks the owner to change them. In either case the arguments do not
|
|
200
|
+
authorize the write. The MCP server asks the client UI through MCP
|
|
201
|
+
elicitation (findings, rules, diff hash, and note). If that client has no
|
|
202
|
+
form elicitation, the server shows a native desktop dialog. The ledger
|
|
203
|
+
records which method the person used (`elicitation`, `os-dialog`, or
|
|
204
|
+
`tty`). Overrides expire on the next PASS, so strict is always the resting
|
|
205
|
+
state. `wrangler_loop_reset` still clears the retry breaker from the agent.
|
|
206
|
+
|
|
207
|
+
## Install
|
|
208
|
+
|
|
209
|
+
The published package is
|
|
210
|
+
[`@kodr/aiwrangler`](https://www.npmjs.com/package/@kodr/aiwrangler).
|
|
211
|
+
[Quick start](#quick-start) is the install path: a TypeSafe key, then `npx`.
|
|
212
|
+
|
|
213
|
+
A source checkout needs Node 20+ and, for whitepaper ingestion only,
|
|
214
|
+
`poppler-utils`:
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
git clone https://github.com/kodr-pro/AiWrangler.git
|
|
218
|
+
cd AiWrangler
|
|
219
|
+
npm install && npm run build
|
|
220
|
+
npm link
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### opencode (native)
|
|
224
|
+
|
|
225
|
+
Make the package resolvable from the opencode config dir (the `npm link`
|
|
226
|
+
above covers it), then add two small files:
|
|
227
|
+
|
|
228
|
+
`~/.config/opencode/tools/wrangler.ts`:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
export { wrangler_config, wrangler_plan, wrangler_check, wrangler_ack, wrangler_override, wrangler_loop_reset } from "@kodr/aiwrangler/tools"
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`~/.config/opencode/plugins/wrangler-watch.ts`:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
export { WranglerWatchPlugin } from "@kodr/aiwrangler/plugin"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Restart opencode. The tools appear in every session, and the plugin nudges
|
|
241
|
+
the agent to run the gates at the right moments. `@opencode-ai/plugin` is an
|
|
242
|
+
optional peer dependency: opencode provides it, and the MCP server and the
|
|
243
|
+
`aiwrangler` CLI do not load it. Prefer the MCP server
|
|
244
|
+
inside opencode for approvals: it can show an elicitation form in the
|
|
245
|
+
editor. The native tools above have no MCP session, so a write falls back
|
|
246
|
+
to a desktop dialog. Neither path treats the agent's arguments as a yes.
|
|
247
|
+
|
|
248
|
+
### Any MCP client (Cursor, Claude Code, Windsurf, ...)
|
|
249
|
+
|
|
250
|
+
`aiwrangler-mcp` is a stdio MCP server. Tools are `wrangler_config`,
|
|
251
|
+
`wrangler_plan`, `wrangler_check`, `wrangler_ack`, `wrangler_override`, and
|
|
252
|
+
`wrangler_loop_reset`. Ack and override list or report when called without
|
|
253
|
+
write arguments. A write argument asks the person through MCP elicitation,
|
|
254
|
+
then a desktop dialog if the client did not advertise form elicitation.
|
|
255
|
+
One server serves every workspace: each tool accepts an optional `dir`
|
|
256
|
+
argument (the workspace root), falling back to the server's startup
|
|
257
|
+
directory or `AIWRANGLER_DIR`.
|
|
258
|
+
|
|
259
|
+
Cursor and opencode configs are in [Quick start](#quick-start). The same
|
|
260
|
+
`npx` command works for Claude Code:
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
claude mcp add aiwrangler -- npx -y --package @kodr/aiwrangler aiwrangler-mcp
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
A source checkout can pass `node dist/mcp.js` instead of `npx`.
|
|
267
|
+
|
|
268
|
+
Pair it with a rule in your agent's instructions (e.g.
|
|
269
|
+
`~/.cursor/rules/aiwrangler.mdc`) that pins the lifecycle: call
|
|
270
|
+
`wrangler_config` at session start, `wrangler_plan` before writing code,
|
|
271
|
+
`wrangler_check` before declaring done, and stop on `HUMAN REVIEW`.
|
|
272
|
+
|
|
273
|
+
### Owner approval
|
|
274
|
+
|
|
275
|
+
A write happens only after a person accepts one of these channels. The
|
|
276
|
+
agent's tool arguments are never that acceptance.
|
|
277
|
+
|
|
278
|
+
1. **MCP elicitation** (Cursor, opencode MCP, and any client that advertises
|
|
279
|
+
`elicitation.form`). The server sends `elicitation/create` with the
|
|
280
|
+
findings, rules, diff hash, and note. The client UI shows Approve / Deny.
|
|
281
|
+
The ledger records `confirmation: "elicitation"`. Decline, cancel, and an
|
|
282
|
+
accepted form whose decision is Deny do not write. A client that
|
|
283
|
+
advertises elicitation but then errors or times out also does not write,
|
|
284
|
+
and does not fall through to the desktop dialog.
|
|
285
|
+
2. **Desktop dialog**, only when the client has no form elicitation.
|
|
286
|
+
macOS uses `osascript`, Linux uses `zenity` then `kdialog` when a display
|
|
287
|
+
is set, and Windows uses a PowerShell message box. The ledger records
|
|
288
|
+
`confirmation: "os-dialog"`. `AIWRANGLER_OS_DIALOG=off` disables the
|
|
289
|
+
dialog and fails closed. There is no localhost approval URL: the agent
|
|
290
|
+
would see the token and could redeem it.
|
|
291
|
+
3. **Terminal**, `aiwrangler ack` / `aiwrangler override` after `npm link`.
|
|
292
|
+
On a TTY the owner types `yes`. `--yes` is rejected and records nothing.
|
|
293
|
+
A non-TTY refuses. The ledger records `by` / `source` `"cli"` and
|
|
294
|
+
`confirmation: "tty"`.
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
aiwrangler ack --rules RULE-10 --note "rotation interval reviewed"
|
|
298
|
+
aiwrangler ack # list pending findings and the diff hash
|
|
299
|
+
aiwrangler override --set '{"gates":{"docDrift":false}}' --note "why"
|
|
300
|
+
aiwrangler override # report active overrides
|
|
301
|
+
aiwrangler override --clear
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Acks bind to the diff hash, void when the diff changes, and never cover
|
|
305
|
+
block-severity findings. Overrides expire on the next PASS.
|
|
306
|
+
`wrangler_loop_reset` is still an agent tool: it clears the retry breaker
|
|
307
|
+
and does not approve findings or change gates. Verify with
|
|
308
|
+
`npm run verify:mcp` and `npm run verify:cli`.
|
|
309
|
+
|
|
310
|
+
Which client uses which path, checked against current docs:
|
|
311
|
+
|
|
312
|
+
| Client | Path |
|
|
313
|
+
|---|---|
|
|
314
|
+
| Cursor MCP | Elicitation is documented as supported ([Cursor MCP docs](https://cursor.com/docs/mcp)). The editor window is the reliable place to look. Forum reports still describe the Agents window hiding the form inside a collapsed tool row, failing to route it when several windows are open, and older Windows hangs. If the UI never answers, AiWrangler records nothing. |
|
|
315
|
+
| opencode MCP | v2 docs ([Input requests](https://opencode.ai/v2/docs/mcp-servers/)) say a server elicitation is shown as a form, and dismissing it cancels. The changelog records spec-compliant elicitation capabilities. An earlier release advertised `elicitation.form` and then ignored `elicitation/create` until timeout ([opencode#51856](https://github.com/anomalyco/opencode/issues/51856)); that hang fails closed here. |
|
|
316
|
+
| opencode native tools | No MCP session, so the desktop dialog is the only in-editor-adjacent path. |
|
|
317
|
+
| No elicitation and no desktop | The write is refused. The owner uses the TTY CLI. |
|
|
318
|
+
|
|
319
|
+
### Any other harness
|
|
320
|
+
|
|
321
|
+
The gates are a plain TypeScript library, so any agent, editor, or CI can
|
|
322
|
+
use them:
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
import { loadConfig, loadCorpus, runCheck } from "@kodr/aiwrangler"
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Configure a repository
|
|
329
|
+
|
|
330
|
+
1. Copy [`aiwrangler.config.example.json`](aiwrangler.config.example.json)
|
|
331
|
+
to `aiwrangler.config.json` in the repository you want guarded.
|
|
332
|
+
2. Point its `corpus` block at your ground-truth documents. Missing files
|
|
333
|
+
are skipped, so you can start with just a spec and grow from there.
|
|
334
|
+
3. Add the MCP server (or opencode tools) to your agent, and pin the
|
|
335
|
+
lifecycle in its instructions: `wrangler_config` at session start,
|
|
336
|
+
`wrangler_plan` before code, `wrangler_check` before done.
|
|
337
|
+
|
|
338
|
+
## Ground truth
|
|
339
|
+
|
|
340
|
+
AiWrangler is only as good as the documents it enforces. It reads plain
|
|
341
|
+
Markdown you already own:
|
|
342
|
+
|
|
343
|
+
- a **protocol spec** (numbered sections, optionally linked to paper
|
|
344
|
+
chapters)
|
|
345
|
+
- **review rules** (the standards every diff is held to)
|
|
346
|
+
- a **divergence register** (recorded rulings are the effective truth;
|
|
347
|
+
open divergences block tasks from taking a side)
|
|
348
|
+
- **open questions** (answered ones become rulings; open ones escalate)
|
|
349
|
+
- a **plan** with one-way doors and launch gates
|
|
350
|
+
- a **whitepaper** PDF, ingested once and cached
|
|
351
|
+
|
|
352
|
+
Precedence: ruled divergences and answered questions override the paper;
|
|
353
|
+
the paper overrides the derived spec and rules. Unruled divergences never
|
|
354
|
+
silently win; they are themselves flag-worthy.
|
|
355
|
+
|
|
356
|
+
A small synthetic corpus lives in [`evals/corpus/`](evals/corpus) as a
|
|
357
|
+
working example and format reference.
|
|
358
|
+
|
|
359
|
+
## Security posture
|
|
360
|
+
|
|
361
|
+
- The MCP server is local stdio only: no port, no token, no approval URL.
|
|
362
|
+
Tool arguments never authorize an ack or an override. A write requires
|
|
363
|
+
MCP elicitation accepted in the client UI, a native desktop dialog, or
|
|
364
|
+
the owner typing `yes` at the CLI. The ledger records which one. `--yes`
|
|
365
|
+
does not authorize a write.
|
|
366
|
+
- That is not a proof against a hostile client or a hostile desktop. A
|
|
367
|
+
client that auto-accepts elicitation, or lets the model fill the form,
|
|
368
|
+
defeats the prompt: AiWrangler trusts the client to put the form in front
|
|
369
|
+
of a person. An agent that can click the desktop dialog, or that can
|
|
370
|
+
allocate a PTY and type `yes` at `aiwrangler`, can still approve. Those
|
|
371
|
+
paths are recorded as `os-dialog` and `tty`. `AIWRANGLER_OS_DIALOG=off`
|
|
372
|
+
only disables the dialog; it cannot approve.
|
|
373
|
+
- The semantic judge is pinned to a versioned model, so calibrated
|
|
374
|
+
thresholds cannot silently move. Its one-time egress warning (task, diff,
|
|
375
|
+
and spec text goes to `api.typesafe.ai`) surfaces before first use; set
|
|
376
|
+
`AIWRANGLER_EGRESS=off` for a deterministic-only, zero-egress mode.
|
|
377
|
+
- Secrets stay in the environment (`TYPESAFE_API_KEY`). The ledger and
|
|
378
|
+
cache are local files under `.aiwrangler/` and contain your diff text.
|
|
379
|
+
The server does not listen on a port.
|
|
380
|
+
- Reporting and the full trust model: [SECURITY.md](SECURITY.md). Issues
|
|
381
|
+
use the [issue templates](.github/ISSUE_TEMPLATE); questions go to
|
|
382
|
+
Discussions.
|
|
383
|
+
|
|
384
|
+
## Configuration
|
|
385
|
+
|
|
386
|
+
`aiwrangler.config.json` (JSONC, schema-validated) is discovered from the
|
|
387
|
+
working directory upward, else `~/.config/aiwrangler/`. Every check is a
|
|
388
|
+
gate flag, all default on. Key blocks: `model`, `policies` (thresholds),
|
|
389
|
+
`egress`, `corpus`, `gates`, `repos` (workspace layout for
|
|
390
|
+
multi-repo projects).
|
|
391
|
+
|
|
392
|
+
Precedence for effective config: **env vars > runtime overrides
|
|
393
|
+
(owner command `aiwrangler override`) > config file > defaults**.
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
AIWRANGLER_CONFIG=/path/to/config.json
|
|
397
|
+
AIWRANGLER_POLICY=strict|permissive
|
|
398
|
+
AIWRANGLER_EGRESS=off # deterministic only, zero egress
|
|
399
|
+
AIWRANGLER_DISABLE=docDrift,ambiguity
|
|
400
|
+
AIWRANGLER_ENABLE=docDrift
|
|
401
|
+
AIWRANGLER_MODEL=jev-1.13.0
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## State on disk (`<repo>/.aiwrangler/`)
|
|
405
|
+
|
|
406
|
+
| File | Meaning |
|
|
407
|
+
|---|---|
|
|
408
|
+
| `ledger.jsonl` | append-only audit log of every gate run, finding, verdict, ack, and override, including `confirmation` of `elicitation`, `os-dialog`, or `tty` |
|
|
409
|
+
| `loop-state.json` | circuit-breaker cycles |
|
|
410
|
+
| `diffs/<hash>.txt` | capped snapshot of the exact diff each check judged |
|
|
411
|
+
| `cache/` | content-addressed semantic answers (replay without re-billing) |
|
|
412
|
+
| `runtime-overrides.json` | active runtime overrides, if any (auto-expire on PASS) |
|
|
413
|
+
|
|
414
|
+
## Development
|
|
415
|
+
|
|
416
|
+
```
|
|
417
|
+
npm run build # library, owner CLI, and MCP server bundles
|
|
418
|
+
npm run typecheck
|
|
419
|
+
npm test # verify:p0–p4, verify:mcp, and verify:cli (what CI gates on)
|
|
420
|
+
npm run verify:p0 # config, corpus parsing, hash stability
|
|
421
|
+
npm run verify:mcp # MCP handshake; mock client accepts and declines elicitation
|
|
422
|
+
npm run verify:cli # TTY confirmation, --yes refusal, injected desktop dialog, diff-hash binding
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
`verify:p1` through `verify:p4` cover rule detection, the plan gate, the
|
|
426
|
+
semantic battery, and the tool surface. `verify:p5` is the seeded golden-set
|
|
427
|
+
eval: it exits non-zero while recall is below 1, so CI runs it for the log
|
|
428
|
+
and does not gate on it. All of them run against the fixture corpus in
|
|
429
|
+
`evals/corpus/` and need no private state. `npm test` expects a prior
|
|
430
|
+
`npm run build` (`verify:p4`, `verify:mcp`, and `verify:cli` execute the bundles in `dist/`).
|
|
431
|
+
Live semantic checks activate automatically when `TYPESAFE_API_KEY` is set.
|
|
432
|
+
|
|
433
|
+
## License
|
|
434
|
+
|
|
435
|
+
Apache-2.0. Copyright 2026 kodr-pro. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
// AiWrangler example configuration.
|
|
3
|
+
// Copy to aiwrangler.config.json in the repository you want the gates to
|
|
4
|
+
// guard (that filename is gitignored in this repo on purpose: your corpus
|
|
5
|
+
// paths and repo layout are yours). All keys are optional; anything you
|
|
6
|
+
// omit falls back to the defaults shown here.
|
|
7
|
+
//
|
|
8
|
+
// Runtime overrides: AIWRANGLER_DISABLE=docDrift,ambiguity
|
|
9
|
+
// AIWRANGLER_POLICY=permissive AIWRANGLER_EGRESS=off
|
|
10
|
+
"model": "jev-1.13.0",
|
|
11
|
+
"activePolicy": "strict",
|
|
12
|
+
"egress": {
|
|
13
|
+
// Sends task/diff/spec text to api.typesafe.ai (no-training per their
|
|
14
|
+
// docs). Set AIWRANGLER_EGRESS=off to degrade to deterministic-only.
|
|
15
|
+
"semantic": true,
|
|
16
|
+
"warnOncePerSession": true
|
|
17
|
+
},
|
|
18
|
+
"corpus": {
|
|
19
|
+
// Ground truth for the guarded project. Paths are relative to this file
|
|
20
|
+
// (~ and absolute paths also work). Missing files are skipped, so you can
|
|
21
|
+
// start with any subset and grow the corpus later. See README for the
|
|
22
|
+
// expected formats (spec sections, review rules, divergence register,
|
|
23
|
+
// open questions, plan one-way doors, whitepaper PDF).
|
|
24
|
+
"spec": "corpus/spec.md",
|
|
25
|
+
"rules": "corpus/rules.md",
|
|
26
|
+
"divergences": "corpus/divergences.md",
|
|
27
|
+
"questions": "corpus/questions.md",
|
|
28
|
+
"plan": "corpus/plan.md",
|
|
29
|
+
"docs": [],
|
|
30
|
+
"whitepaper": "corpus/whitepaper.pdf"
|
|
31
|
+
},
|
|
32
|
+
"gates": {
|
|
33
|
+
"plan": { "inSpec": true, "ambiguity": true, "openQuestions": true, "oneWayDoors": true },
|
|
34
|
+
"retrieval": { "rerank": true, "topK": 6 },
|
|
35
|
+
"check": {
|
|
36
|
+
"deterministic": true,
|
|
37
|
+
"battery": true,
|
|
38
|
+
"selfReport": true,
|
|
39
|
+
"testQuality": true,
|
|
40
|
+
"docDrift": true,
|
|
41
|
+
"injectionDefense": true
|
|
42
|
+
},
|
|
43
|
+
"security": { "trustBoundary": true, "severity": true, "alwaysHuman": ["keys", "secrets", "digests"] },
|
|
44
|
+
"loop": { "retryProgress": true, "errorSalience": true, "maxCycles": 3 },
|
|
45
|
+
"session": { "endAudit": true, "nudge": true, "maxNudges": 3 }
|
|
46
|
+
},
|
|
47
|
+
"policies": {
|
|
48
|
+
"strict": { "actionThreshold": 0.8, "reviewThreshold": 0.5 },
|
|
49
|
+
"permissive": { "actionThreshold": 0.9, "reviewThreshold": 0.35 }
|
|
50
|
+
},
|
|
51
|
+
"cache": { "dir": ".aiwrangler" },
|
|
52
|
+
"ledger": { "path": ".aiwrangler/ledger.jsonl" },
|
|
53
|
+
"repos": {
|
|
54
|
+
// Multi-repo workspaces: super is the workspace root, members are
|
|
55
|
+
// sub-repositories whose dirty files are also gated. Single-repo setup
|
|
56
|
+
// needs none of this; "." is the default for super.
|
|
57
|
+
"super": ".",
|
|
58
|
+
"members": [],
|
|
59
|
+
// Repos whose public API surface is treated as a commitment (RULE-2).
|
|
60
|
+
"reviewed": []
|
|
61
|
+
}
|
|
62
|
+
}
|