stata-code 0.6.3__tar.gz → 0.6.4__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.
- {stata_code-0.6.3 → stata_code-0.6.4}/CHANGELOG.md +45 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/PKG-INFO +60 -13
- {stata_code-0.6.3 → stata_code-0.6.4}/PUBLISHING.md +45 -22
- {stata_code-0.6.3 → stata_code-0.6.4}/README.md +59 -12
- {stata_code-0.6.3 → stata_code-0.6.4}/SCHEMA.md +1 -1
- {stata_code-0.6.3 → stata_code-0.6.4}/docs/design/hard_timeout.md +19 -26
- {stata_code-0.6.3 → stata_code-0.6.4}/pyproject.toml +1 -1
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/__init__.py +1 -1
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/core/_pool.py +72 -14
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/core/_runtime.py +105 -12
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/core/runner.py +48 -40
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/mcp/server.py +2 -2
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_pool.py +62 -0
- stata_code-0.6.4/tests/test_runtime_discovery.py +232 -0
- stata_code-0.6.3/tests/test_runtime_discovery.py +0 -81
- {stata_code-0.6.3 → stata_code-0.6.4}/.gitignore +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/LICENSE +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/LICENSE-POLICY.md +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/examples/01-basic-regression.md +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/examples/02-did-card-krueger.md +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/examples/03-graphs.md +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/examples/04-multi-session.md +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/examples/05-large-matrix.md +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/examples/README.md +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/schema/run_result.schema.json +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/scripts/check_versions.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/scripts/export_schema.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/core/__init__.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/core/_refs.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/core/errors.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/core/log_artifacts.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/core/notebook.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/core/run_index.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/core/schema.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/kernel/__init__.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/kernel/__main__.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/kernel/assets/logo-32x32.png +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/kernel/assets/logo-64x64.png +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/kernel/assets/logo-svg.svg +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/kernel/kernel.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/mcp/__init__.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/stata_code/mcp/__main__.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/__init__.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/conftest.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/fixtures/.gitkeep +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_cancel.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_errors.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_kernel.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_log_artifacts.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_mcp.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_mcp_stdio.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_notebook.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_notebook_phase2.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_public_api.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_run_index.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_runner.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_schema.py +0 -0
- {stata_code-0.6.3 → stata_code-0.6.4}/tests/test_schema_artifact.py +0 -0
|
@@ -6,6 +6,51 @@ to semver-major.minor for the result schema (see `SCHEMA.md` §6).
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 0.6.4 — 2026-05-21
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Claude Code plugin marketplace manifest.** `.claude-plugin/marketplace.json`
|
|
14
|
+
+ `.claude-plugin/plugin.json` expose the repo as a single-plugin
|
|
15
|
+
marketplace, so users can install everything (MCP server config + agent
|
|
16
|
+
skill) with `claude plugin marketplace add brycewang-stanford/stata-code`
|
|
17
|
+
followed by `claude plugin install stata-code`.
|
|
18
|
+
- **`stata-code` agent skill.** `skills/stata-code/SKILL.md` teaches Claude
|
|
19
|
+
the v1.0 RunResult schema, the 15 MCP tools, the token-economy defaults,
|
|
20
|
+
the 32-kind error taxonomy, and the diagnose-only vs. fix-and-rerun
|
|
21
|
+
workflows. The plugin manifest auto-installs it alongside the MCP
|
|
22
|
+
server.
|
|
23
|
+
- **VS Code install-hint probe.** On activation the extension now resolves
|
|
24
|
+
the configured `stata-code-mcp` candidate list against `PATH` and any
|
|
25
|
+
workspace `.venv` / `venv`; if nothing matches, a one-time notification
|
|
26
|
+
offers to copy the `pip install "stata-code[mcp]"` command or open the
|
|
27
|
+
install docs. "Don't show again" pins the dismissal to the installed
|
|
28
|
+
extension version. Backed by a new pure module `serverProbe.ts` with
|
|
29
|
+
unit tests.
|
|
30
|
+
- **README multi-client section.** Cursor, Claude Desktop, Cline,
|
|
31
|
+
Continue, Windsurf, and Antigravity now have their config-file paths
|
|
32
|
+
spelled out next to the shared `stata-code-mcp` JSON snippet, plus
|
|
33
|
+
guidance for project-venv absolute paths and `uvx` setups.
|
|
34
|
+
- **README cell + section reference.** Documents that the VS Code
|
|
35
|
+
extension recognizes both `* %%` Jupyter-style cell markers and
|
|
36
|
+
`**#` … `**######` six-level section headings in `.do` files, and how
|
|
37
|
+
each interacts with the code-lens and Outline view.
|
|
38
|
+
- **Open VSX publish step.** `vscode-release.yml` now publishes the VSIX
|
|
39
|
+
to Open VSX on every `vscode-v*` tag. The step is gated on
|
|
40
|
+
`OVSX_PAT` being set at runtime and runs with `continue-on-error: true`,
|
|
41
|
+
so a missing or expired token never blocks the primary VS Code
|
|
42
|
+
Marketplace publish.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- **Subprocess worker JSON protocol hardening.** Worker processes now keep
|
|
47
|
+
their private JSON protocol fds separate from real stdin/stdout and
|
|
48
|
+
redirect the real fd 0/1 pair to `os.devnull` before importing the
|
|
49
|
+
runner. The parent reader also ignores blank protocol noise while still
|
|
50
|
+
failing on non-empty non-JSON output. This prevents a pystata/Stata
|
|
51
|
+
initialization newline from surfacing as
|
|
52
|
+
`adapter_crash: worker emitted non-JSON: '\n'`.
|
|
53
|
+
|
|
9
54
|
## 0.6.3 — 2026-05-10
|
|
10
55
|
|
|
11
56
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stata-code
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
4
4
|
Summary: Agent-native Stata bridge — one core, multiple frontends (MCP, Jupyter, VSCode)
|
|
5
5
|
Project-URL: Homepage, https://github.com/brycewang-stanford/stata-code
|
|
6
6
|
Project-URL: Repository, https://github.com/brycewang-stanford/stata-code
|
|
@@ -84,7 +84,7 @@ Description-Content-Type: text/markdown
|
|
|
84
84
|
└─────────────┘ └────────────┘ └─────────────────┘
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
**Status: v0.6 (May 2026)** — the core, MCP server, Jupyter kernel, and VS Code extension work end-to-end against Stata 18 MP.
|
|
87
|
+
**Status: v0.6 (May 2026)** — the core, MCP server, Jupyter kernel, and VS Code extension work end-to-end against Stata 18 MP. The test suite covers schema, runner, MCP, kernel, notebook, run-index, subprocess-pool, and VS Code modules; CI also checks linting, type safety, schema generation, package metadata, and VSIX packaging. License: **MIT**.
|
|
88
88
|
|
|
89
89
|
Two workflows v0.6 explicitly supports for end users:
|
|
90
90
|
|
|
@@ -204,14 +204,34 @@ If you want the repair loop, say so explicitly. Otherwise, treat failed runs as
|
|
|
204
204
|
If you prefer not to `pip install stata-code` globally, run it ephemerally through [`uv`](https://github.com/astral-sh/uv):
|
|
205
205
|
|
|
206
206
|
```bash
|
|
207
|
-
claude mcp add stata-code --scope user -- uvx --from stata-code stata-code-mcp
|
|
207
|
+
claude mcp add stata-code --scope user -- uvx --from "stata-code[mcp]" stata-code-mcp
|
|
208
208
|
```
|
|
209
209
|
|
|
210
210
|
`uvx` will resolve and cache `stata-code` on first launch. Note: `pystata` is **not** on PyPI, so it still has to be locatable on the host. The runner adds the standard Stata install path (e.g. `/Applications/Stata/utilities/pystata` on macOS) to `sys.path` automatically; if your Stata lives elsewhere, set `PYTHONPATH` in the env block.
|
|
211
211
|
|
|
212
|
-
####
|
|
212
|
+
#### Claude Code via plugin marketplace
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
This repository also ships a Claude Code plugin manifest (`.claude-plugin/`). Once you've added the marketplace to your Claude Code config, two commands wire up both the MCP server and the agent skill that teaches Claude the v1.0 result schema:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
claude plugin marketplace add brycewang-stanford/stata-code
|
|
218
|
+
claude plugin install stata-code
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The plugin registers the `stata-code` MCP server and installs the [`stata-code` skill](skills/stata-code/SKILL.md) so Claude branches on `error.kind`, calls `get_log(ref)` lazily, and uses the notebook-edit tools without you re-explaining them every session.
|
|
222
|
+
|
|
223
|
+
#### Other MCP clients (Cursor / Claude Desktop / Cline / Continue / Windsurf / Antigravity)
|
|
224
|
+
|
|
225
|
+
Most non-Claude-Code MCP clients accept the same JSON snippet. Drop it into the client's MCP config file:
|
|
226
|
+
|
|
227
|
+
| Client | Config file |
|
|
228
|
+
| --- | --- |
|
|
229
|
+
| Claude Desktop | macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`; Windows: `%APPDATA%\Claude\claude_desktop_config.json` |
|
|
230
|
+
| Cursor | `~/.cursor/mcp.json` (user) or `<workspace>/.cursor/mcp.json` (project) |
|
|
231
|
+
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |
|
|
232
|
+
| Cline (VS Code) | settings: `cline.mcpServers` |
|
|
233
|
+
| Continue | `~/.continue/config.json` under `experimental.modelContextProtocolServers` |
|
|
234
|
+
| Antigravity / generic | `~/.claude/mcp.json` or whatever the client documents |
|
|
215
235
|
|
|
216
236
|
```json
|
|
217
237
|
{
|
|
@@ -223,12 +243,26 @@ For clients without a `mcp add` CLI, edit the config file directly (`~/.claude/m
|
|
|
223
243
|
}
|
|
224
244
|
```
|
|
225
245
|
|
|
226
|
-
Or
|
|
246
|
+
Or, when the binary is not on `PATH`, run it as a module:
|
|
227
247
|
|
|
228
248
|
```bash
|
|
229
249
|
python -m stata_code.mcp
|
|
230
250
|
```
|
|
231
251
|
|
|
252
|
+
When `stata-code-mcp` lives inside a project virtualenv (recommended for reproducibility), point the client at the absolute path:
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"mcpServers": {
|
|
257
|
+
"stata-code": {
|
|
258
|
+
"command": "/abs/path/to/.venv/bin/stata-code-mcp"
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
For `uvx`-only setups, set `"command": "uvx"` and `"args": ["--from", "stata-code", "stata-code-mcp"]`.
|
|
265
|
+
|
|
232
266
|
The MCP server registers 15 tools:
|
|
233
267
|
|
|
234
268
|
| Tool | Purpose |
|
|
@@ -239,7 +273,7 @@ The MCP server registers 15 tools:
|
|
|
239
273
|
| `get_graph` | Fetch graph bytes behind a `graph://` ref (`ImageContent`) |
|
|
240
274
|
| `get_matrix` | Fetch matrix payloads behind a `matrix://` ref |
|
|
241
275
|
| `list_sessions` | Enumerate live sessions |
|
|
242
|
-
| `cancel_session` |
|
|
276
|
+
| `cancel_session` | Cancel a session; the subprocess-backed path terminates in-flight runs and short-circuits pending ones |
|
|
243
277
|
| `reset_session` | Drop a session's data |
|
|
244
278
|
| `notebook_outline` | Compact per-cell index of a `.ipynb` (cell_id, type, preview) |
|
|
245
279
|
| `notebook_get_cell` | One cell's full source plus a token-economic outputs summary |
|
|
@@ -304,7 +338,20 @@ The companion extension is on the Marketplace as [`brycewang-stanford.stata-code
|
|
|
304
338
|
code --install-extension brycewang-stanford.stata-code-vscode
|
|
305
339
|
```
|
|
306
340
|
|
|
307
|
-
Or open the **Extensions** sidebar in VS Code and search `stata-code`.
|
|
341
|
+
Or open the **Extensions** sidebar in VS Code and search `stata-code`. The extension is also available from [Open VSX](https://open-vsx.org/) so Cursor, Windsurf, and other VS Code-compatible editors can install it without going through the Microsoft Marketplace.
|
|
342
|
+
|
|
343
|
+
On first activation the extension probes for `stata-code-mcp` on `PATH` (and in any workspace `.venv` / `venv`). If nothing resolves, it shows a one-time install hint with the exact `pip install "stata-code[mcp]"` command — choose **Don't show again** to silence it for the installed extension version.
|
|
344
|
+
|
|
345
|
+
#### Cell and section conventions
|
|
346
|
+
|
|
347
|
+
The extension recognizes two complementary structural markers inside `.do` files. Either can be mixed in the same file; they do not conflict.
|
|
348
|
+
|
|
349
|
+
| Marker | Purpose | Example |
|
|
350
|
+
| --- | --- | --- |
|
|
351
|
+
| `* %% [title]` | Cell boundary. Each marker gets a **▶ Run Cell** code-lens; "Run Cell" submits the lines between this marker and the next one. Compatible with the Jupyter-style cell convention used by `kylebutts/vscode-stata`. | `* %% 02 model fit` |
|
|
352
|
+
| `**# title` … `**###### title` | Section heading, 1–6 levels deep. Each heading gets a **▶ Run Section** code-lens and contributes to the Outline view. "Run Section" submits the heading through the next equal- or higher-level heading, matching the hierarchical execution model from `ZihaoVistonWang.stata-all-in-one`. | `**## DiD specification` |
|
|
353
|
+
|
|
354
|
+
`program define … end` blocks are also surfaced in the Outline, nested under whichever section contains them.
|
|
308
355
|
|
|
309
356
|
The extension still requires the MCP extra on your system Python (`pip install "stata-code[mcp]"`), so that `stata-code-mcp` resolves on `PATH` and can import the MCP SDK. Stata 17+ and a valid Stata license are required as for any other frontend.
|
|
310
357
|
|
|
@@ -397,7 +444,7 @@ stata_code/
|
|
|
397
444
|
- MCP server: 15 tools, including notebook navigation / search / atomic edits and the run-bundle index (`list_runs`)
|
|
398
445
|
- Jupyter kernel: rewired to the v1.0 pipeline, kernel logos bundled
|
|
399
446
|
- Matrix size cap + `get_matrix(ref)` for large matrices (>10k cells)
|
|
400
|
-
-
|
|
447
|
+
- Subprocess-backed hard timeout and cancellation for the public Python API and MCP server: `timeout_ms`, `cancel(session_id)`, and MCP `cancel_session`
|
|
401
448
|
- Per-cell repair loop on `.ipynb` via `notebook_outline` / `notebook_get_cell` / `notebook_edit_cell` with optimistic-concurrency `expected_source` guards and `origin_cell_id` echo on `RunResult`
|
|
402
449
|
- Persistent run bundles + `list_runs` query over `manifest.json` files (filter by cell / origin / session / since / ok)
|
|
403
450
|
- JSON Schema artifact auto-generated from `schema.py`: [`schema/run_result.schema.json`](schema/run_result.schema.json)
|
|
@@ -407,8 +454,8 @@ stata_code/
|
|
|
407
454
|
### Next Up
|
|
408
455
|
|
|
409
456
|
- Console fallback for Stata 11–16, re-implemented against the v1.0 schema
|
|
410
|
-
-
|
|
411
|
-
- Extra VS Code polish
|
|
457
|
+
- Decide whether to move the Jupyter kernel from the direct in-process runner to the subprocess pool, or keep documenting the current interactivity-first tradeoff
|
|
458
|
+
- Extra VS Code polish: extension-host end-to-end tests, first-run diagnostics, and command palette UX
|
|
412
459
|
- **v1.0** — Stable schema, broader Stata edition coverage
|
|
413
460
|
|
|
414
461
|
See [SCHEMA.md §7](SCHEMA.md) for explicitly out-of-scope items.
|
|
@@ -419,9 +466,9 @@ See [SCHEMA.md §7](SCHEMA.md) for explicitly out-of-scope items.
|
|
|
419
466
|
|
|
420
467
|
```bash
|
|
421
468
|
pip install -e ".[dev,mcp,kernel]"
|
|
422
|
-
pytest # full suite
|
|
469
|
+
pytest # full suite, including Stata tests when Stata is available
|
|
423
470
|
pytest -m "not stata_required" # CI subset; no Stata needed
|
|
424
|
-
pytest -m "stata_required" -v # Stata
|
|
471
|
+
pytest -m "stata_required" -v # real-Stata integration tests only
|
|
425
472
|
```
|
|
426
473
|
|
|
427
474
|
The `stata_required` marker tags the real-Stata integration tests. CI uses `pytest -m "not stata_required"` so it does not collect them. Locally without Stata, those tests skip cleanly with the `"pystata / Stata 17+ not available"` message.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
# Publishing `stata-code` to PyPI
|
|
1
|
+
# Publishing `stata-code` to TestPyPI and PyPI
|
|
2
2
|
|
|
3
|
-
This project publishes to PyPI via **GitHub Actions Trusted
|
|
4
|
-
There are **no API tokens** stored in GitHub repository
|
|
5
|
-
the OIDC identity of the workflow run
|
|
3
|
+
This project publishes to TestPyPI and PyPI via **GitHub Actions Trusted
|
|
4
|
+
Publishing** (OIDC). There are **no API tokens** stored in GitHub repository
|
|
5
|
+
secrets — each package index verifies the OIDC identity of the workflow run
|
|
6
|
+
instead.
|
|
6
7
|
|
|
7
8
|
The release pipeline is in [`.github/workflows/release.yml`](.github/workflows/release.yml).
|
|
8
9
|
|
|
@@ -28,25 +29,34 @@ Trusted Publishing has two modes:
|
|
|
28
29
|
|
|
29
30
|
Either path lands you in the same place after the first run.
|
|
30
31
|
|
|
31
|
-
### 2. Configure the Trusted
|
|
32
|
+
### 2. Configure the Trusted Publishers
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
PyPI and TestPyPI are separate package indexes, so configure both publisher
|
|
35
|
+
records independently:
|
|
34
36
|
|
|
35
|
-
|
|
|
36
|
-
| --- | --- |
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
| Site | Manage URL | Environment |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| PyPI | <https://pypi.org/manage/project/stata-code/settings/publishing/> | `pypi` |
|
|
40
|
+
| TestPyPI | <https://test.pypi.org/manage/project/stata-code/settings/publishing/> | `testpypi` |
|
|
41
|
+
|
|
42
|
+
Use the same GitHub publisher values on both sites:
|
|
43
|
+
|
|
44
|
+
- Owner: `brycewang-stanford`
|
|
45
|
+
- Repository name: `stata-code`
|
|
46
|
+
- Workflow filename: `release.yml`
|
|
47
|
+
- Environment name: `pypi` or `testpypi`, matching the site above
|
|
41
48
|
|
|
42
49
|
The environment name is **required** and must match the `environment.name`
|
|
43
|
-
declared in `release.yml
|
|
50
|
+
declared in `release.yml`. PyPI / TestPyPI use it as an extra constraint:
|
|
44
51
|
even a malicious workflow change in the repo cannot publish unless it runs
|
|
45
|
-
under
|
|
52
|
+
under the exact environment configured for that index.
|
|
53
|
+
|
|
54
|
+
### 3. Create the GitHub Environments
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
In the GitHub repo: **Settings → Environments → New environment**. Create both:
|
|
48
57
|
|
|
49
|
-
|
|
58
|
+
- `testpypi`
|
|
59
|
+
- `pypi`
|
|
50
60
|
|
|
51
61
|
Recommended hardening (all in the environment settings page):
|
|
52
62
|
|
|
@@ -66,25 +76,38 @@ The environment doesn't need any secrets — Trusted Publishing handles auth.
|
|
|
66
76
|
|
|
67
77
|
Once the one-time setup above is done, every release is just:
|
|
68
78
|
|
|
69
|
-
1. **Bump the version
|
|
70
|
-
|
|
79
|
+
1. **Bump the version everywhere**:
|
|
80
|
+
- `pyproject.toml` → `[project] version`
|
|
81
|
+
- `stata_code/__init__.py` → `__version__`
|
|
82
|
+
- `stata_code/mcp/server.py` → `__version__`
|
|
83
|
+
- `vscode/package.json` → `version`
|
|
84
|
+
- `vscode/package-lock.json` → root package `version`
|
|
85
|
+
2. **Run the version guard** before tagging:
|
|
86
|
+
```bash
|
|
87
|
+
python scripts/check_versions.py
|
|
88
|
+
```
|
|
89
|
+
3. **Update `CHANGELOG.md`**: move the `[Unreleased]` entries under a new
|
|
71
90
|
`## [X.Y.Z] - YYYY-MM-DD` heading, leaving an empty `[Unreleased]` shell
|
|
72
91
|
on top.
|
|
73
|
-
|
|
92
|
+
4. **Commit** the bump:
|
|
74
93
|
```bash
|
|
75
|
-
git add pyproject.toml CHANGELOG.md
|
|
94
|
+
git add pyproject.toml stata_code/__init__.py stata_code/mcp/server.py vscode/package.json vscode/package-lock.json CHANGELOG.md
|
|
76
95
|
git commit -m "release: vX.Y.Z"
|
|
77
96
|
```
|
|
78
|
-
|
|
97
|
+
5. **Tag and push**:
|
|
79
98
|
```bash
|
|
80
99
|
git tag vX.Y.Z
|
|
81
100
|
git push origin main
|
|
82
101
|
git push origin vX.Y.Z
|
|
83
102
|
```
|
|
84
|
-
|
|
103
|
+
6. Watch the **`release` workflow** under the Actions tab. It will:
|
|
85
104
|
- build the sdist + wheel
|
|
86
105
|
- run `twine check` on the artifacts
|
|
106
|
+
- publish to TestPyPI under the `testpypi` environment (no token needed)
|
|
87
107
|
- publish to PyPI under the `pypi` environment (no token needed)
|
|
108
|
+
- poll PyPI's per-version JSON endpoint so a failed trusted-publisher
|
|
109
|
+
publish marks the workflow run failed even though the publish job itself
|
|
110
|
+
is `continue-on-error`
|
|
88
111
|
- create a GitHub Release for the tag with the wheel + sdist attached and
|
|
89
112
|
auto-generated release notes
|
|
90
113
|
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
└─────────────┘ └────────────┘ └─────────────────┘
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
**Status: v0.6 (May 2026)** — the core, MCP server, Jupyter kernel, and VS Code extension work end-to-end against Stata 18 MP.
|
|
48
|
+
**Status: v0.6 (May 2026)** — the core, MCP server, Jupyter kernel, and VS Code extension work end-to-end against Stata 18 MP. The test suite covers schema, runner, MCP, kernel, notebook, run-index, subprocess-pool, and VS Code modules; CI also checks linting, type safety, schema generation, package metadata, and VSIX packaging. License: **MIT**.
|
|
49
49
|
|
|
50
50
|
Two workflows v0.6 explicitly supports for end users:
|
|
51
51
|
|
|
@@ -165,14 +165,34 @@ If you want the repair loop, say so explicitly. Otherwise, treat failed runs as
|
|
|
165
165
|
If you prefer not to `pip install stata-code` globally, run it ephemerally through [`uv`](https://github.com/astral-sh/uv):
|
|
166
166
|
|
|
167
167
|
```bash
|
|
168
|
-
claude mcp add stata-code --scope user -- uvx --from stata-code stata-code-mcp
|
|
168
|
+
claude mcp add stata-code --scope user -- uvx --from "stata-code[mcp]" stata-code-mcp
|
|
169
169
|
```
|
|
170
170
|
|
|
171
171
|
`uvx` will resolve and cache `stata-code` on first launch. Note: `pystata` is **not** on PyPI, so it still has to be locatable on the host. The runner adds the standard Stata install path (e.g. `/Applications/Stata/utilities/pystata` on macOS) to `sys.path` automatically; if your Stata lives elsewhere, set `PYTHONPATH` in the env block.
|
|
172
172
|
|
|
173
|
-
####
|
|
173
|
+
#### Claude Code via plugin marketplace
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
This repository also ships a Claude Code plugin manifest (`.claude-plugin/`). Once you've added the marketplace to your Claude Code config, two commands wire up both the MCP server and the agent skill that teaches Claude the v1.0 result schema:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
claude plugin marketplace add brycewang-stanford/stata-code
|
|
179
|
+
claude plugin install stata-code
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The plugin registers the `stata-code` MCP server and installs the [`stata-code` skill](skills/stata-code/SKILL.md) so Claude branches on `error.kind`, calls `get_log(ref)` lazily, and uses the notebook-edit tools without you re-explaining them every session.
|
|
183
|
+
|
|
184
|
+
#### Other MCP clients (Cursor / Claude Desktop / Cline / Continue / Windsurf / Antigravity)
|
|
185
|
+
|
|
186
|
+
Most non-Claude-Code MCP clients accept the same JSON snippet. Drop it into the client's MCP config file:
|
|
187
|
+
|
|
188
|
+
| Client | Config file |
|
|
189
|
+
| --- | --- |
|
|
190
|
+
| Claude Desktop | macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`; Windows: `%APPDATA%\Claude\claude_desktop_config.json` |
|
|
191
|
+
| Cursor | `~/.cursor/mcp.json` (user) or `<workspace>/.cursor/mcp.json` (project) |
|
|
192
|
+
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |
|
|
193
|
+
| Cline (VS Code) | settings: `cline.mcpServers` |
|
|
194
|
+
| Continue | `~/.continue/config.json` under `experimental.modelContextProtocolServers` |
|
|
195
|
+
| Antigravity / generic | `~/.claude/mcp.json` or whatever the client documents |
|
|
176
196
|
|
|
177
197
|
```json
|
|
178
198
|
{
|
|
@@ -184,12 +204,26 @@ For clients without a `mcp add` CLI, edit the config file directly (`~/.claude/m
|
|
|
184
204
|
}
|
|
185
205
|
```
|
|
186
206
|
|
|
187
|
-
Or
|
|
207
|
+
Or, when the binary is not on `PATH`, run it as a module:
|
|
188
208
|
|
|
189
209
|
```bash
|
|
190
210
|
python -m stata_code.mcp
|
|
191
211
|
```
|
|
192
212
|
|
|
213
|
+
When `stata-code-mcp` lives inside a project virtualenv (recommended for reproducibility), point the client at the absolute path:
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"mcpServers": {
|
|
218
|
+
"stata-code": {
|
|
219
|
+
"command": "/abs/path/to/.venv/bin/stata-code-mcp"
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
For `uvx`-only setups, set `"command": "uvx"` and `"args": ["--from", "stata-code", "stata-code-mcp"]`.
|
|
226
|
+
|
|
193
227
|
The MCP server registers 15 tools:
|
|
194
228
|
|
|
195
229
|
| Tool | Purpose |
|
|
@@ -200,7 +234,7 @@ The MCP server registers 15 tools:
|
|
|
200
234
|
| `get_graph` | Fetch graph bytes behind a `graph://` ref (`ImageContent`) |
|
|
201
235
|
| `get_matrix` | Fetch matrix payloads behind a `matrix://` ref |
|
|
202
236
|
| `list_sessions` | Enumerate live sessions |
|
|
203
|
-
| `cancel_session` |
|
|
237
|
+
| `cancel_session` | Cancel a session; the subprocess-backed path terminates in-flight runs and short-circuits pending ones |
|
|
204
238
|
| `reset_session` | Drop a session's data |
|
|
205
239
|
| `notebook_outline` | Compact per-cell index of a `.ipynb` (cell_id, type, preview) |
|
|
206
240
|
| `notebook_get_cell` | One cell's full source plus a token-economic outputs summary |
|
|
@@ -265,7 +299,20 @@ The companion extension is on the Marketplace as [`brycewang-stanford.stata-code
|
|
|
265
299
|
code --install-extension brycewang-stanford.stata-code-vscode
|
|
266
300
|
```
|
|
267
301
|
|
|
268
|
-
Or open the **Extensions** sidebar in VS Code and search `stata-code`.
|
|
302
|
+
Or open the **Extensions** sidebar in VS Code and search `stata-code`. The extension is also available from [Open VSX](https://open-vsx.org/) so Cursor, Windsurf, and other VS Code-compatible editors can install it without going through the Microsoft Marketplace.
|
|
303
|
+
|
|
304
|
+
On first activation the extension probes for `stata-code-mcp` on `PATH` (and in any workspace `.venv` / `venv`). If nothing resolves, it shows a one-time install hint with the exact `pip install "stata-code[mcp]"` command — choose **Don't show again** to silence it for the installed extension version.
|
|
305
|
+
|
|
306
|
+
#### Cell and section conventions
|
|
307
|
+
|
|
308
|
+
The extension recognizes two complementary structural markers inside `.do` files. Either can be mixed in the same file; they do not conflict.
|
|
309
|
+
|
|
310
|
+
| Marker | Purpose | Example |
|
|
311
|
+
| --- | --- | --- |
|
|
312
|
+
| `* %% [title]` | Cell boundary. Each marker gets a **▶ Run Cell** code-lens; "Run Cell" submits the lines between this marker and the next one. Compatible with the Jupyter-style cell convention used by `kylebutts/vscode-stata`. | `* %% 02 model fit` |
|
|
313
|
+
| `**# title` … `**###### title` | Section heading, 1–6 levels deep. Each heading gets a **▶ Run Section** code-lens and contributes to the Outline view. "Run Section" submits the heading through the next equal- or higher-level heading, matching the hierarchical execution model from `ZihaoVistonWang.stata-all-in-one`. | `**## DiD specification` |
|
|
314
|
+
|
|
315
|
+
`program define … end` blocks are also surfaced in the Outline, nested under whichever section contains them.
|
|
269
316
|
|
|
270
317
|
The extension still requires the MCP extra on your system Python (`pip install "stata-code[mcp]"`), so that `stata-code-mcp` resolves on `PATH` and can import the MCP SDK. Stata 17+ and a valid Stata license are required as for any other frontend.
|
|
271
318
|
|
|
@@ -358,7 +405,7 @@ stata_code/
|
|
|
358
405
|
- MCP server: 15 tools, including notebook navigation / search / atomic edits and the run-bundle index (`list_runs`)
|
|
359
406
|
- Jupyter kernel: rewired to the v1.0 pipeline, kernel logos bundled
|
|
360
407
|
- Matrix size cap + `get_matrix(ref)` for large matrices (>10k cells)
|
|
361
|
-
-
|
|
408
|
+
- Subprocess-backed hard timeout and cancellation for the public Python API and MCP server: `timeout_ms`, `cancel(session_id)`, and MCP `cancel_session`
|
|
362
409
|
- Per-cell repair loop on `.ipynb` via `notebook_outline` / `notebook_get_cell` / `notebook_edit_cell` with optimistic-concurrency `expected_source` guards and `origin_cell_id` echo on `RunResult`
|
|
363
410
|
- Persistent run bundles + `list_runs` query over `manifest.json` files (filter by cell / origin / session / since / ok)
|
|
364
411
|
- JSON Schema artifact auto-generated from `schema.py`: [`schema/run_result.schema.json`](schema/run_result.schema.json)
|
|
@@ -368,8 +415,8 @@ stata_code/
|
|
|
368
415
|
### Next Up
|
|
369
416
|
|
|
370
417
|
- Console fallback for Stata 11–16, re-implemented against the v1.0 schema
|
|
371
|
-
-
|
|
372
|
-
- Extra VS Code polish
|
|
418
|
+
- Decide whether to move the Jupyter kernel from the direct in-process runner to the subprocess pool, or keep documenting the current interactivity-first tradeoff
|
|
419
|
+
- Extra VS Code polish: extension-host end-to-end tests, first-run diagnostics, and command palette UX
|
|
373
420
|
- **v1.0** — Stable schema, broader Stata edition coverage
|
|
374
421
|
|
|
375
422
|
See [SCHEMA.md §7](SCHEMA.md) for explicitly out-of-scope items.
|
|
@@ -380,9 +427,9 @@ See [SCHEMA.md §7](SCHEMA.md) for explicitly out-of-scope items.
|
|
|
380
427
|
|
|
381
428
|
```bash
|
|
382
429
|
pip install -e ".[dev,mcp,kernel]"
|
|
383
|
-
pytest # full suite
|
|
430
|
+
pytest # full suite, including Stata tests when Stata is available
|
|
384
431
|
pytest -m "not stata_required" # CI subset; no Stata needed
|
|
385
|
-
pytest -m "stata_required" -v # Stata
|
|
432
|
+
pytest -m "stata_required" -v # real-Stata integration tests only
|
|
386
433
|
```
|
|
387
434
|
|
|
388
435
|
The `stata_required` marker tags the real-Stata integration tests. CI uses `pytest -m "not stata_required"` so it does not collect them. Locally without Stata, those tests skip cleanly with the `"pystata / Stata 17+ not available"` message.
|
|
@@ -430,7 +430,7 @@ Suggestions are best-effort; agents should treat them as hints, not directives.
|
|
|
430
430
|
| `stata_limit` | 901, 902, 903 | Edition / matsize / similar Stata-imposed caps. Distinct from OS OOM. Suggestion: `set maxvar` or upgrade edition. |
|
|
431
431
|
| `out_of_memory` | 480, 909 | OS-level memory exhaustion. |
|
|
432
432
|
| `interrupt` | 1 | User Break / Ctrl-C from a frontend. |
|
|
433
|
-
| `cancelled` | (synthetic `rc: -3`) |
|
|
433
|
+
| `cancelled` | (synthetic `rc: -3`) | Cancellation was requested. Subprocess-backed producers may terminate an in-flight worker; the direct in-process runner only short-circuits before Stata receives code. |
|
|
434
434
|
| `timeout` | (synthetic `rc: -2`) | Adapter-imposed time limit exceeded. |
|
|
435
435
|
| `adapter_crash` | (synthetic `rc: -1`) | Producer-side failure (pystata exception, IPC death). |
|
|
436
436
|
| `unknown` | any unmapped rc | Catch-all. Agents fall back to `message`. We aim to shrink this over time. |
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# Design note: hard timeout / mid-Stata interrupt
|
|
2
2
|
|
|
3
|
-
> Status: **
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
>
|
|
3
|
+
> Status: **partially shipped**. The public Python API and MCP server now use
|
|
4
|
+
> the subprocess-worker architecture described below (`stata_code.core._pool`),
|
|
5
|
+
> so `timeout_ms` and in-flight `cancel_session` terminate the worker and return
|
|
6
|
+
> structured `timeout` / `cancelled` results. The remaining gap is the direct
|
|
7
|
+
> in-process runner (`stata_code.core.runner.execute`) and the Jupyter kernel,
|
|
8
|
+
> which still use `pystata` in-process for interactivity. This note is retained
|
|
9
|
+
> as the rationale for that boundary.
|
|
8
10
|
|
|
9
11
|
## The constraint
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
The direct runner runs Stata via **pystata, in-process**. It imports
|
|
12
14
|
`pystata.config`, calls `pystata.config.init(edition)`, then submits
|
|
13
15
|
code through `stata.run(code)`. That call is a synchronous, blocking C
|
|
14
16
|
call into Stata's runtime. From Python's perspective, the interpreter
|
|
@@ -26,7 +28,7 @@ There is **no public pystata API** to:
|
|
|
26
28
|
is process-wide; a second `init()` is rejected. Frames give us
|
|
27
29
|
data isolation, not control isolation.
|
|
28
30
|
|
|
29
|
-
So `timeout_ms` cannot be enforced in the
|
|
31
|
+
So `timeout_ms` cannot be enforced in the direct in-process runner.
|
|
30
32
|
`cancel(session_id)` (cooperative) catches the case where the agent
|
|
31
33
|
hasn't yet submitted the next command, but if the agent already issued
|
|
32
34
|
`bootstrap, reps(10000): regress …` and is mid-flight, nothing on the
|
|
@@ -43,7 +45,7 @@ and a synthetic `rc: -2` for exactly this case.
|
|
|
43
45
|
|
|
44
46
|
## Options considered
|
|
45
47
|
|
|
46
|
-
### Option A — Subprocess pystata worker (
|
|
48
|
+
### Option A — Subprocess pystata worker (shipped for public API / MCP)
|
|
47
49
|
|
|
48
50
|
Move pystata out of the main Python process. One persistent worker
|
|
49
51
|
subprocess per session, with a small JSON-over-stdio protocol:
|
|
@@ -140,22 +142,13 @@ When Option A is approved, the rough work breakdown is:
|
|
|
140
142
|
|
|
141
143
|
Total: ~8–10 days of focused work.
|
|
142
144
|
|
|
143
|
-
## Why not
|
|
145
|
+
## Why not in the direct runner / kernel yet
|
|
144
146
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
In the meantime the ground we did cover is real:
|
|
154
|
-
|
|
155
|
-
- Cooperative cancellation (`cancel(session_id)`) — in this push.
|
|
156
|
-
- Token economy preserved end-to-end (refs, not bytes) — in this push.
|
|
157
|
-
- VSCode webview that exercises the same MCP surface a hard-timeout
|
|
158
|
-
agent would — in this push.
|
|
159
|
-
|
|
160
|
-
The hardest piece is still ahead. This document exists so the next
|
|
161
|
-
person picking it up doesn't have to rediscover the constraints.
|
|
147
|
+
The subprocess pool is now the default for the package-level API and MCP
|
|
148
|
+
server. The lower-level `core.runner.execute()` remains available as an
|
|
149
|
+
explicit in-process escape hatch, and the Jupyter kernel still calls it so
|
|
150
|
+
notebook cells can use inline logs and graphs without paying the worker
|
|
151
|
+
round-trip or changing notebook semantics. Moving the kernel onto the pool is
|
|
152
|
+
possible, but should be treated as a separate UX decision: hard interruption
|
|
153
|
+
would improve resilience, while worker death would discard in-memory Stata
|
|
154
|
+
state after timeouts or cancels.
|