pywrit 0.2.7__tar.gz → 0.2.8__tar.gz
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.
- {pywrit-0.2.7/src/pywrit.egg-info → pywrit-0.2.8}/PKG-INFO +54 -12
- {pywrit-0.2.7 → pywrit-0.2.8}/README.md +52 -10
- {pywrit-0.2.7 → pywrit-0.2.8}/pyproject.toml +2 -2
- {pywrit-0.2.7 → pywrit-0.2.8}/src/pywrit/__init__.py +1 -1
- {pywrit-0.2.7 → pywrit-0.2.8}/src/pywrit/cli.py +520 -64
- {pywrit-0.2.7 → pywrit-0.2.8/src/pywrit.egg-info}/PKG-INFO +54 -12
- {pywrit-0.2.7 → pywrit-0.2.8}/src/pywrit.egg-info/SOURCES.txt +3 -1
- pywrit-0.2.8/tests/test_scan_python.py +130 -0
- {pywrit-0.2.7 → pywrit-0.2.8}/tests/test_scan_tsjs.py +44 -0
- pywrit-0.2.8/tests/test_wrap.py +183 -0
- {pywrit-0.2.7 → pywrit-0.2.8}/LICENSE +0 -0
- {pywrit-0.2.7 → pywrit-0.2.8}/setup.cfg +0 -0
- {pywrit-0.2.7 → pywrit-0.2.8}/src/pywrit/client.py +0 -0
- {pywrit-0.2.7 → pywrit-0.2.8}/src/pywrit/errors.py +0 -0
- {pywrit-0.2.7 → pywrit-0.2.8}/src/pywrit.egg-info/dependency_links.txt +0 -0
- {pywrit-0.2.7 → pywrit-0.2.8}/src/pywrit.egg-info/entry_points.txt +0 -0
- {pywrit-0.2.7 → pywrit-0.2.8}/src/pywrit.egg-info/requires.txt +0 -0
- {pywrit-0.2.7 → pywrit-0.2.8}/src/pywrit.egg-info/top_level.txt +0 -0
- {pywrit-0.2.7 → pywrit-0.2.8}/tests/test_client.py +0 -0
- {pywrit-0.2.7 → pywrit-0.2.8}/tests/test_scan_json.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pywrit
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: Python client + CLI for Writ — the gate before the write
|
|
5
5
|
License-Expression: MIT
|
|
6
|
-
Project-URL: Homepage, https://github.com/
|
|
6
|
+
Project-URL: Homepage, https://github.com/withwrit/pywrit
|
|
7
7
|
Project-URL: Documentation, https://withwrit.com/docs
|
|
8
8
|
Requires-Python: >=3.9
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
@@ -27,7 +27,9 @@ client and `writ` CLI for [Writ](https://withwrit.com): an allow/deny gate that
|
|
|
27
27
|
in front of your agent's consequential writes (database, HTTP, files, email, queues,
|
|
28
28
|
AWS) and records a hash-chained receipt for every decision.
|
|
29
29
|
|
|
30
|
-
](https://mcpservers.org/servers/avenuedadmin/pywrit)
|
|
31
|
+
|
|
32
|
+

|
|
31
33
|
|
|
32
34
|
`writ scan` is **local, deterministic, and free**: it parses your code with Python's
|
|
33
35
|
`ast` module, makes no network calls, and needs no API key.
|
|
@@ -52,7 +54,7 @@ first run it installs `pywrit[polyglot]` from PyPI using your Python 3.9+
|
|
|
52
54
|
environment overrides (`WRIT_PYTHON`, `WRIT_SCAN_NO_INSTALL`), and the
|
|
53
55
|
install test.
|
|
54
56
|
|
|
55
|
-
## 60-second quickstart: scan →
|
|
57
|
+
## 60-second quickstart: scan → wrap → gate
|
|
56
58
|
|
|
57
59
|
**1. Scan.** See which functions write, and how many of them are gated.
|
|
58
60
|
|
|
@@ -75,14 +77,17 @@ It also prints a **risk report** (0-100, weighted by risk tier), writes the disc
|
|
|
75
77
|
verbs to `writ-policy.json`, and shows the instrumentation it would add as a unified
|
|
76
78
|
diff. Nothing in your code changes yet. `writ scan . --score` prints just the risk report.
|
|
77
79
|
|
|
78
|
-
**2.
|
|
80
|
+
**2. Wrap.** One command takes the repo from unwrapped to wrapped: scan, show the diff,
|
|
81
|
+
ask for explicit approval, apply the gates, and report before/after coverage.
|
|
79
82
|
|
|
80
83
|
```bash
|
|
81
|
-
writ
|
|
82
|
-
writ
|
|
84
|
+
writ wrap . # interactive approval
|
|
85
|
+
writ wrap . --yes # no prompt (e.g. in CI)
|
|
83
86
|
```
|
|
84
87
|
|
|
85
|
-
|
|
88
|
+
`--dry-run` previews the plan and exits before prompting; `--diff-only` prints the
|
|
89
|
+
unified diff for review tools. Each gated function now asks Writ before it writes, and
|
|
90
|
+
fails closed:
|
|
86
91
|
|
|
87
92
|
```python
|
|
88
93
|
def issue_refund(charge_id, amount_cents):
|
|
@@ -91,7 +96,8 @@ def issue_refund(charge_id, amount_cents):
|
|
|
91
96
|
...
|
|
92
97
|
```
|
|
93
98
|
|
|
94
|
-
|
|
99
|
+
`writ scan . --apply` is still available as the manual alternative. Re-run `writ scan .`
|
|
100
|
+
and you'll see `gated: 4/4 (100%)`.
|
|
95
101
|
|
|
96
102
|
**3. Gate.** Get a free API key, load the discovered policy, and run your agent.
|
|
97
103
|
|
|
@@ -152,7 +158,7 @@ clients, Prisma writes, and JS SDK calls such as `stripe.refunds.create(...)`.
|
|
|
152
158
|
(Still not covered: the *Python* SDK equivalents like `stripe.Refund.create(...)`
|
|
153
159
|
— see "Not covered" above.)
|
|
154
160
|
|
|
155
|
-

|
|
156
162
|
|
|
157
163
|
## GitHub Action
|
|
158
164
|
|
|
@@ -163,7 +169,7 @@ job summary. The check fails when an ungated finding meets your
|
|
|
163
169
|
|
|
164
170
|
```yaml
|
|
165
171
|
- uses: actions/checkout@v4
|
|
166
|
-
- uses:
|
|
172
|
+
- uses: withwrit/pywrit@v0
|
|
167
173
|
with:
|
|
168
174
|
fail-on-risk: high # high | medium | low | never
|
|
169
175
|
```
|
|
@@ -171,6 +177,39 @@ job summary. The check fails when an ungated finding meets your
|
|
|
171
177
|
No API key needed. Full reference: [docs/github-action.md](docs/github-action.md) ·
|
|
172
178
|
example workflow: [examples/github-action/writ-scan.yml](examples/github-action/writ-scan.yml)
|
|
173
179
|
|
|
180
|
+
## MCP server (`writ-mcp`)
|
|
181
|
+
|
|
182
|
+
Give any MCP-compatible agent point-of-action policy checks. `writ-mcp` is a
|
|
183
|
+
[Model Context Protocol](https://modelcontextprotocol.io) server (stdio transport,
|
|
184
|
+
built on the MCP Python SDK) that exposes the Writ gate as **8 MCP tools**:
|
|
185
|
+
the agent calls `writ_check` before a consequential write and gets back `ALLOW`,
|
|
186
|
+
`DENY`, or `STEP_UP` — with a tamper-evident, hash-chained audit receipt for
|
|
187
|
+
every decision.
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
uvx writ-mcp # no install — runs on demand
|
|
191
|
+
# or
|
|
192
|
+
pip install writ-mcp # then run `writ-mcp`
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
| Tool | What it does |
|
|
196
|
+
|---|---|
|
|
197
|
+
| `writ_check` | The gate: `ALLOW` / `DENY` / `STEP_UP` for a proposed write |
|
|
198
|
+
| `writ_verify_token` | Validate an `ALLOW` auth token (catches purpose drift) |
|
|
199
|
+
| `writ_grant` | Human-sponsor approval for the `STEP_UP` path |
|
|
200
|
+
| `writ_revoke` / `writ_reinstate` | The kill switch |
|
|
201
|
+
| `writ_receipts` | Read the tenant's audit log |
|
|
202
|
+
| `writ_policy` | Manage the verb policy |
|
|
203
|
+
| `writ_sandbox` | Keyless 90-second demo grant — no API key needed |
|
|
204
|
+
|
|
205
|
+
The gate tools need a free Writ API key (`WRIT_API_KEY`): 10,000 receipts/month
|
|
206
|
+
free, no credit card. `writ_sandbox` works with no key at all.
|
|
207
|
+
|
|
208
|
+
The server implementation lives in [`mcp/`](mcp/) (MIT). Also published on the
|
|
209
|
+
official MCP Registry as `io.github.withwrit/writ`, on
|
|
210
|
+
[Smithery](https://smithery.ai/server/withwrit/writ-mcp), and listed on
|
|
211
|
+
[Glama](https://glama.ai/mcp/servers/withwrit/pywrit).
|
|
212
|
+
|
|
174
213
|
## Python client
|
|
175
214
|
|
|
176
215
|
```python
|
|
@@ -223,6 +262,9 @@ What's covered:
|
|
|
223
262
|
## CLI reference
|
|
224
263
|
|
|
225
264
|
```bash
|
|
265
|
+
writ wrap . # scan, diff, approve, apply, verify coverage
|
|
266
|
+
writ wrap . --dry-run # preview only
|
|
267
|
+
writ wrap . --yes # non-interactive CI mode
|
|
226
268
|
writ check --key writ_... --sponsor acme --agent agent-7 \
|
|
227
269
|
--verb db.write --target prod.customers --purpose "backfill region field"
|
|
228
270
|
writ policy --key writ_... --set payments.refund require_grant
|
|
@@ -241,7 +283,7 @@ Show that your agent's writes are gated by Writ. Two flavors:
|
|
|
241
283
|
**Static** (works today, no API key) — same design, no live count:
|
|
242
284
|
|
|
243
285
|
```md
|
|
244
|
-
[](https://withwrit.com)
|
|
245
287
|
```
|
|
246
288
|
|
|
247
289
|
**Dynamic** (live 30-day gated-write count from your audit log) — mint a badge
|
|
@@ -5,7 +5,9 @@ client and `writ` CLI for [Writ](https://withwrit.com): an allow/deny gate that
|
|
|
5
5
|
in front of your agent's consequential writes (database, HTTP, files, email, queues,
|
|
6
6
|
AWS) and records a hash-chained receipt for every decision.
|
|
7
7
|
|
|
8
|
-
](https://mcpservers.org/servers/avenuedadmin/pywrit)
|
|
9
|
+
|
|
10
|
+

|
|
9
11
|
|
|
10
12
|
`writ scan` is **local, deterministic, and free**: it parses your code with Python's
|
|
11
13
|
`ast` module, makes no network calls, and needs no API key.
|
|
@@ -30,7 +32,7 @@ first run it installs `pywrit[polyglot]` from PyPI using your Python 3.9+
|
|
|
30
32
|
environment overrides (`WRIT_PYTHON`, `WRIT_SCAN_NO_INSTALL`), and the
|
|
31
33
|
install test.
|
|
32
34
|
|
|
33
|
-
## 60-second quickstart: scan →
|
|
35
|
+
## 60-second quickstart: scan → wrap → gate
|
|
34
36
|
|
|
35
37
|
**1. Scan.** See which functions write, and how many of them are gated.
|
|
36
38
|
|
|
@@ -53,14 +55,17 @@ It also prints a **risk report** (0-100, weighted by risk tier), writes the disc
|
|
|
53
55
|
verbs to `writ-policy.json`, and shows the instrumentation it would add as a unified
|
|
54
56
|
diff. Nothing in your code changes yet. `writ scan . --score` prints just the risk report.
|
|
55
57
|
|
|
56
|
-
**2.
|
|
58
|
+
**2. Wrap.** One command takes the repo from unwrapped to wrapped: scan, show the diff,
|
|
59
|
+
ask for explicit approval, apply the gates, and report before/after coverage.
|
|
57
60
|
|
|
58
61
|
```bash
|
|
59
|
-
writ
|
|
60
|
-
writ
|
|
62
|
+
writ wrap . # interactive approval
|
|
63
|
+
writ wrap . --yes # no prompt (e.g. in CI)
|
|
61
64
|
```
|
|
62
65
|
|
|
63
|
-
|
|
66
|
+
`--dry-run` previews the plan and exits before prompting; `--diff-only` prints the
|
|
67
|
+
unified diff for review tools. Each gated function now asks Writ before it writes, and
|
|
68
|
+
fails closed:
|
|
64
69
|
|
|
65
70
|
```python
|
|
66
71
|
def issue_refund(charge_id, amount_cents):
|
|
@@ -69,7 +74,8 @@ def issue_refund(charge_id, amount_cents):
|
|
|
69
74
|
...
|
|
70
75
|
```
|
|
71
76
|
|
|
72
|
-
|
|
77
|
+
`writ scan . --apply` is still available as the manual alternative. Re-run `writ scan .`
|
|
78
|
+
and you'll see `gated: 4/4 (100%)`.
|
|
73
79
|
|
|
74
80
|
**3. Gate.** Get a free API key, load the discovered policy, and run your agent.
|
|
75
81
|
|
|
@@ -130,7 +136,7 @@ clients, Prisma writes, and JS SDK calls such as `stripe.refunds.create(...)`.
|
|
|
130
136
|
(Still not covered: the *Python* SDK equivalents like `stripe.Refund.create(...)`
|
|
131
137
|
— see "Not covered" above.)
|
|
132
138
|
|
|
133
|
-

|
|
134
140
|
|
|
135
141
|
## GitHub Action
|
|
136
142
|
|
|
@@ -141,7 +147,7 @@ job summary. The check fails when an ungated finding meets your
|
|
|
141
147
|
|
|
142
148
|
```yaml
|
|
143
149
|
- uses: actions/checkout@v4
|
|
144
|
-
- uses:
|
|
150
|
+
- uses: withwrit/pywrit@v0
|
|
145
151
|
with:
|
|
146
152
|
fail-on-risk: high # high | medium | low | never
|
|
147
153
|
```
|
|
@@ -149,6 +155,39 @@ job summary. The check fails when an ungated finding meets your
|
|
|
149
155
|
No API key needed. Full reference: [docs/github-action.md](docs/github-action.md) ·
|
|
150
156
|
example workflow: [examples/github-action/writ-scan.yml](examples/github-action/writ-scan.yml)
|
|
151
157
|
|
|
158
|
+
## MCP server (`writ-mcp`)
|
|
159
|
+
|
|
160
|
+
Give any MCP-compatible agent point-of-action policy checks. `writ-mcp` is a
|
|
161
|
+
[Model Context Protocol](https://modelcontextprotocol.io) server (stdio transport,
|
|
162
|
+
built on the MCP Python SDK) that exposes the Writ gate as **8 MCP tools**:
|
|
163
|
+
the agent calls `writ_check` before a consequential write and gets back `ALLOW`,
|
|
164
|
+
`DENY`, or `STEP_UP` — with a tamper-evident, hash-chained audit receipt for
|
|
165
|
+
every decision.
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
uvx writ-mcp # no install — runs on demand
|
|
169
|
+
# or
|
|
170
|
+
pip install writ-mcp # then run `writ-mcp`
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
| Tool | What it does |
|
|
174
|
+
|---|---|
|
|
175
|
+
| `writ_check` | The gate: `ALLOW` / `DENY` / `STEP_UP` for a proposed write |
|
|
176
|
+
| `writ_verify_token` | Validate an `ALLOW` auth token (catches purpose drift) |
|
|
177
|
+
| `writ_grant` | Human-sponsor approval for the `STEP_UP` path |
|
|
178
|
+
| `writ_revoke` / `writ_reinstate` | The kill switch |
|
|
179
|
+
| `writ_receipts` | Read the tenant's audit log |
|
|
180
|
+
| `writ_policy` | Manage the verb policy |
|
|
181
|
+
| `writ_sandbox` | Keyless 90-second demo grant — no API key needed |
|
|
182
|
+
|
|
183
|
+
The gate tools need a free Writ API key (`WRIT_API_KEY`): 10,000 receipts/month
|
|
184
|
+
free, no credit card. `writ_sandbox` works with no key at all.
|
|
185
|
+
|
|
186
|
+
The server implementation lives in [`mcp/`](mcp/) (MIT). Also published on the
|
|
187
|
+
official MCP Registry as `io.github.withwrit/writ`, on
|
|
188
|
+
[Smithery](https://smithery.ai/server/withwrit/writ-mcp), and listed on
|
|
189
|
+
[Glama](https://glama.ai/mcp/servers/withwrit/pywrit).
|
|
190
|
+
|
|
152
191
|
## Python client
|
|
153
192
|
|
|
154
193
|
```python
|
|
@@ -201,6 +240,9 @@ What's covered:
|
|
|
201
240
|
## CLI reference
|
|
202
241
|
|
|
203
242
|
```bash
|
|
243
|
+
writ wrap . # scan, diff, approve, apply, verify coverage
|
|
244
|
+
writ wrap . --dry-run # preview only
|
|
245
|
+
writ wrap . --yes # non-interactive CI mode
|
|
204
246
|
writ check --key writ_... --sponsor acme --agent agent-7 \
|
|
205
247
|
--verb db.write --target prod.customers --purpose "backfill region field"
|
|
206
248
|
writ policy --key writ_... --set payments.refund require_grant
|
|
@@ -219,7 +261,7 @@ Show that your agent's writes are gated by Writ. Two flavors:
|
|
|
219
261
|
**Static** (works today, no API key) — same design, no live count:
|
|
220
262
|
|
|
221
263
|
```md
|
|
222
|
-
[](https://withwrit.com)
|
|
223
265
|
```
|
|
224
266
|
|
|
225
267
|
**Dynamic** (live 30-day gated-write count from your audit log) — mint a badge
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pywrit"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.8"
|
|
8
8
|
description = "Python client + CLI for Writ — the gate before the write"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -22,7 +22,7 @@ polyglot = [
|
|
|
22
22
|
keywords = ["ai-agents", "policy", "audit-log", "guardrails"]
|
|
23
23
|
|
|
24
24
|
[project.urls]
|
|
25
|
-
Homepage = "https://github.com/
|
|
25
|
+
Homepage = "https://github.com/withwrit/pywrit"
|
|
26
26
|
Documentation = "https://withwrit.com/docs"
|
|
27
27
|
|
|
28
28
|
[project.scripts]
|