pywrit 0.2.7__tar.gz → 0.2.9__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.9}/PKG-INFO +66 -13
- {pywrit-0.2.7 → pywrit-0.2.9}/README.md +64 -11
- {pywrit-0.2.7 → pywrit-0.2.9}/pyproject.toml +2 -2
- {pywrit-0.2.7 → pywrit-0.2.9}/src/pywrit/__init__.py +1 -1
- {pywrit-0.2.7 → pywrit-0.2.9}/src/pywrit/cli.py +567 -67
- {pywrit-0.2.7 → pywrit-0.2.9/src/pywrit.egg-info}/PKG-INFO +66 -13
- {pywrit-0.2.7 → pywrit-0.2.9}/src/pywrit.egg-info/SOURCES.txt +3 -1
- pywrit-0.2.9/tests/test_scan_python.py +130 -0
- {pywrit-0.2.7 → pywrit-0.2.9}/tests/test_scan_tsjs.py +110 -3
- pywrit-0.2.9/tests/test_wrap.py +183 -0
- {pywrit-0.2.7 → pywrit-0.2.9}/LICENSE +0 -0
- {pywrit-0.2.7 → pywrit-0.2.9}/setup.cfg +0 -0
- {pywrit-0.2.7 → pywrit-0.2.9}/src/pywrit/client.py +0 -0
- {pywrit-0.2.7 → pywrit-0.2.9}/src/pywrit/errors.py +0 -0
- {pywrit-0.2.7 → pywrit-0.2.9}/src/pywrit.egg-info/dependency_links.txt +0 -0
- {pywrit-0.2.7 → pywrit-0.2.9}/src/pywrit.egg-info/entry_points.txt +0 -0
- {pywrit-0.2.7 → pywrit-0.2.9}/src/pywrit.egg-info/requires.txt +0 -0
- {pywrit-0.2.7 → pywrit-0.2.9}/src/pywrit.egg-info/top_level.txt +0 -0
- {pywrit-0.2.7 → pywrit-0.2.9}/tests/test_client.py +0 -0
- {pywrit-0.2.7 → pywrit-0.2.9}/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.9
|
|
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
|
|
|
@@ -148,11 +154,15 @@ Python scanning never needs it.
|
|
|
148
154
|
TS/JS is **scan-only**: findings are listed with verbs for your policy file,
|
|
149
155
|
but `writ scan --apply` never rewrites TS/JS files — gate those by hand.
|
|
150
156
|
Covered patterns: `fetch`/`axios` writes, `fs` writes, SQL through knex-style
|
|
151
|
-
clients, Prisma writes,
|
|
157
|
+
clients, Prisma writes, JS SDK calls such as `stripe.refunds.create(...)`, and
|
|
158
|
+
generated REST resource clients: `create` / `update` / `patch` / `remove`
|
|
159
|
+
methods (plus their `...WithHttpInfo` variants) that build request options
|
|
160
|
+
with an explicit write HTTP method and route them through the client request
|
|
161
|
+
pipeline (twilio-style generated clients, e.g. `twilio-node`).
|
|
152
162
|
(Still not covered: the *Python* SDK equivalents like `stripe.Refund.create(...)`
|
|
153
163
|
— see "Not covered" above.)
|
|
154
164
|
|
|
155
|
-

