# dsh-stata ![platform](https://img.shields.io/badge/platform-Windows-0078D4) ![node](https://img.shields.io/badge/node-22.19%2B%20%7C%7C%2024%2B-3C873A) ![license](https://img.shields.io/badge/license-MIT-green) Control a live **Stata** session from [DSH (DeepSeek Harness)](https://github.com/deepseek-ai): run commands, execute `.do` files, read back the real console output, and keep data in memory between calls. > 中文文档:[README.md](README.md) ## Why not PyStata or COM The official automation entry points are unavailable in many setups: | Approach | Requirement | Observed failure | | --- | --- | --- | | PyStata / `StataSO_Main` | valid license, headless init | `SystemError: failed to initialize Stata` once the license expires | | Batch mode `/e do file.do` | valid license | process exits immediately, no `.log` produced | | OLE Automation (COM) | `StataOLEApp` registered in HKLM | not registered by Stata 17; per-user HKCU registration is invisible to `ole32` in some environments and needs admin rights | | **dsh-stata** | ability to open the Stata GUI | ✅ plain file queue: no COM, no admin, no batch mode | `dsh-stata` does not bypass Stata's license check — it drives a **visible GUI window** with a polling do-file, so if you can run a command in Stata by hand, this plugin can run it for you. ## Features | Tool | Purpose | | --- | --- | | `stata_run` | Run Stata commands in the persistent session (multi-line, loops, `program define`, comments) and return console output | | `stata_do` | Run a `.do` file from disk (absolute or workspace-relative) | | `stata_status` | Report executable, session directory, pid, readiness, exchange files | | `stata_reset` | Stop the command loop and return the Stata window to an interactive prompt; recover from hangs/timeouts | It also registers the `stata` skill so the agent knows the session model and the usual workflows. - State persists across calls: current dataset, macros, `program define`, stored estimation results. - Output is the real Results window: command echo, results, and error codes such as `r(111);`. - A Stata error does not kill the session; the next command still runs. - One command block at a time, with timeouts and a mutex; recover with `stata_reset`. ## Requirements - Windows (the only platform currently supported; PRs welcome). - A Stata GUI installation (Stata 14+, any edition). - DSH with plugin management enabled (sidebar **Plugins** page, or the `plugin_manager` tool in Creator mode). - Node.js `^22.19.0 || >=24.0.0` (the bundled DSH runtime is fine). ## Install 1. Open **Plugins** in the DSH sidebar. 2. Click **Add plugin** and enter either the Git URL `https://github.com/SoldierNo4/dsh-stata` or the absolute path of a local checkout (the directory containing `package.json`). 3. Click **Install**, then **Enable**. Profiles with HMR recompose immediately; others need a restart. The plugin's own `cordis.patch.yml` registers the tools and the skill — no profile patch editing required. ## Quick start Ask DSH: > Load the auto dataset in Stata and regress price on mpg and weight ```text stata_run: sysuse auto, clear regress price mpg weight . sysuse auto, clear (1978 automobile data) . regress price mpg weight Source | SS df MS Number of obs = 74 -------------+---------------------------------- F(2, 71) = 14.74 Model | 186321280 2 93160639.9 Prob > F = 0.0000 Residual | 448744116 71 6320339.67 R-squared = 0.2934 -------------+---------------------------------- Adj R-squared = 0.2735 Total | 635065396 73 8699525.97 Root MSE = 2514 ------------------------------------------------------------------------------ price | Coefficient Std. err. t P>|t| [95% conf. interval] -------------+---------------------------------------------------------------- mpg | -49.51222 86.15604 -0.57 0.567 -221.3025 122.278 weight | 1.746559 .6413538 2.72 0.008 .467736 3.025382 _cons | 1946.069 3597.05 0.54 0.590 -5226.245 9118.382 ------------------------------------------------------------------------------ ``` The next call reuses the data already in memory: ```text stata_run: display _N " obs in memory" → 74 obs in memory ``` ## Tools ### `stata_run` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `code` | string | ✅ | Commands, multi-line or semicolon-separated, exactly as typed in Stata | | `timeoutSeconds` | integer | ❌ | Max wait in seconds (default 180, max 3600) | Returns `{ ok, output, durationMs, error? }`; `output` is the console text with the log banner/footer removed. ### `stata_do` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `path` | string | ✅ | Path to the `.do` file, absolute or relative to the workspace | | `timeoutSeconds` | integer | ❌ | Same as above | ### `stata_status` No parameters. Reports session directory, executable, pid, `processAlive` / `ready` / `idle` / `lockHeld`, and exchange files. ### `stata_reset` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `force` | boolean | ❌ | Kill the Stata process if it will not leave the loop gracefully | The graceful path leaves Stata at an interactive prompt (data kept); `force` discards in-memory data. The next `stata_run` starts a fresh session. ## Configuration Works with zero configuration. To override, add a `config` block to the plugin row in the profile patch layer: ```yaml - id: dsh-stata config: stataPath: C:\Program Files\Stata17\StataMP-64.exe sessionDir: E:\stata-session defaultTimeoutMs: 180000 ``` | Key | Default | Description | | --- | --- | --- | | `stataPath` | auto-detect | Stata executable. Order: this key → `DSH_STATA_EXE` / `STATA_EXE` → newest `StataMP-64.exe` / `StataSE-64.exe` / `StataBE-64.exe` under `%ProgramFiles%\Stata*` | | `sessionDir` | `%LOCALAPPDATA%\dsh-stata` | Exchange directory; falls back to the temp directory when not writable | | `defaultTimeoutMs` | `180000` | Default per-command timeout, max `3600000` | ## How it works ```text stata_run(code) │ 1. write /cmd.do (prefixed with `log using response.log`) │ 2. delete /done.txt ▼ /cmd.do ──poll 60ms──► daemon.do running inside the Stata GUI ▲ │ capture noisily do cmd.do │ │ close log, write done.txt │ 3. poll until done.txt ◄────────────┘ └─ 4. read response.log → strip banner/footer → return as tool output ``` - `daemon.do` loops with `quietly { while (1) { ... quietly sleep 100 } }`; `stop.txt` makes it exit and write `idle.txt` (the graceful `stata_reset` path). - `.lock` serializes commands within a session; `state.json` stores pid and start time to detect live sessions. - Measured latency (Stata 17 MP, Windows 11): 1.5–3.0 s for the first command after a cold start, 0.2–1.1 s afterwards. ## Troubleshooting | Symptom | Fix | | --- | --- | | `stata_run` timed out | The command may still be running; check `stata_status`, then `stata_reset` (raise `timeoutSeconds` for long jobs) | | Stata window closed by hand | The next `stata_run` detects the dead session and starts a new one | | `Stata was not found` | Set the `stataPath` config or the `DSH_STATA_EXE` environment variable | | Session directory not writable | Point `sessionDir` at a writable folder (some sandboxes restrict `%LOCALAPPDATA%`) | | Full reset | `stata_reset` with `force: true`, then call `stata_run` again | ## Limitations - **Windows only** for now (executable detection is Windows-specific). - **Serial**: one command block per session at a time; parallel work needs multiple sessions with distinct `sessionDir`. - Never call `exit` / `quit` — it closes Stata and kills the session. - Do not edit `daemon.do` / `cmd.do` inside the session directory while a session is active. - Use `graph export out.png, replace` for images; a bare `graph` opens a Stata window. - The plugin launches Stata and reads/writes files on your machine — only run scripts you trust. ## Development ```bash # unit tests, no Stata required (registration, schemas, config parsing) npm test # end-to-end check: install this directory into DSH, then call # stata_run "display 2 + 2" # stata_run "sysuse auto, clear" # stata_run "summarize mpg" # stata_do # stata_reset ``` Layout: ```text stata-plugin/ ├── cordis.patch.yml # registers the plugin row ├── lib/ │ ├── index.js # entry: register tools + skill, handle dispose │ ├── session.js # StataSession: dirs, process, lock, logs, shutdown │ ├── tools.js # schemas and execution for the four tools │ └── skill.js # loads skills/stata/SKILL.md ├── locale/{en,zh}.json # plugin manager display metadata ├── skills/stata/SKILL.md # skill body ├── test/ # node:test unit tests └── icon.svg ``` ## Contributing Issues and PRs are welcome. Run `npm test` before submitting, and include your Stata version, DSH version, and reproduction steps. Do not commit `node_modules/` or session exchange files. ## License [MIT](LICENSE) © 2026 SoldierNo4 Not affiliated with StataCorp and contains no Stata code; a valid Stata license is required.