stata-code 0.9.1__tar.gz → 0.11.0__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.9.1 → stata_code-0.11.0}/CHANGELOG.md +186 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/PKG-INFO +126 -28
- stata_code-0.9.1/README.md → stata_code-0.11.0/README.en.md +125 -27
- stata_code-0.11.0/README.md +567 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/SCHEMA.md +118 -33
- {stata_code-0.9.1 → stata_code-0.11.0}/docs/competitive-landscape.md +58 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/docs/industry-leader-roadmap.md +1 -1
- stata_code-0.11.0/docs/quickstart.zh.md +102 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/pyproject.toml +5 -2
- {stata_code-0.9.1 → stata_code-0.11.0}/schema/run_result.schema.json +88 -0
- stata_code-0.11.0/scripts/build_standalone.py +81 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/__init__.py +77 -1
- stata_code-0.11.0/stata_code/cli.py +538 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/_pool.py +109 -9
- stata_code-0.11.0/stata_code/core/console.py +818 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/errors.py +82 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/estimation.py +132 -36
- stata_code-0.11.0/stata_code/core/jobs.py +200 -0
- stata_code-0.11.0/stata_code/core/lint.py +246 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/log_artifacts.py +59 -1
- stata_code-0.11.0/stata_code/core/policy.py +375 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/runner.py +651 -93
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/schema.py +61 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/doctor.py +22 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/kernel/kernel.py +6 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/mcp/server.py +468 -16
- stata_code-0.11.0/stata_code/mcp_setup.py +174 -0
- stata_code-0.11.0/tests/test_agent_ergonomics.py +867 -0
- stata_code-0.11.0/tests/test_benchmark.py +59 -0
- stata_code-0.11.0/tests/test_cli.py +213 -0
- stata_code-0.11.0/tests/test_console.py +304 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_errors.py +5 -1
- stata_code-0.11.0/tests/test_lint.py +89 -0
- stata_code-0.11.0/tests/test_lint_mcp.py +67 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_mcp.py +82 -134
- stata_code-0.11.0/tests/test_mcp_transport.py +51 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_new_tools.py +8 -6
- stata_code-0.11.0/tests/test_policy.py +156 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_runner.py +141 -5
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_runner_helpers.py +109 -17
- stata_code-0.9.1/stata_code/cli.py +0 -85
- stata_code-0.9.1/tests/test_mcp_stdio.py +0 -163
- {stata_code-0.9.1 → stata_code-0.11.0}/.gitignore +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/LICENSE +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/LICENSE-POLICY.md +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/PUBLISHING.md +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/docs/design/hard_timeout.md +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/examples/01-basic-regression.md +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/examples/02-did-card-krueger.md +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/examples/03-graphs.md +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/examples/04-multi-session.md +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/examples/05-large-matrix.md +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/examples/06-cross-stack-parity-audit.md +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/examples/07-data-mcp-handoff.md +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/examples/README.md +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/scripts/build_skill_zip.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/scripts/check_github_actions.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/scripts/check_versions.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/scripts/export_schema.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/__main__.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/__init__.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/_refs.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/_runtime.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/handoff.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/notebook.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/provenance.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/core/run_index.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/kernel/__init__.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/kernel/__main__.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/kernel/assets/logo-32x32.png +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/kernel/assets/logo-64x64.png +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/kernel/assets/logo-svg.svg +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/mcp/__init__.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/stata_code/mcp/__main__.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/__init__.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/conftest.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/fixtures/.gitkeep +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_bugfix_regressions.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_cancel.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_doctor.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_estimation.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_github_actions.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_handoff.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_kernel.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_log_artifacts.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_mcp_kernel_extra.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_method_prompts.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_notebook.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_notebook_phase2.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_pool.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_pool_refs_extra.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_provenance.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_public_api.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_real_stata.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_release_versions.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_run_index.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_runtime_discovery.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_schema.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_schema_artifact.py +0 -0
- {stata_code-0.9.1 → stata_code-0.11.0}/tests/test_skill_package.py +0 -0
|
@@ -6,6 +6,192 @@ to semver-major.minor for the result schema (see `SCHEMA.md` §6).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## 0.11.0 — 2026-07-28
|
|
10
|
+
|
|
11
|
+
An agent-ergonomics release, driven by a report from an agent that used the MCP
|
|
12
|
+
server to write a full empirical paper. Each item below is a cost that agent
|
|
13
|
+
actually paid.
|
|
14
|
+
|
|
15
|
+
**Minor, not patch:** `include_results` defaults to `"scalars"`, so `r()` / `e()`
|
|
16
|
+
matrices now arrive as `matrix://` stubs instead of inline values. Pass
|
|
17
|
+
`include_results="full"` for the previous shape. `results.estimation` is
|
|
18
|
+
unaffected — and is now *more* accurate, since two silent numeric bugs in it are
|
|
19
|
+
fixed. The Jupyter kernel and the `stata-code run` CLI keep `"full"`.
|
|
20
|
+
|
|
21
|
+
### Agent-ergonomics pass
|
|
22
|
+
|
|
23
|
+
#### Result payloads are now bounded (and correct)
|
|
24
|
+
|
|
25
|
+
- **`include_results`** (`"none" | "scalars" | "full"`, default **`"scalars"`**).
|
|
26
|
+
A single estimation used to encode the same numbers four times over — `e(b)`,
|
|
27
|
+
`e(V)`'s row/column labels, `e(beta)`, and `r(table)` — on top of
|
|
28
|
+
`results.estimation`. Under the new default, scalars and macros stay inline
|
|
29
|
+
and every matrix becomes a `matrix://` stub carrying only its shape
|
|
30
|
+
(`n_rows` / `n_cols`); values and labels remain retrievable with
|
|
31
|
+
`get_matrix(ref)`. A 123-term regression's envelope drops from ~57 KB to
|
|
32
|
+
~28 KB. **This is a behaviour change** — pass `include_results="full"` for
|
|
33
|
+
the previous shape. The Jupyter kernel keeps `"full"`, since a notebook has
|
|
34
|
+
no token budget to defend.
|
|
35
|
+
- **`include_estimation`** (`"none" | "summary" | "full"`, default `"full"`)
|
|
36
|
+
and **`max_coefficients`**. `"summary"` keeps the model-level block and
|
|
37
|
+
diagnostics but drops per-term rows — the right setting for specifications
|
|
38
|
+
dominated by `i.year i.firm` nuisance terms. `estimation.n_coefficients`
|
|
39
|
+
always reports the model's true term count and
|
|
40
|
+
`estimation.coefficients_truncated` flags any cut, so trimmed output can
|
|
41
|
+
never be mistaken for a smaller model.
|
|
42
|
+
|
|
43
|
+
#### Fixed: two silent numeric-correctness bugs
|
|
44
|
+
|
|
45
|
+
- **Standard errors were blanked whenever `e(V)` was returned by reference.**
|
|
46
|
+
Any model whose VCV exceeded the inline cell cap (roughly >100 terms) came
|
|
47
|
+
back with `se`, `statistic`, `p_value` and both CI bounds `null` for *every*
|
|
48
|
+
coefficient, while still reporting `source: "e_b_v"` as though inference had
|
|
49
|
+
been computed. The estimation builder now resolves `matrix://` refs, so the
|
|
50
|
+
wire representation no longer degrades the numbers. `e(b)` by reference is
|
|
51
|
+
resolved too, where it previously suppressed the estimation block entirely.
|
|
52
|
+
- **`r()` did not survive to the next call.** The runner's own probes are all
|
|
53
|
+
r-class (`graph dir` before a run, `graph export` after), so the canonical
|
|
54
|
+
two-call pattern — `summarize price`, then `display r(mean)` — read back a
|
|
55
|
+
missing value. All internal housekeeping now runs inside a Stata
|
|
56
|
+
`_return hold` / `_return restore` pair. Pre-existing; the new `log query`
|
|
57
|
+
probe would have widened it to runs with `include_graphs="none"`.
|
|
58
|
+
- **Stata's missing values leaked as `8.988e+307`.** `sfi` returns system
|
|
59
|
+
missing (`.`) and extended missings (`.a`–`.z`) as ordinary doubles at or
|
|
60
|
+
above `2^1023`. These reached the wire unconverted in `r()`/`e()` scalars and
|
|
61
|
+
in every matrix cell — an omitted base level's standard error arrived as a
|
|
62
|
+
number an agent would format into a table. All numerics now normalize to
|
|
63
|
+
`null`, matching what SCHEMA.md already required of scalars.
|
|
64
|
+
|
|
65
|
+
#### Errors inside `do`-files are now located
|
|
66
|
+
|
|
67
|
+
- `error.line` and `error.context` are resolved **inside the invoked script**.
|
|
68
|
+
A failing `do "analysis.do"` previously produced `line: null`, empty
|
|
69
|
+
before/after context, and `message: "end of do-file"` — Stata's epilogue
|
|
70
|
+
rather than the diagnosis. The transcript parser now skips structural
|
|
71
|
+
boilerplate (`end of do-file`, `--Break--`), folds `///` continuation
|
|
72
|
+
fragments back into one logical command, and resolves the failing command
|
|
73
|
+
against the script on disk. New `error.source_file` names the file that
|
|
74
|
+
`error.line` indexes into.
|
|
75
|
+
- **A failed run now produces a log.** pystata raises the whole transcript as
|
|
76
|
+
its exception message and leaves stdout empty, so failures used to come back
|
|
77
|
+
with no `log.head`, no `log://` ref and nothing for `search_log` to search —
|
|
78
|
+
exactly when the transcript matters most.
|
|
79
|
+
|
|
80
|
+
#### A failed run can no longer poison its session
|
|
81
|
+
|
|
82
|
+
- New **`auto_close_logs`** (default `true`) closes log handles that a *failed*
|
|
83
|
+
run opened. A script aborting between `log using` and `log close` previously
|
|
84
|
+
left the handle dangling, and every subsequent run in that session died with
|
|
85
|
+
r(604) — an error about a different run, with no suggestions attached.
|
|
86
|
+
Handles opened by earlier runs are left alone; a `log_closed` warning records
|
|
87
|
+
what was closed.
|
|
88
|
+
- r(604) and r(606) are now the typed `ErrorKind.LOG_STATE` with Stata's
|
|
89
|
+
official labels and rc-specific fixes (`capture log close _all` for 604,
|
|
90
|
+
`capture log close` guarding for 606), and `recovery.retriable = true`.
|
|
91
|
+
|
|
92
|
+
#### Long runs no longer block the caller
|
|
93
|
+
|
|
94
|
+
- **`timeout_ms` is an advertised `stata_run` argument.** It was enforced but
|
|
95
|
+
not exposed, so passing it was an input-validation error.
|
|
96
|
+
- **`run_in_background`** returns a `job_id` immediately; poll with the new
|
|
97
|
+
**`stata_run_status`** tool (`wait_ms` blocks up to 60 s instead of
|
|
98
|
+
busy-polling) and enumerate with **`list_background_runs`**. Intended for
|
|
99
|
+
bootstraps, permutation tests and grouped estimation loops.
|
|
100
|
+
- **Fixed: the worker lock was acquired without a deadline**, so a call queued
|
|
101
|
+
behind a long run blocked indefinitely no matter what `timeout_ms` said.
|
|
102
|
+
`timeout_ms` now budgets the whole call, queueing included, and contention
|
|
103
|
+
returns the new `rc=-5` / `error.kind="session_busy"` — distinct from a
|
|
104
|
+
timeout because nothing was submitted to Stata and the healthy worker is
|
|
105
|
+
*not* killed.
|
|
106
|
+
|
|
107
|
+
#### Inline graphs are visible again
|
|
108
|
+
|
|
109
|
+
- `include_graphs: "inline"` now delivers graph bytes as MCP `ImageContent`
|
|
110
|
+
blocks instead of base64 inside a JSON string. The old form was unreadable to
|
|
111
|
+
a vision-capable client and was pure token cost. `graphs[].inline` is cleared
|
|
112
|
+
from the structured body so the bytes cross the wire once, and
|
|
113
|
+
`inline_delivered` records the outcome. At most 4 images per response (the
|
|
114
|
+
rest stay reachable via `get_graph(ref)`, reported as an
|
|
115
|
+
`inline_graphs_truncated` warning); `pdf` is never sent as an image block.
|
|
116
|
+
|
|
117
|
+
#### Generated files are reported
|
|
118
|
+
|
|
119
|
+
- New top-level **`outputs`** lists files a run created or modified in its
|
|
120
|
+
working directory (`esttab` tables, exported graphs, saved `.dta`), with
|
|
121
|
+
`path` / `bytes` / `created`. Previously this required
|
|
122
|
+
`persist_log_files=true` *and* `origin_path` — a dependency that was not
|
|
123
|
+
obvious from `persist_generated_files` defaulting to `true`, so the field was
|
|
124
|
+
always `null` in practice. Controlled by the new `track_output_files`
|
|
125
|
+
(default `true`); working directories above 5,000 files skip detection and
|
|
126
|
+
say so with an `output_tracking_skipped` warning rather than reporting a
|
|
127
|
+
partial answer.
|
|
128
|
+
|
|
129
|
+
### Added
|
|
130
|
+
|
|
131
|
+
- The VS Code extension now accepts Stata's familiar `Ctrl+D` shortcut
|
|
132
|
+
(`Cmd+D` on macOS) for running the selection or current line, while retaining
|
|
133
|
+
`Cmd/Ctrl+Enter`. The extension README also documents how to customize the
|
|
134
|
+
`stataCode.runSelection` keybinding and resolve conflicts.
|
|
135
|
+
|
|
136
|
+
## 0.10.0 — 2026-07-24
|
|
137
|
+
|
|
138
|
+
Surface-coverage and safety additions so every Stata usage scenario — MCP,
|
|
139
|
+
Jupyter, VS Code, and now a plain Bash/terminal — routes through the same typed
|
|
140
|
+
engine, and so an autonomous agent loop cannot escape to the OS. Also broadens
|
|
141
|
+
execution to **Stata 13+ without pystata** (console backend) and adds a
|
|
142
|
+
zero-Python standalone binary and one-click VS Code onboarding. The default
|
|
143
|
+
README is now Chinese (`README.md`); the English README moves to `README.en.md`.
|
|
144
|
+
|
|
145
|
+
### Added
|
|
146
|
+
|
|
147
|
+
- **Command-safety guard** (`stata_code.core.policy`). OS-escape / file-deletion
|
|
148
|
+
commands (`shell`, `winexec`, `erase`, `rm`, `rmdir`, and the `!` shell escape)
|
|
149
|
+
are screened out of submitted code *before* Stata runs, at both the
|
|
150
|
+
subprocess-pool boundary and the in-process runner. A blocked run returns
|
|
151
|
+
`ok=false`, `rc=-4`, `error.kind="policy_blocked"` (a new 32nd `ErrorKind`)
|
|
152
|
+
without touching Stata. Configurable from the environment so it crosses the
|
|
153
|
+
worker boundary: `STATA_CODE_COMMAND_POLICY` (`enforce` default / `warn` /
|
|
154
|
+
`off`), `STATA_CODE_POLICY_ALLOW`, `STATA_CODE_POLICY_BLOCK`. It is a guard
|
|
155
|
+
rail, not a sandbox.
|
|
156
|
+
- **`stata-code run`** — execute a `.do` file, one or more `-e` snippets, or
|
|
157
|
+
stdin through the subprocess pool and print the `RunResult` (text summary or
|
|
158
|
+
`--json`), with `--session`, `--timeout-ms`, `--full-log`, and `--graphs DIR`.
|
|
159
|
+
Exit code is `0` on success, `1` on a Stata/adapter error. This is the
|
|
160
|
+
Bash / plain-terminal surface: any agent that can shell out gets the full
|
|
161
|
+
structured error loop without MCP.
|
|
162
|
+
- **`stata-code setup`** — opt-in, config-mutating counterpart to `doctor`.
|
|
163
|
+
Writes the `stata-code` MCP server entry into Claude Code (`.mcp.json`),
|
|
164
|
+
Cursor (`.cursor/mcp.json`), and VS Code (`.vscode/mcp.json`), preserving other
|
|
165
|
+
servers and backing up any file it overwrites (`--dry-run`, `--python`,
|
|
166
|
+
`--json`). Codex (TOML) and Claude Desktop are emitted as copy-paste snippets.
|
|
167
|
+
- **Static do-file linting** (`stata_code.core.lint`), exposed as the `lint_do`
|
|
168
|
+
MCP tool (19th tool) and `stata-code lint`. A Stata-free syntactic check —
|
|
169
|
+
unbalanced braces, a `program` / `mata` / `python` block with no `end`, a stray
|
|
170
|
+
`end`, and a dangling `///` — so an agent can catch a class of mistakes before
|
|
171
|
+
spending a run. Advisory: a clean result is not a correctness guarantee.
|
|
172
|
+
- Public API: `lint_code`, `LintFinding`, `CommandPolicy`, `Violation`,
|
|
173
|
+
`policy_from_env`.
|
|
174
|
+
- **Console (batch) backend** (`stata_code.core.console`, `Backend.CONSOLE`).
|
|
175
|
+
Drives the Stata command-line executable in batch mode and parses the log plus
|
|
176
|
+
a marker-delimited results dump into the same v1.0 `RunResult` — typed
|
|
177
|
+
`r()`/`e()` scalars and macros, the estimation matrices (`e(b)`/`e(V)`/
|
|
178
|
+
`r(table)`), the error taxonomy, warnings, and dataset metadata. Works with
|
|
179
|
+
**Stata 13+** and **no pystata**. Exposed as `stata_code.run_console()`,
|
|
180
|
+
`stata-code run --backend {auto,pystata,console}`, and a `doctor` check. Stateless
|
|
181
|
+
per call; graphs not captured yet. Public API adds `run_console`,
|
|
182
|
+
`console_available`, `find_stata_cli`, `ConsoleNotAvailable`.
|
|
183
|
+
- **Zero-Python standalone binary** — `scripts/build_standalone.py` (PyInstaller)
|
|
184
|
+
and a CI workflow template (`packaging/standalone.github-workflow.yml`, copy to
|
|
185
|
+
`.github/workflows/`) build a self-contained `stata-code` executable per OS.
|
|
186
|
+
Paired with `--backend console` it needs no Python on the target machine.
|
|
187
|
+
- **One-click VS Code onboarding** — when the MCP server is missing, the extension
|
|
188
|
+
offers to create a workspace `.venv` and install the package; also available as
|
|
189
|
+
the command-palette entry "Stata: Set Up MCP Server (create .venv)".
|
|
190
|
+
- **Benchmark harness** (`benchmarks/`) — a tool-agnostic scaffold measuring
|
|
191
|
+
iterations-to-correct and tokens for typed vs. raw-log execution, with an offline
|
|
192
|
+
mock runner wired end-to-end and CI-covered.
|
|
193
|
+
- Chinese quickstart ([docs/quickstart.zh.md](docs/quickstart.zh.md)).
|
|
194
|
+
|
|
9
195
|
## 0.9.1 — 2026-07-22
|
|
10
196
|
|
|
11
197
|
Quality-hardening pass: correctness fixes from an adversarial code review,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stata-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
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
|
|
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
|
|
|
42
42
|
</p>
|
|
43
43
|
|
|
44
44
|
<p align="center">
|
|
45
|
-
<a href="README.md"><strong>English</strong></a> | <a href="README.
|
|
45
|
+
<a href="README.en.md"><strong>English</strong></a> | <a href="README.md">中文</a>
|
|
46
46
|
</p>
|
|
47
47
|
|
|
48
48
|
# stata-code
|
|
@@ -99,13 +99,13 @@ Then just ask:
|
|
|
99
99
|
|
|
100
100
|
`stata-code` writes the do-file, runs it, returns the table, and interprets the result — and can re-estimate the same ATT with [StatsPAI](https://github.com/brycewang-stanford/StatsPAI) to confirm the two stacks agree. These workflows ship as one-call MCP prompts (`did_event_study`, `iv_2sls`, `rdd`, `publication_table`, `cross_validate_did`) backed by an on-demand [recipe library](skills/stata-code/references/recipes/).
|
|
101
101
|
|
|
102
|
-
**Why `stata-code`:** MIT-licensed · ships as an MCP server, a bundled agent skill, a Jupyter kernel, **and** a
|
|
102
|
+
**Why `stata-code`:** MIT-licensed · ships as an MCP server, a bundled agent skill, a Jupyter kernel, a VS Code extension, **and** a plain-terminal CLI (`stata-code run`) · one structured, token-economy result schema (typed errors, native `r()` / `e()`) · runs on **Stata 17+ via pystata *or* Stata 13+ via a console backend that needs no pystata** · a zero-Python standalone binary · a default command-safety guard for unattended agents · cross-stack validation with StatsPAI for the Cunningham check.
|
|
103
103
|
|
|
104
104
|
```text
|
|
105
105
|
┌────────────────────────────────────────┐
|
|
106
106
|
│ stata-code core (Python) │
|
|
107
107
|
│ │
|
|
108
|
-
│ • pystata
|
|
108
|
+
│ • pystata 17+ / console 13+ backends │
|
|
109
109
|
│ • v1.0 unified result schema │
|
|
110
110
|
│ • token-economy defaults │
|
|
111
111
|
│ • multi-session via Stata frames │
|
|
@@ -118,10 +118,13 @@ Then just ask:
|
|
|
118
118
|
└─────────────┘ └────────────┘ └─────────────────┘
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
**
|
|
121
|
+
A fourth frontend, a **plain-terminal CLI** (`stata-code run` / `lint` / `setup`), gives any agent that can shell out — or a bare terminal — the same typed `RunResult`. And the core runs two backends: **pystata** (Stata 17+, in-memory sessions) or a **console backend** (Stata 13+ in batch mode, no pystata) — both returning the identical schema.
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
**Status: v0.10 (July 2026)** — the core, MCP server, Jupyter kernel, VS Code extension, and CLI work end-to-end against Stata 18 MP; the console backend broadens coverage to Stata 13+ without pystata. The test suite covers schema, runner, console parser, MCP, kernel, notebook, run-index, subprocess-pool, command policy, linter, and VS Code modules; CI also checks linting, type safety, schema generation, package metadata, and VSIX packaging. License: **MIT**.
|
|
124
124
|
|
|
125
|
+
Four workflows the current tree explicitly supports for end users and agents:
|
|
126
|
+
|
|
127
|
+
- **Run Stata from a plain terminal or a zero-Python binary.** `stata-code run analysis.do` (or `-e "code"`, or stdin) prints the same structured `RunResult` — text or `--json` — so any agent that can shell out gets the typed error loop without MCP. `--backend console` runs **Stata 13+ with no pystata**, and a standalone binary needs no Python install at all. See [From the Command Line](#from-the-command-line-bash).
|
|
125
128
|
- **Run Stata code from a Jupyter notebook.** `pip install "stata-code[kernel]"` + `stata-code-kernel install --user` registers a **Stata** kernel that the Jupyter Notebook UI, JupyterLab, and the VS Code Jupyter extension all pick up by name. Cells render Stata logs, graphs, and warnings inline (the kernel logo bundled since v0.5 makes it appear in VS Code's kernel picker too). See [As a Jupyter Kernel](#as-a-jupyter-kernel).
|
|
126
129
|
- **Optional agent "fix and rerun" loop.** `stata_run` returns typed `error.kind/line/context` plus `suggestions` on every failure. By default Claude Code only reports diagnostics — but if you explicitly say "fix this and rerun until it passes", the agent uses the same fields to edit your `.do` file and re-call `stata_run` until the run is green. The repair loop is **opt-in**: failed runs are diagnostics first, not automatic rewrite permission. See [Error Recovery in Agent Workflows](#error-recovery-in-agent-workflows).
|
|
127
130
|
- **Economist workflow guides.** The bundled skill and cookbook now cover
|
|
@@ -222,6 +225,72 @@ else:
|
|
|
222
225
|
print("hint:", s.action) # "Did you mean `mpg`?"
|
|
223
226
|
```
|
|
224
227
|
|
|
228
|
+
### From the Command Line (Bash)
|
|
229
|
+
|
|
230
|
+
Any agent or script that can shell out gets the same structured engine — no MCP
|
|
231
|
+
required. `stata-code run` executes a `.do` file, one or more `-e` snippets, or
|
|
232
|
+
code piped on stdin, and prints the `RunResult`:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
stata-code run analysis.do # run a do-file, text summary
|
|
236
|
+
stata-code run -e "sysuse auto" -e "regress mpg weight"
|
|
237
|
+
stata-code run analysis.do --json # full RunResult JSON (for agents)
|
|
238
|
+
echo "summarize price" | stata-code run - # read code from stdin
|
|
239
|
+
stata-code run model.do --graphs out/ # also export graphs to out/
|
|
240
|
+
stata-code run job.do --session modelA --timeout-ms 120000
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Exit code is `0` on success and `1` on a Stata / adapter error, so it drops into
|
|
244
|
+
CI and scripted fix-and-rerun loops. `stata-code lint analysis.do` runs the
|
|
245
|
+
static checker (unbalanced braces, missing `end`, dangling `///`) without
|
|
246
|
+
touching Stata.
|
|
247
|
+
|
|
248
|
+
**Backends — Stata 13+ without pystata.** `--backend` selects how code runs:
|
|
249
|
+
`pystata` (Stata 17+, in-memory sessions), `console` (Stata 13+ batch, no pystata,
|
|
250
|
+
stateless), or `auto` (default: pystata when available, else console). The console
|
|
251
|
+
backend drives the Stata command-line executable and parses the log into the same
|
|
252
|
+
typed `RunResult` — typed `r()`/`e()`, the estimation table, and the error
|
|
253
|
+
taxonomy — so older Stata and pystata-free environments are first-class:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
stata-code run analysis.do --backend console
|
|
257
|
+
export STATA_CODE_STATA_CLI=/usr/local/stata18/stata-mp # if not auto-found
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Zero-Python binary.** A standalone `stata-code` executable (built by
|
|
261
|
+
[`scripts/build_standalone.py`](scripts/build_standalone.py), with a ready-to-use
|
|
262
|
+
CI workflow template at
|
|
263
|
+
[`packaging/standalone.github-workflow.yml`](packaging/standalone.github-workflow.yml))
|
|
264
|
+
bundles the runtime and needs no Python install. Paired with `--backend console`,
|
|
265
|
+
it is a fully Python-free path to typed Stata results.
|
|
266
|
+
|
|
267
|
+
### One-Command Client Setup
|
|
268
|
+
|
|
269
|
+
`stata-code setup` writes the MCP server entry into a client's config — the
|
|
270
|
+
opt-in, mutating counterpart to the read-only `doctor`. It preserves other
|
|
271
|
+
servers and backs up any file it overwrites:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
stata-code setup --all # Claude Code, Cursor, VS Code (project)
|
|
275
|
+
stata-code setup --claude --dry-run # preview without writing
|
|
276
|
+
stata-code setup --vscode --python .venv/bin/python # pin an interpreter
|
|
277
|
+
stata-code setup --codex # print a copy-paste TOML snippet
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Command Safety
|
|
281
|
+
|
|
282
|
+
By default the runner blocks OS-escape and file-deletion commands (`shell`,
|
|
283
|
+
`winexec`, `erase`, `rm`, `rmdir`, and the `!` shell escape) *before* they reach
|
|
284
|
+
Stata, so an autonomous agent loop can't delete files or run arbitrary shell
|
|
285
|
+
commands. A block returns a `policy_blocked` result (`rc=-4`) rather than
|
|
286
|
+
running. It is a guard rail, not a sandbox; tune it with environment variables:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
STATA_CODE_COMMAND_POLICY=off # disable the guard entirely
|
|
290
|
+
STATA_CODE_POLICY_ALLOW=shell # allow specific commands (comma-separated)
|
|
291
|
+
STATA_CODE_POLICY_BLOCK=python # block additional commands
|
|
292
|
+
```
|
|
293
|
+
|
|
225
294
|
### As an MCP Server
|
|
226
295
|
|
|
227
296
|
After `pip install "stata-code[mcp]"`, the `stata-code-mcp` binary is on your `PATH`. You can wire it into Claude Code, Cursor, Claude Desktop, or any other MCP-compatible client.
|
|
@@ -243,7 +312,7 @@ claude mcp add stata-code --scope local -- stata-code-mcp
|
|
|
243
312
|
claude mcp add stata-code --scope project -- stata-code-mcp
|
|
244
313
|
```
|
|
245
314
|
|
|
246
|
-
Then launch `claude` and type `/mcp` to confirm `stata-code` shows up with its
|
|
315
|
+
Then launch `claude` and type `/mcp` to confirm `stata-code` shows up with its 21 tools (`stata_run`, `stata_run_status`, `list_background_runs`, `stata_info`, `get_log`, `search_log`, `get_graph`, `get_matrix`, `inspect_data`, `lint_do`, `install_package`, `list_sessions`, `cancel_session`, `reset_session`, `notebook_outline`, `notebook_get_cell`, `notebook_locate`, `notebook_edit_cell`, `notebook_insert_cell`, `notebook_delete_cell`, `list_runs`).
|
|
247
316
|
|
|
248
317
|
#### Error Recovery in Agent Workflows
|
|
249
318
|
|
|
@@ -331,17 +400,20 @@ If an OpenAI-backed client reports `API Error: 400 Invalid schema for function
|
|
|
331
400
|
upgrade to `stata-code>=0.6.5`, then restart the MCP client. Older server
|
|
332
401
|
processes keep advertising the stale schema until they are restarted.
|
|
333
402
|
|
|
334
|
-
The MCP server registers
|
|
403
|
+
The MCP server registers 21 tools:
|
|
335
404
|
|
|
336
405
|
| Tool | Purpose |
|
|
337
406
|
| --- | --- |
|
|
338
|
-
| `stata_run` | Execute Stata code and return a v1.0 RunResult JSON |
|
|
407
|
+
| `stata_run` | Execute Stata code and return a v1.0 RunResult JSON; `include_results` / `include_estimation` bound the payload, `timeout_ms` enforces a hard deadline, `run_in_background` returns a job id |
|
|
408
|
+
| `stata_run_status` | Poll a background run's status and result; `wait_ms` blocks up to a bounded time |
|
|
409
|
+
| `list_background_runs` | List background runs tracked by this server |
|
|
339
410
|
| `stata_info` | Report Stata edition, version, and capabilities |
|
|
340
411
|
| `get_log` | Fetch the full log behind a `log://` ref |
|
|
341
412
|
| `search_log` | Search matching lines inside a stored `log://` payload |
|
|
342
413
|
| `get_graph` | Fetch graph bytes behind a `graph://` ref (`ImageContent`) |
|
|
343
414
|
| `get_matrix` | Fetch matrix payloads behind a `matrix://` ref |
|
|
344
415
|
| `inspect_data` | Run `describe` + `codebook` and return compact dataset metadata |
|
|
416
|
+
| `lint_do` | Statically check do-file source (unbalanced braces, missing `end`, dangling `///`) before spending a run |
|
|
345
417
|
| `install_package` | Install an SSC or explicit `net install` package and verify it resolves |
|
|
346
418
|
| `list_sessions` | Enumerate live sessions |
|
|
347
419
|
| `cancel_session` | Cancel a session; the subprocess-backed path terminates in-flight runs and short-circuits pending ones |
|
|
@@ -485,7 +557,9 @@ stata_code/
|
|
|
485
557
|
│ ├── runner.py # in-process execute(); collects everything via sfi
|
|
486
558
|
│ └── _pool.py # subprocess workers for public API / MCP hard timeouts
|
|
487
559
|
├── mcp/
|
|
488
|
-
│
|
|
560
|
+
│ ├── server.py # MCP server (21 tools)
|
|
561
|
+
│ └── ...
|
|
562
|
+
├── core/console.py # console (batch) backend — Stata 13+, no pystata
|
|
489
563
|
└── kernel/
|
|
490
564
|
└── kernel.py # Jupyter kernel
|
|
491
565
|
```
|
|
@@ -496,18 +570,33 @@ stata_code/
|
|
|
496
570
|
|
|
497
571
|
## Comparison
|
|
498
572
|
|
|
499
|
-
| | stata-code | SepineTam/stata-mcp | hanlulong/stata-mcp | nbstata |
|
|
500
|
-
| --- | --- | --- | --- | --- |
|
|
501
|
-
| License | **MIT** | AGPL-3.0 | MIT | GPL-3.0 |
|
|
502
|
-
| Standalone MCP | ✓ | ✓ | bundled with VS Code | — |
|
|
503
|
-
| Jupyter kernel | ✓ | — | — | ✓ |
|
|
504
|
-
| Unified result schema | ✓ ([SCHEMA.md](SCHEMA.md)) | per-tool | per-tool | per-tool |
|
|
505
|
-
|
|
|
506
|
-
|
|
|
507
|
-
|
|
|
508
|
-
|
|
|
509
|
-
|
|
510
|
-
|
|
573
|
+
| | stata-code | SepineTam/stata-mcp | hanlulong/stata-mcp | stata-all-in-one | nbstata |
|
|
574
|
+
| --- | --- | --- | --- | --- | --- |
|
|
575
|
+
| License | **MIT** | AGPL-3.0 | MIT | MIT | GPL-3.0 |
|
|
576
|
+
| Standalone MCP | ✓ | ✓ | bundled with VS Code | — (bespoke HTTP + copy-paste) | — |
|
|
577
|
+
| Jupyter kernel | ✓ | — | — | — | ✓ |
|
|
578
|
+
| Unified result schema | ✓ ([SCHEMA.md](SCHEMA.md)) | per-tool | per-tool | raw log to the agent | per-tool |
|
|
579
|
+
| Typed errors + suggestions | ✓ (32 kinds) | — | — | — | — |
|
|
580
|
+
| Token-economy defaults | ✓ (log refs, graph refs) | — | — | — | — |
|
|
581
|
+
| Command-safety guard | ✓ (`shell`/`erase`/`rmdir`/`!`) | ✓ (27-rule) | — | — | — |
|
|
582
|
+
| Bash / plain-terminal CLI | ✓ (`stata-code run`) | ✓ | — | — | — |
|
|
583
|
+
| One-command client setup | ✓ (`stata-code setup`) | ✓ (`install --all`) | bundled | — | — |
|
|
584
|
+
| Static pre-run lint | ✓ (`lint_do`) | — | — | — | — |
|
|
585
|
+
| Stata 13–16 (no pystata) | ✓ (console backend) | ✓ | — | ✓ (COM/dylib) | — |
|
|
586
|
+
| Zero-Python install | ✓ (standalone binary) | — | — | ✓ (VS Code-native) | — |
|
|
587
|
+
| Human IDE polish (data viewer, inline graphs) | growing | — | ✓ | ✓ (strongest) | ✓ |
|
|
588
|
+
| Multi-session | ✓ (Stata frames) | partial | — | — | — |
|
|
589
|
+
| Mature ecosystem | early | ✓ (statamcp.com) | ✓ (11k installs) | ✓ (distributor-backed) | ✓ |
|
|
590
|
+
|
|
591
|
+
`stata-code` owns the **agent-native, typed-contract** lane: one structured
|
|
592
|
+
`RunResult` schema across MCP, Jupyter, VS Code, and a plain-terminal CLI; a
|
|
593
|
+
32-kind error taxonomy with recovery contracts; token-economy refs; and now a
|
|
594
|
+
console backend (Stata 13+) plus a zero-Python binary. Editor-first tools like
|
|
595
|
+
`stata-all-in-one` lead on human IDE polish and hand the agent raw log text;
|
|
596
|
+
`stata-code` matches their onboarding/version reach while keeping the typed
|
|
597
|
+
execution contract they don't have. See
|
|
598
|
+
[docs/competitive-landscape.md](docs/competitive-landscape.md) for the full
|
|
599
|
+
teardown.
|
|
511
600
|
|
|
512
601
|
---
|
|
513
602
|
|
|
@@ -522,8 +611,13 @@ stata_code/
|
|
|
522
611
|
- Graph capture: `png` / `svg` / `pdf` with ref store and source-command attribution
|
|
523
612
|
- Log truncation with ref store
|
|
524
613
|
- Warning extraction: 5 categories + generic notes
|
|
525
|
-
-
|
|
526
|
-
- MCP server:
|
|
614
|
+
- 32-kind error taxonomy with canonical suggestions
|
|
615
|
+
- MCP server: 21 tools, including notebook navigation / search / atomic edits, the run-bundle index (`list_runs`), log grep (`search_log`), dataset inspection (`inspect_data`), static linting (`lint_do`), and package installation (`install_package`)
|
|
616
|
+
- Command-safety guard: OS-escape / file-deletion commands (`shell`, `winexec`, `erase`, `rm`, `rmdir`, `!`) are blocked before Stata runs; configurable via `STATA_CODE_COMMAND_POLICY` / `STATA_CODE_POLICY_ALLOW` / `STATA_CODE_POLICY_BLOCK`
|
|
617
|
+
- Bash / plain-terminal surface: `stata-code run` (a `.do` file, `-e` snippets, or stdin) prints the same structured `RunResult` any agent that can shell out can consume; `stata-code lint` runs the linter; `stata-code setup` writes MCP client configs
|
|
618
|
+
- Console (batch) backend (`core/console.py`, `--backend console`, `run_console()`): drives the Stata command-line executable, parses the log into the same typed `RunResult`, and supports **Stata 13+ with no pystata**
|
|
619
|
+
- Zero-Python standalone binary ([`scripts/build_standalone.py`](scripts/build_standalone.py) + CI workflow template [`packaging/standalone.github-workflow.yml`](packaging/standalone.github-workflow.yml)); with `--backend console` it is a fully Python-free path to typed results
|
|
620
|
+
- One-click VS Code onboarding: the extension offers to create a workspace `.venv` and install the server (command palette: “Stata: Set Up MCP Server”)
|
|
527
621
|
- Jupyter kernel: rewired to the v1.0 pipeline, kernel logos bundled
|
|
528
622
|
- Matrix size cap + `get_matrix(ref)` for large matrices (>10k cells)
|
|
529
623
|
- Subprocess-backed hard timeout and cancellation for the public Python API and MCP server: `timeout_ms`, `cancel(session_id)`, and MCP `cancel_session`
|
|
@@ -541,12 +635,16 @@ stata_code/
|
|
|
541
635
|
|
|
542
636
|
### Next Up
|
|
543
637
|
|
|
544
|
-
-
|
|
545
|
-
-
|
|
546
|
-
-
|
|
638
|
+
- Streaming / progress for long runs (`log.complete:false`, incremental log lines) so 20-minute `boottest` / `csdid` jobs report before they finish
|
|
639
|
+
- Hard timeout / cancellation for the Jupyter kernel (move it from the direct in-process runner to the subprocess pool, or an equivalent)
|
|
640
|
+
- Console backend: graph capture and richer matrix coverage (values currently materialized for the estimation matrices; state is per-call)
|
|
641
|
+
- Human IDE polish to match editor-first tools: inline graph rendering + DPI export, a scalable data viewer, and an optional "attach to a running Stata" backend
|
|
642
|
+
- Publish the reliability/token [benchmark](benchmarks/) results (typed contract vs. raw-log tools) as evidence, not a claim
|
|
547
643
|
- **v1.0** — Stable schema, broader Stata edition coverage
|
|
548
644
|
|
|
549
|
-
See [
|
|
645
|
+
See [docs/competitive-landscape.md](docs/competitive-landscape.md) for how these
|
|
646
|
+
priorities line up against comparable tools, and [SCHEMA.md §7](SCHEMA.md) for
|
|
647
|
+
explicitly out-of-scope items.
|
|
550
648
|
|
|
551
649
|
---
|
|
552
650
|
|