|
|
156
166
|
|
|
157
167
|
## GitHub Action
|
|
158
168
|
|
|
@@ -163,7 +173,7 @@ job summary. The check fails when an ungated finding meets your
|
|
|
163
173
|
|
|
164
174
|
```yaml
|
|
165
175
|
- uses: actions/checkout@v4
|
|
166
|
-
- uses:
|
|
176
|
+
- uses: withwrit/pywrit@v0
|
|
167
177
|
with:
|
|
168
178
|
fail-on-risk: high # high | medium | low | never
|
|
169
179
|
```
|
|
@@ -171,6 +181,46 @@ job summary. The check fails when an ungated finding meets your
|
|
|
171
181
|
No API key needed. Full reference: [docs/github-action.md](docs/github-action.md) ·
|
|
172
182
|
example workflow: [examples/github-action/writ-scan.yml](examples/github-action/writ-scan.yml)
|
|
173
183
|
|
|
184
|
+
## GitHub App
|
|
185
|
+
|
|
186
|
+
Install the [Writ GitHub App](https://docs.withwrit.com/git-hub-app) on selected
|
|
187
|
+
repositories: every push and pull request is scanned for ungated write-sites,
|
|
188
|
+
with results as a check run on the PR and in the
|
|
189
|
+
[portal](https://app.withwrit.com), where you manage policy, keys, and the audit log.
|
|
190
|
+
|
|
191
|
+
## MCP server (`writ-mcp`)
|
|
192
|
+
|
|
193
|
+
Give any MCP-compatible agent point-of-action policy checks. `writ-mcp` is a
|
|
194
|
+
[Model Context Protocol](https://modelcontextprotocol.io) server (stdio transport,
|
|
195
|
+
built on the MCP Python SDK) that exposes the Writ gate as **8 MCP tools**:
|
|
196
|
+
the agent calls `writ_check` before a consequential write and gets back `ALLOW`,
|
|
197
|
+
`DENY`, or `STEP_UP` — with a tamper-evident, hash-chained audit receipt for
|
|
198
|
+
every decision.
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
uvx writ-mcp # no install — runs on demand
|
|
202
|
+
# or
|
|
203
|
+
pip install writ-mcp # then run `writ-mcp`
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
| Tool | What it does |
|
|
207
|
+
|---|---|
|
|
208
|
+
| `writ_check` | The gate: `ALLOW` / `DENY` / `STEP_UP` for a proposed write |
|
|
209
|
+
| `writ_verify_token` | Validate an `ALLOW` auth token (catches purpose drift) |
|
|
210
|
+
| `writ_grant` | Human-sponsor approval for the `STEP_UP` path |
|
|
211
|
+
| `writ_revoke` / `writ_reinstate` | The kill switch |
|
|
212
|
+
| `writ_receipts` | Read the tenant's audit log |
|
|
213
|
+
| `writ_policy` | Manage the verb policy |
|
|
214
|
+
| `writ_sandbox` | Keyless 90-second demo grant — no API key needed |
|
|
215
|
+
|
|
216
|
+
The gate tools need a free Writ API key (`WRIT_API_KEY`): 10,000 receipts/month
|
|
217
|
+
free, no credit card. `writ_sandbox` works with no key at all.
|
|
218
|
+
|
|
219
|
+
The server implementation lives in [`mcp/`](mcp/) (MIT). Also published on the
|
|
220
|
+
official MCP Registry as `io.github.withwrit/writ`, on
|
|
221
|
+
[Smithery](https://smithery.ai/server/withwrit/writ-mcp), and listed on
|
|
222
|
+
[Glama](https://glama.ai/mcp/servers/withwrit/pywrit).
|
|
223
|
+
|
|
174
224
|
## Python client
|
|
175
225
|
|
|
176
226
|
```python
|
|
@@ -223,6 +273,9 @@ What's covered:
|
|
|
223
273
|
## CLI reference
|
|
224
274
|
|
|
225
275
|
```bash
|
|
276
|
+
writ wrap . # scan, diff, approve, apply, verify coverage
|
|
277
|
+
writ wrap . --dry-run # preview only
|
|
278
|
+
writ wrap . --yes # non-interactive CI mode
|
|
226
279
|
writ check --key writ_... --sponsor acme --agent agent-7 \
|
|
227
280
|
--verb db.write --target prod.customers --purpose "backfill region field"
|
|
228
281
|
writ policy --key writ_... --set payments.refund require_grant
|
|
@@ -241,7 +294,7 @@ Show that your agent's writes are gated by Writ. Two flavors:
|
|
|
241
294
|
**Static** (works today, no API key) — same design, no live count:
|
|
242
295
|
|
|
243
296
|
```md
|
|
244
|
-
[](https://withwrit.com)
|
|
245
298
|
```
|
|
246
299
|
|
|
247
300
|
**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
|
|
|
@@ -126,11 +132,15 @@ Python scanning never needs it.
|
|
|
126
132
|
TS/JS is **scan-only**: findings are listed with verbs for your policy file,
|
|
127
133
|
but `writ scan --apply` never rewrites TS/JS files — gate those by hand.
|
|
128
134
|
Covered patterns: `fetch`/`axios` writes, `fs` writes, SQL through knex-style
|
|
129
|
-
clients, Prisma writes,
|
|
135
|
+
clients, Prisma writes, JS SDK calls such as `stripe.refunds.create(...)`, and
|
|
136
|
+
generated REST resource clients: `create` / `update` / `patch` / `remove`
|
|
137
|
+
methods (plus their `...WithHttpInfo` variants) that build request options
|
|
138
|
+
with an explicit write HTTP method and route them through the client request
|
|
139
|
+
pipeline (twilio-style generated clients, e.g. `twilio-node`).
|
|
130
140
|
(Still not covered: the *Python* SDK equivalents like `stripe.Refund.create(...)`
|
|
131
141
|
— see "Not covered" above.)
|
|
132
142
|
|
|
133
|
-

|
|
134
144
|
|
|
135
145
|
## GitHub Action
|
|
136
146
|
|
|
@@ -141,7 +151,7 @@ job summary. The check fails when an ungated finding meets your
|
|
|
141
151
|
|
|
142
152
|
```yaml
|
|
143
153
|
- uses: actions/checkout@v4
|
|
144
|
-
- uses:
|
|
154
|
+
- uses: withwrit/pywrit@v0
|
|
145
155
|
with:
|
|
146
156
|
fail-on-risk: high # high | medium | low | never
|
|
147
157
|
```
|
|
@@ -149,6 +159,46 @@ job summary. The check fails when an ungated finding meets your
|
|
|
149
159
|
No API key needed. Full reference: [docs/github-action.md](docs/github-action.md) ·
|
|
150
160
|
example workflow: [examples/github-action/writ-scan.yml](examples/github-action/writ-scan.yml)
|
|
151
161
|
|
|
162
|
+
## GitHub App
|
|
163
|
+
|
|
164
|
+
Install the [Writ GitHub App](https://docs.withwrit.com/git-hub-app) on selected
|
|
165
|
+
repositories: every push and pull request is scanned for ungated write-sites,
|
|
166
|
+
with results as a check run on the PR and in the
|
|
167
|
+
[portal](https://app.withwrit.com), where you manage policy, keys, and the audit log.
|
|
168
|
+
|
|
169
|
+
## MCP server (`writ-mcp`)
|
|
170
|
+
|
|
171
|
+
Give any MCP-compatible agent point-of-action policy checks. `writ-mcp` is a
|
|
172
|
+
[Model Context Protocol](https://modelcontextprotocol.io) server (stdio transport,
|
|
173
|
+
built on the MCP Python SDK) that exposes the Writ gate as **8 MCP tools**:
|
|
174
|
+
the agent calls `writ_check` before a consequential write and gets back `ALLOW`,
|
|
175
|
+
`DENY`, or `STEP_UP` — with a tamper-evident, hash-chained audit receipt for
|
|
176
|
+
every decision.
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
uvx writ-mcp # no install — runs on demand
|
|
180
|
+
# or
|
|
181
|
+
pip install writ-mcp # then run `writ-mcp`
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
| Tool | What it does |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `writ_check` | The gate: `ALLOW` / `DENY` / `STEP_UP` for a proposed write |
|
|
187
|
+
| `writ_verify_token` | Validate an `ALLOW` auth token (catches purpose drift) |
|
|
188
|
+
| `writ_grant` | Human-sponsor approval for the `STEP_UP` path |
|
|
189
|
+
| `writ_revoke` / `writ_reinstate` | The kill switch |
|
|
190
|
+
| `writ_receipts` | Read the tenant's audit log |
|
|
191
|
+
| `writ_policy` | Manage the verb policy |
|
|
192
|
+
| `writ_sandbox` | Keyless 90-second demo grant — no API key needed |
|
|
193
|
+
|
|
194
|
+
The gate tools need a free Writ API key (`WRIT_API_KEY`): 10,000 receipts/month
|
|
195
|
+
free, no credit card. `writ_sandbox` works with no key at all.
|
|
196
|
+
|
|
197
|
+
The server implementation lives in [`mcp/`](mcp/) (MIT). Also published on the
|
|
198
|
+
official MCP Registry as `io.github.withwrit/writ`, on
|
|
199
|
+
[Smithery](https://smithery.ai/server/withwrit/writ-mcp), and listed on
|
|
200
|
+
[Glama](https://glama.ai/mcp/servers/withwrit/pywrit).
|
|
201
|
+
|
|
152
202
|
## Python client
|
|
153
203
|
|
|
154
204
|
```python
|
|
@@ -201,6 +251,9 @@ What's covered:
|
|
|
201
251
|
## CLI reference
|
|
202
252
|
|
|
203
253
|
```bash
|
|
254
|
+
writ wrap . # scan, diff, approve, apply, verify coverage
|
|
255
|
+
writ wrap . --dry-run # preview only
|
|
256
|
+
writ wrap . --yes # non-interactive CI mode
|
|
204
257
|
writ check --key writ_... --sponsor acme --agent agent-7 \
|
|
205
258
|
--verb db.write --target prod.customers --purpose "backfill region field"
|
|
206
259
|
writ policy --key writ_... --set payments.refund require_grant
|
|
@@ -219,7 +272,7 @@ Show that your agent's writes are gated by Writ. Two flavors:
|
|
|
219
272
|
**Static** (works today, no API key) — same design, no live count:
|
|
220
273
|
|
|
221
274
|
```md
|
|
222
|
-
[](https://withwrit.com)
|
|
223
276
|
```
|
|
224
277
|
|
|
225
278
|
**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.9"
|
|
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]
|