Introduction
Stata projects increasingly run inside larger workflows — a Makefile that rebuilds results when inputs change, a CI service that reruns an analysis on every commit, a replication package that must run unattended on a stranger’s machine. Integration like this rests on two things Stata leaves implicit: whether a step succeeded, and what the project needs in order to run.
stacy makes both explicit. It is a task runner and package manager for Stata: stacy run executes a script, parses the log, and returns a proper exit code, while stacy add and stacy install maintain a manifest and lockfile for dependencies. Every command works from both the terminal and the Stata console. With these primitives, Stata projects can be automated, versioned, and reproduced.
| If you know… | stacy is like… | Key similarity |
|---|---|---|
| Rust | Cargo | Manifest + lockfile + build orchestration |
| Python | uv or Poetry | Project dependencies + reproducible environments |
| JavaScript | npm | package.json / package-lock.json workflow |
| R | renv | Project-local library snapshots |
| Stata | (nothing existed) | This is what stacy provides |
The Problem
The outcome is implicit. Stata’s batch mode (stata-mp -b do script.do) exits with code 0 even when scripts fail. Errors are buried in logs. Build systems, CI pipelines, and downstream scripts cannot detect failure — they proceed as if nothing went wrong.
The environment is implicit. User-written packages install to a global path — no manifest, no lockfile, no isolation between projects. There is no way to declare dependencies and install from that declaration. Each ssc install retrieves whatever version exists at that moment; a collaborator installing later gets a different version entirely.
The Solution
stacy makes both sides explicit:
# Execution: proper exit codes
stacy run analysis.do
echo $? # 0 on success, 1-10 on various errors
# Environment: lockfile-based packages
stacy add estout reghdfe # Adds to stacy.toml, creates stacy.lock
stacy install # Installs exact versions from lockfile
A project that declares its dependencies can be installed identically elsewhere. A project that signals failure can be automated reliably:
- Journals can verify that replication packages run
- Cluster jobs fail fast instead of silently producing garbage
- Collaborators work from the same locked environment rather than debugging “it worked on my machine”
results/output.dta: analysis.do data/input.dta
stacy run analysis.do # Stops on failure
One Tool, Two Interfaces
stacy is a single binary you can drive from the terminal or from inside Stata:
# Terminal
$ stacy run analysis.do --timeout 600
. stacy run analysis.do, timeout(600)
The Stata commands are thin wrappers around the same binary, generated from the same command schema as the command-line interface, so the two never drift apart. help stacy works as for any other Stata package. See Installation for setup.
What stacy Manages (and What It Doesn’t)
stacy makes two things explicit – execution outcomes and the package environment – and stays out of everything else:
| stacy manages | stacy does not manage |
|---|---|
| Whether a script succeeded (exit codes) | Orchestrating large pipelines (use Make/Snakemake on top) |
| Which packages a project needs (manifest) | The Stata version itself (use Docker for full-stack pinning) |
| Which exact versions are installed (lockfile + checksums) | Data files or other languages’ environments |
Where Stata looks for packages at runtime (S_ADO) | Transitive dependencies (Stata packages don’t declare them reliably) |
This makes stacy a small, composable piece of infrastructure rather than a framework: it slots under whatever build system, scheduler, or CI service you already use.
At a Glance
| Without stacy | With stacy |
|---|---|
stata -b do script.do returns 0 even on error | stacy run script.do returns 1-10 on error |
| Packages are global, unversioned | stacy.lock pins exact versions with SHA256 checksums |
| Errors buried in log files | Errors displayed with documentation links |
| “It worked on my machine” | Same versions everywhere via lockfile |
Manual ssc install in scripts | stacy install from lockfile |
Quick Example
# Run with error detection
stacy run analysis.do
# Initialize a project and add packages
stacy init
stacy add estout reghdfe
# Install all packages from lockfile (like npm install)
stacy install
# Check system configuration
stacy doctor
How to Use These Docs
- Just want to run scripts? Start with Quick Start
- Setting up a project? See Project Config
- Integrating with build tools? Read Build Integration
- Looking up a command? Check the Command Reference
- Something not working? Try Troubleshooting or FAQ
Next Steps
- Installation - Get stacy installed
- Quick Start - Run your first script
- FAQ - Common questions answered
Installation
stacy is a single static binary with no runtime dependencies.
Quick Install
macOS / Linux:
curl -fsSL https://stacy.janfasnacht.com/install.sh | bash
Windows (PowerShell):
irm https://raw.githubusercontent.com/janfasnacht/stacy/main/install.ps1 | iex
Both install to ~/.local/bin/ (or equivalent). Ensure this directory is in your PATH.
From within Stata
If you prefer not to leave Stata, you can install stacy directly from the Stata console:
net install stacy, from("https://stacy.janfasnacht.com/stata")
stacy setup
This downloads the Stata wrappers and installs the stacy binary to ~/.local/bin/. After setup, all stacy commands are available as native Stata commands (e.g., stacy run analysis.do, stacy add estout).
Other Methods
Homebrew:
brew install janfasnacht/stacy/stacy
Cargo (from source):
cargo install --git https://github.com/janfasnacht/stacy.git
Manual download: Get binaries from the releases page.
Verify Installation
stacy --version # Check version
stacy doctor # Check Stata detection and configuration
Stata Detection
stacy automatically finds Stata in common locations:
| Platform | Searched paths |
|---|---|
| macOS | /Applications/Stata*/, /Applications/StataNow/ |
| Linux | /usr/local/stata*, ~/stata* |
| Windows | C:\Program Files\Stata*\ |
If Stata is elsewhere, configure via (in precedence order):
- CLI flag:
stacy run --engine /path/to/stata-mp script.do - Environment:
export STATA_BINARY=/path/to/stata-mp - Config file:
~/.config/stacy/config.toml
Troubleshooting
Common installation issues:
| Problem | Solution |
|---|---|
stacy: command not found | Add ~/.local/bin to PATH, restart terminal |
| macOS blocks binary | Run xattr -d com.apple.quarantine ~/.local/bin/stacy |
| Stata not found | Set STATA_BINARY env var or config file |
See Troubleshooting for detailed solutions.
Updating
Re-run the install command, or:
brew upgrade stacy # Homebrew
cargo install ... --force # Cargo
Update Notifications
stacy checks for new releases on startup and prints a notification to stderr if one is available:
Update available: v1.1.0 → v1.2.0
Run `brew upgrade stacy` to update
The check uses a local cache refreshed every 24 hours in the background. It never slows down commands.
Update notifications are automatically suppressed in CI environments, non-interactive sessions, and piped output. To disable globally, set update_check = false in ~/.config/stacy/config.toml or set the STACY_NO_UPDATE_CHECK environment variable.
Uninstalling
rm ~/.local/bin/stacy # Remove binary
rm -rf ~/.config/stacy # Remove config (optional)
rm -rf ~/.cache/stacy # Remove package cache (optional)
Next Steps
- Quick Start - Run your first script
- stacy doctor - Troubleshoot detection issues
Quick Start
This page uses terminal commands. If you installed the Stata wrappers, every command also works from the Stata console – . stacy run analysis.do instead of $ stacy run analysis.do.
1. Verify Setup
stacy doctor
This checks that stacy can find Stata. If it fails, see Troubleshooting.
2. Run a Script
stacy run analysis.do
On success: exit code 0. On failure: non-zero exit code with error details.
❌ Failed: analysis.do (0.08s)
Error: r(199) - unrecognized command
See: https://www.stata.com/manuals/perror.pdf#r199
That’s it. Your existing scripts work unchanged.
3. Initialize a Project
stacy init
Creates stacy.toml (project configuration) and .gitignore.
4. Add Packages
stacy add estout reghdfe
This adds packages to stacy.toml, downloads them to the cache, and creates stacy.lock with exact versions.
To install from an existing lockfile:
stacy install
Your dependencies are now pinned. Anyone running stacy install gets the same versions.
5. Define Tasks
Add a [scripts] section to stacy.toml:
[scripts]
clean = "clean_data.do"
analysis = "run_analysis.do"
all = ["clean", "analysis"]
Run tasks by name:
stacy task clean # Run one script
stacy task all # Run sequence
This replaces master.do with explicit, named tasks that stop on error.
For complex pipelines with dependency tracking, see Build Integration.
Common Options
stacy run -v analysis.do # Stream log output
stacy run -c 'display 2+2' # Run inline code
stacy run --format json ... # Machine-readable output
Next Steps
- Commands - Full reference
- Configuration - Project settings
- FAQ - Common questions
Project Config (stacy.toml)
The stacy.toml file configures project-level settings. Created by stacy init.
Location
my-project/
├── stacy.toml # Project config (this file)
├── stacy.lock # Package lockfile
└── ...
Full Reference
[project]
name = "my-analysis"
authors = ["Jane Doe <jane@university.edu>"]
description = "Economic analysis of market dynamics"
url = "https://github.com/user/my-analysis"
[run]
log_dir = "logs"
show_progress = true
progress_interval_seconds = 10
max_log_size_mb = 50
[paths]
ado = ["ado", "lib/custom"]
[packages.dependencies]
estout = "ssc"
reghdfe = "github:sergiocorreia/reghdfe"
[packages.dev]
assert = "ssc"
[scripts]
clean = "src/01_clean.do"
analyze = "src/02_analyze.do"
build = ["clean", "analyze"]
Sections
[project]
Project metadata. Optional but recommended for collaborative projects.
| Field | Type | Default | Description |
|---|---|---|---|
name | string | directory name | Project name |
authors | array | [] | List of authors |
description | string | none | Project description |
url | string | none | Project URL |
[run]
Settings for stacy run command.
| Field | Type | Default | Description |
|---|---|---|---|
log_dir | string | "logs" | Directory for kept log files, relative to the project root |
show_progress | bool | true | Show progress during execution |
progress_interval_seconds | int | 10 | Progress update interval |
max_log_size_mb | int | 50 | Log size warning threshold |
Batch logs are internal: a script that succeeds leaves none behind. A script that
fails keeps its log, and log_dir is where it goes — for stacy run as well as
the scripts run by stacy task, stacy test and stacy bench. The directory is
created when the first log needs it. stacy run --log <path> overrides log_dir
for that run.
[paths]
Local ado directories to prepend to S_ADO. Paths are relative to the project root and resolved to absolute paths at runtime. This lets strict mode work with project-local .ado programs without needing adopath ++ boilerplate.
| Field | Type | Default | Description |
|---|---|---|---|
ado | array | [] | Local ado directories |
[paths]
ado = ["ado", "lib/custom"]
Directories are prepended to S_ADO in declared order, before package cache paths. Non-existent paths produce a warning in stacy doctor but are not a hard error.
[packages.dependencies], [packages.dev], [packages.test]
Package dependencies by group. Format: package_name = "source".
[packages.dependencies]
estout = "ssc" # From SSC
reghdfe = "github:sergiocorreia/reghdfe" # From GitHub
[packages.dev]
assert = "ssc"
[packages.test]
mytest = "ssc"
Sources:
"ssc"- Install from SSC"github:user/repo"- Install from GitHub (default branch)"github:user/repo@tag"- Install from GitHub at specific tag/branch
[scripts]
Task definitions for stacy task. Supports three formats:
[scripts]
# Simple: a task is a script path
clean = "src/01_clean.do"
analyze = "src/02_analyze.do"
tables = "src/03_tables.do"
# Sequential: an array runs tasks (or script paths) in order
build = ["clean", "analyze", "src/03_tables.do"]
# Parallel: run tasks (or script paths) concurrently
outputs = { parallel = ["analyze", "tables"] }
Array entries and parallel lists may name other tasks or point directly at script paths. The object form also supports script, args, and description keys:
analyze = { script = "src/02_analyze.do", description = "Main estimates" }
Important Notes
Unknown Keys Are Rejected
Task names under [scripts] are yours to pick. Every other key must be one stacy
knows — including the keys inside a package table (source, version) and a task
table (script, args, parallel, description). A key it does not know is an
error, not a shrug:
$ stacy lock
Error: Failed to parse stacy.toml: TOML parse error at line 4, column 1
|
4 | [dependencies]
| ^^^^^^^^^^^^^^
unknown field `dependencies`, expected one of `project`, `run`, `paths`, `packages`, `scripts`
hint: declare these under [packages.dependencies]
Dependencies declared under the wrong key used to be dropped without a word, and
stacy lock then reported success on zero packages. The same went for a typo
inside a package table: { source = "ssc", verison = "1.0.0" } parsed, lost the
version pin, and resolved the latest release instead.
Stata Binary
stacy auto-detects Stata in common locations. If detection fails, configure manually:
# Environment variable (recommended)
export STATA_BINARY=/path/to/stata-mp
# Or per-command
stacy run --engine /path/to/stata-mp script.do
# Or user config file (see User Config docs)
stata_binary = "/path/to/stata-mp"
Run stacy doctor to verify detection.
All Fields are Optional
An empty stacy.toml is valid:
# This file can be empty - all fields have defaults
Paths are Relative
Paths in stacy.toml are relative to the project root (e.g., script paths in [scripts]).
Global Package Cache
Packages are installed to a global cache at ~/.cache/stacy/packages/ and shared across all projects. Use stacy cache packages list to view cached packages.
Examples
Minimal
[project]
name = "analysis"
With Packages
[project]
name = "analysis"
[packages.dependencies]
estout = "ssc"
reghdfe = "github:sergiocorreia/reghdfe"
With Local Ado Paths
[project]
name = "analysis"
[paths]
ado = ["ado"]
[packages.dependencies]
estout = "ssc"
With Tasks
[project]
name = "analysis"
[packages.dependencies]
estout = "ssc"
[scripts]
clean = "src/01_clean.do"
build = "src/02_build.do"
report = "src/03_report.do"
all = ["clean", "build", "report"]
CI-Friendly
[project]
name = "analysis"
[run]
show_progress = false # Cleaner CI logs
Validation
stacy validates the config on load. Invalid TOML causes an error:
Error: Failed to parse stacy.toml: expected `=` at position 15-16
Use stacy env to verify your configuration is loaded correctly.
See Also
- User Config - Machine-specific settings (
~/.config/stacy/config.toml) - stacy init - Create stacy.toml
- stacy env - View loaded configuration
User Config (~/.config/stacy/config.toml)
Machine-specific settings that should not be committed to version control.
This is separate from project config (stacy.toml), which lives in the project directory and is shared with collaborators.
Location
| Platform | Path |
|---|---|
| macOS / Linux | ~/.config/stacy/config.toml |
| Windows | %APPDATA%\stacy\config.toml |
Created automatically by stacy init if it doesn’t exist, or create it manually.
Full Reference
# Stata binary path (overrides auto-detection)
stata_binary = "/Applications/StataNow/StataMP.app/Contents/MacOS/stata-mp"
# Check for updates on startup (default: true)
# update_check = false
Fields
stata_binary
Override Stata auto-detection with an explicit path.
# macOS
stata_binary = "/Applications/StataNow/StataMP.app/Contents/MacOS/stata-mp"
# Linux
stata_binary = "/usr/local/stata18/stata-mp"
# Windows
stata_binary = "C:\\Program Files\\Stata18\\StataMP-64.exe"
stacy validates the path on load. If the file doesn’t exist, you’ll get an error with a hint to fix it.
Precedence for Stata binary resolution (highest first):
--engineCLI flagSTATA_BINARYenvironment variablestata_binaryin user config- Auto-detection from common install paths
update_check
Controls whether stacy checks for new releases on startup. Enabled by default.
# Disable update notifications
update_check = false
When enabled, stacy:
- Reads a local cache (
~/.cache/stacy/version-check.json) on startup - Prints a notification to stderr if a newer version is available
- Refreshes the cache in the background (every 24 hours)
The check never blocks or slows down commands. The background refresh uses the GitHub Releases API with a 3-second timeout.
Environment Variables
These environment variables affect stacy behavior independently of the config file:
| Variable | Effect |
|---|---|
STATA_BINARY | Stata binary path (overrides config file) |
STACY_NO_UPDATE_CHECK | Suppress update notifications (set to any value) |
CI | Suppresses update notifications automatically |
GITHUB_ACTIONS | Suppresses update notifications automatically |
Update Notification Suppression
Update notifications are automatically suppressed when:
update_check = falsein user configSTACY_NO_UPDATE_CHECKenvironment variable is setCIorGITHUB_ACTIONSenvironment variable is set- stderr is not a terminal (piped output, cron jobs, etc.)
The notification looks like:
Update available: v1.1.0 → v1.2.0
Run `brew upgrade stacy` to update
The upgrade instruction adapts to your install method (Homebrew, Cargo, or manual download).
Cache Directory
stacy stores cached data at:
| Platform | Path |
|---|---|
| macOS / Linux | ~/.cache/stacy/ |
| Windows | %LOCALAPPDATA%\stacy\cache\ |
Contents:
| File | Purpose |
|---|---|
packages/ | Global package cache (shared across projects) |
version-check.json | Last update check result |
update-available | Flag file for Stata-side notifications |
To clean everything: rm -rf ~/.cache/stacy (packages will be re-downloaded on next stacy install).
Examples
Minimal (just Stata path)
stata_binary = "/usr/local/stata18/stata-mp"
CI / headless server
stata_binary = "/usr/local/stata18/stata-mp"
update_check = false
Default (auto-detect everything)
An empty file or no file at all is valid. stacy auto-detects Stata and enables update checks.
See Also
- Project Config - Per-project settings (
stacy.toml) - stacy env - View resolved configuration
- stacy doctor - Diagnose configuration issues
- Installation - Install methods and Stata detection
Commands Overview
stacy provides commands for Stata execution, package management, and project workflows.
By Category
Execution
stacy run- Execute scripts with error detectionstacy bench- Benchmark script performancestacy task- Run tasks from stacy.tomlstacy test- Run tests
Packages
stacy add/remove/update- Manage dependenciesstacy install- Install from lockfilestacy list/outdated- View package statusstacy lock- Generate/verify lockfile
Project
stacy init- Initialize new projectstacy deps- Analyze script dependencies
Utility
stacy env- Show configurationstacy doctor- System diagnosticsstacy explain- Look up error codesstacy cache- Manage build cache
Getting Help
stacy --help # General help
stacy run --help # Command-specific help
See Exit Codes for exit code reference.
stacy run
Execute a Stata script with error detection
Synopsis
stacy run [SCRIPT] [OPTIONS]
Description
Executes Stata scripts in batch mode and parses log files for errors. Unlike
stata-mp -b, returns proper exit codes that reflect whether the script
succeeded or failed—enabling integration with Make, Snakemake, and CI/CD.
The command runs Stata with -b -q, parses the log for error patterns, and
returns an appropriate exit code (0 for success, 1-10 for various errors).
Program output (boilerplate-stripped) streams to stdout live as Stata writes
it — like Rscript or python — so stacy run foo.do > out.log and pipes
behave as expected. stacy’s own status and error messages go to stderr. On
failure, error details with official Stata documentation links and the log
file path are displayed. Use -v to stream the raw log instead, or -q to
suppress all output.
The batch log file is internal: removed on success, kept on failure with its
path printed in the failure output. Inside a project, kept logs go to [run] log_dir from stacy.toml (logs/ by default); outside one they stay next to
the run. The same rule applies to the scripts run by stacy task, stacy test
and stacy bench, and to every output format — --format json and --format stata report an empty log_file for a run that succeeded, because there is no
log to point at. Use --log <path> to keep the raw Stata log as a durable
artifact — it wins over log_dir and is written whether the run passed or
failed (--quiet --log out.log for a silent file-only run).
Multiple scripts can be run sequentially (default, fail-fast) or in parallel
(--parallel). Parallel mode runs all scripts regardless of failures.
To check a quick result without a script file, use stacy run -c 'display ...'.
In a project with a stacy.lock, run builds the ado-path from the lockfile and
checks it against the package cache before starting Stata. A cached package that
no longer hashes to the checksum the lockfile records fails the run instead of
executing, as does a production package that is not installed at all. dev and
test packages are only checked if they are installed, since stacy install
installs the production group by default. --no-verify skips the check.
Arguments
| Argument | Description |
|---|---|
<SCRIPT> | Script to execute |
Options
| Option | Description |
|---|---|
--allow-global | Allow globally installed packages |
--cache | Enable build cache (skip re-execution if script/deps unchanged) |
--cache-only | Fail if not in cache (useful for CI) |
--cd | Change to script’s parent directory |
-c, --code | Inline Stata code |
-C, --directory | Run Stata in this directory |
--engine | Stata engine to use (overrides config and auto-detection) |
--force | Force rebuild even if cached |
-j, --jobs | Max parallel jobs (default: CPU count) |
--log | Write the raw Stata log to this path |
--no-verify | Skip the check of the package cache against stacy.lock |
-P, --parallel | Run scripts in parallel |
--profile | Include execution metrics |
-q, --quiet | Suppress output |
--timeout | Kill script if it exceeds this many seconds |
--trace | Enable execution tracing at given depth |
--verbose | Extra output |
Examples
Run a script
stacy run analysis.do
Multiple scripts (sequential)
Runs in order, stops on first failure
stacy run clean.do analyze.do report.do
Parallel execution
Run all scripts concurrently for faster execution
stacy run --parallel *.do
stacy run --parallel -j4 a.do b.do c.do
Inline code
Execute Stata code without creating a file
stacy run -c 'display 2+2'
stacy run -c 'sysuse auto, clear
summarize price'
Working directory
Run in a specific directory (script paths resolved before cd)
stacy run -C reports/pilot/ table.do
stacy run --cd reports/pilot/table.do
Verbose output
Stream log file in real-time
stacy run -v long_analysis.do
JSON output
Machine-readable output for CI/CD
stacy run --format json analysis.do
Execution tracing
Enable Stata’s set trace on for debugging
stacy run --trace 2 analysis.do
stacy run --trace 2 -v analysis.do
Timeout
Kill script if it takes longer than 60 seconds
stacy run --timeout 60 long_analysis.do
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Stata error (r() code detected) |
| 2 | Syntax error |
| 3 | File error (not found, permission denied) |
| 4 | Memory error |
| 5 | Internal stacy error |
| 6 | Statistical error (convergence, model problems) |
| 10 | Environment error (Stata not found) |
See Exit Codes Reference for details.
See Also
stacy bench
Benchmark script execution
Synopsis
stacy bench <SCRIPT> [OPTIONS]
Description
Runs a Stata script multiple times and reports timing statistics (mean, median, min, max, stddev). Includes warmup runs by default to account for JIT and caching effects.
Arguments
| Argument | Description |
|---|---|
<SCRIPT> | Stata script to benchmark (required) |
Options
| Option | Description |
|---|---|
--no-warmup | Skip warmup runs |
-q, --quiet | Suppress progress output |
-n, --runs | Number of measured runs |
-w, --warmup | Number of warmup runs |
Examples
Benchmark a script
stacy bench analysis.do
Custom run count
stacy bench -n 20 analysis.do
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Script failed during benchmark |
| 3 | Script not found |
See Exit Codes Reference for details.
See Also
stacy init
Initialize new stacy project
Synopsis
stacy init <PATH> [OPTIONS]
Description
Creates a new stacy project with standard directory structure and configuration.
This sets up stacy.toml for project settings and ado/ for local packages.
Run this in an existing directory or specify a path to create a new one.
Arguments
| Argument | Description |
|---|---|
<PATH> | Project directory (default: current) |
Options
| Option | Description |
|---|---|
--force | Overwrite existing files |
-i, --interactive | Interactive mode: prompt for project details and packages |
Examples
Initialize in current directory
stacy init
Initialize in new directory
stacy init my-project
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Project created successfully |
| 1 | Initialization failed |
See Exit Codes Reference for details.
See Also
stacy add
Add packages to project
Synopsis
stacy add <PACKAGES> [OPTIONS]
Description
Adds packages to your project’s stacy.toml and installs them. Supports SSC
(default) and GitHub sources. Packages are recorded with versions for
reproducible installs via stacy install.
Arguments
| Argument | Description |
|---|---|
<PACKAGES> | Package names to add (required) |
Options
| Option | Description |
|---|---|
--dev | Add as development dependency |
--source | Package source: ssc or github:user/repo[@ref] |
--test | Add as test dependency |
Examples
Add from SSC
stacy add estout
stacy add estout reghdfe
Add from GitHub
stacy add --source github:sergiocorreia/ftools ftools
Add as dev dependency
stacy add --dev assert
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | A package failed to add |
See Exit Codes Reference for details.
See Also
stacy remove
Remove packages from project
Synopsis
stacy remove <PACKAGES>
Description
Removes packages from stacy.toml and deletes them from the local ado/
directory. Does not affect globally installed packages.
Arguments
| Argument | Description |
|---|---|
<PACKAGES> | Package names to remove (required) |
Examples
Remove a package
stacy remove estout
Remove multiple packages
stacy remove estout reghdfe
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | No packages removed |
See Exit Codes Reference for details.
See Also
stacy update
Update packages to latest versions
Synopsis
stacy update <PACKAGES> [OPTIONS]
Description
Checks for newer versions of installed packages and updates them. Updates both
stacy.toml and stacy.lock to reflect new versions. Use --dry-run to preview
changes without applying them.
A local: package lives in the project, so there is no source to check for a
newer version. It is reported as skipped and does not fail the command.
Arguments
| Argument | Description |
|---|---|
<PACKAGES> | Package names to update (default: all) |
Options
| Option | Description |
|---|---|
--dry-run | Show what would be updated without making changes |
Examples
Update all packages
stacy update
Update specific package
stacy update estout
Preview updates
stacy update --dry-run
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | A package failed to update, or its latest version could not be checked |
See Exit Codes Reference for details.
See Also
stacy install
Install packages from lockfile or SSC/GitHub
Synopsis
stacy install [OPTIONS]
Description
Installs the packages stacy.lock pins, at the versions and checksums it records.
install reads the lockfile; it never writes it. If a source no longer serves the
pinned version, or serves different bytes under it, the install fails and
stacy.lock is left untouched. Use stacy update to move a pin.
SSC serves only the current revision of a package, so a pinned version that has left the package cache cannot be downloaded again. A cold-cache install of a superseded pin fails rather than installing a different version under it.
The version pin is checked where the package names its own version. A .pkg
manifest with no Distribution-Date line names none, so for those packages the
checksum alone decides whether the pin is satisfied.
Options
| Option | Description |
|---|---|
--frozen | Fail if lockfile doesn’t match stacy.toml |
--no-verify | Skip checksum verification (a version the source names is still checked) |
--with | Include dependency groups (comma-separated: dev, test) |
Examples
Install from lockfile
Install all packages at locked versions
stacy install
Install specific package
stacy install estout
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | A package failed to install or failed checksum verification |
See Exit Codes Reference for details.
See Also
stacy list
List installed packages
Synopsis
stacy list [OPTIONS]
Description
Shows all packages installed in the current project with their versions and
sources. Use --tree to group by dependency type (production, dev, test).
Options
| Option | Description |
|---|---|
--tree | Group packages by dependency type |
Examples
List packages
stacy list
List by dependency group
stacy list --tree
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
See Exit Codes Reference for details.
See Also
stacy outdated
Check for package updates
Synopsis
stacy outdated
Description
Compares installed package versions against the latest available from their sources. Shows which packages have updates available without modifying anything.
Examples
Check for updates
stacy outdated
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | A package’s latest version could not be checked |
See Exit Codes Reference for details.
See Also
stacy lock
Generate or verify lockfile
Synopsis
stacy lock [OPTIONS]
Description
Generates stacy.lock from stacy.toml, recording exact versions of all packages.
The lockfile ensures reproducible installs across machines. Use --check in CI
to verify the lockfile is up-to-date.
Options
| Option | Description |
|---|---|
--check | Verify lockfile matches stacy.toml without updating |
--refresh | Recompute checksums from the packages installed in the global cache |
Examples
Generate lockfile
stacy lock
Verify lockfile (for CI)
stacy lock --check
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success / in sync |
| 1 | A package could not be resolved, or the lockfile is out of sync (with –check) |
See Exit Codes Reference for details.
See Also
stacy deps
Show dependency tree for Stata scripts
Synopsis
stacy deps <SCRIPT> [OPTIONS]
Description
Analyzes a Stata script to find all files it depends on (via do, run,
include) and package dependencies (via require). Shows a tree view of the
dependency graph, detects circular dependencies, and identifies missing files.
require statements (including cap require and capture require) are
recognized as package dependencies and shown as leaf nodes in the tree.
A path that holds a Stata macro, such as do "$root/prep.do", only points
somewhere once Stata expands the macro. stacy reads the script but does not run
it, so it lists the path as written and marks it as resolved at run time. Such a
path is not a missing file and does not fail the command.
Arguments
| Argument | Description |
|---|---|
<SCRIPT> | Script to analyze (required) |
Options
| Option | Description |
|---|---|
--flat | Show flat list instead of tree |
Examples
Show dependency tree
stacy deps main.do
Show flat list
stacy deps --flat main.do
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Analysis complete |
| 1 | Circular dependencies detected |
| 3 | Script not found, or a dependency is missing |
See Exit Codes Reference for details.
See Also
stacy task
Run tasks from stacy.toml
Synopsis
stacy task <TASK> [OPTIONS]
Description
Runs named tasks defined in stacy.toml. Tasks are like npm scripts—define
sequences of commands once and run them by name. Use --list to see available
tasks.
Arguments
| Argument | Description |
|---|---|
<TASK> | Task name to run |
Options
| Option | Description |
|---|---|
--frozen | Fail if lockfile doesn’t match stacy.toml |
--list | List available tasks |
Examples
Run a task
stacy task build
List available tasks
stacy task --list
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Task failed |
| 5 | Task not found |
See Exit Codes Reference for details.
See Also
stacy test
Run tests
Synopsis
stacy test <TEST> [OPTIONS]
Description
Discovers and runs test scripts from the tests/ or test/ directory. Tests
are Stata scripts that use assertion commands. Supports filtering, parallel
execution, and verbose output for debugging failures.
Each test runs with the project root as the working directory, so relative
paths in tests resolve from the project root regardless of where stacy test
is invoked. Use --directory <dir> to run tests in a specific directory, or
--cd to run each test in its own parent directory.
Arguments
| Argument | Description |
|---|---|
<TEST> | Specific test to run |
Options
| Option | Description |
|---|---|
--cd | Run each test in its own parent directory |
-C, --directory | Run tests in this directory |
-f, --filter | Filter tests by pattern |
--list | List tests without running |
--parallel | Run tests in parallel |
-q, --quiet | Suppress progress output |
-V, --verbose | Show full log context for failures |
Examples
Run all tests
stacy test
Run specific test
stacy test test_regression
Filter tests
stacy test -f 'regression*'
Run each test in its own directory
stacy test --cd
Exit Codes
| Code | Meaning |
|---|---|
| 0 | All tests passed |
| 1 | One or more tests failed |
| 5 | Test not found |
See Exit Codes Reference for details.
See Also
stacy cache info
Show cache statistics
Synopsis
stacy cache info
Description
Displays information about the build cache used by stacy run --cache. Shows
number of cached entries and approximate size. The cache stores results to
skip re-execution of unchanged scripts.
Use stacy cache clean to remove old entries.
Examples
Show cache info
stacy cache info
Clean old entries
stacy cache clean
stacy cache clean --older-than 7
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 10 | Not in project |
See Exit Codes Reference for details.
See Also
stacy env
Show environment configuration
Synopsis
stacy env
Description
Displays the current stacy configuration: Stata binary location, project root, path settings, and adopath order. Useful for debugging configuration issues.
Examples
Show environment
stacy env
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 10 | Environment error (Stata not found) |
See Exit Codes Reference for details.
See Also
stacy doctor
Run system diagnostics
Synopsis
stacy doctor [OPTIONS]
Description
Checks your system configuration and reports any issues. Verifies Stata installation, project detection, and write permissions. Run this first when troubleshooting.
Options
| Option | Description |
|---|---|
--refresh | Re-extract error codes from Stata |
Examples
Run diagnostics
stacy doctor
Exit Codes
| Code | Meaning |
|---|---|
| 0 | All checks passed |
| 1 | One or more checks failed |
See Exit Codes Reference for details.
See Also
stacy explain
Look up Stata error code details
Synopsis
stacy explain <CODE>
Description
Displays detailed information about Stata error codes. Includes the error name, category, full description from the Stata Programming Manual, and link to official documentation. Useful for understanding r() return codes.
Arguments
| Argument | Description |
|---|---|
<CODE> | Error code (e.g., 199 or r(199)) (required) |
Examples
Look up error code
stacy explain 199
Using r() syntax
stacy explain r(601)
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Error code found |
| 1 | Unknown error code |
See Exit Codes Reference for details.
See Also
How It Works
What actually happens when stacy runs a script or installs a package. stacy has four moving parts: an execution engine, an error detector, package management, and a build cache. This page walks through each, then covers the machine interface and stacy’s boundaries.
Contents
- Script Execution
- Error Detection
- Package Isolation
- Build Cache
- Output Streaming
- Machine Interface
- What stacy Does Not Do
Script Execution
stacy run script.do does four things:
┌───────────┐ ┌───────────┐ ┌─────────┐ ┌──────────┐
│ Build │ ──▶ │ Stata │ ──▶ │ Log │ ──▶ │ Exit │
│ S_ADO │ │ -b -q │ │ Parser │ │ Code 0-10│
└───────────┘ └───────────┘ └─────────┘ └──────────┘
from lockfile fresh process from the end translated
- Build the environment. If a lockfile is present, stacy constructs the
S_ADOsearch path from it, so Stata sees exactly the locked packages (see Package Isolation). - Run Stata. The script runs in a fresh batch-mode process (
-b -q). The-qflag skips yourprofile.do, so execution doesn’t depend on machine-specific startup configuration. - Parse the log. stacy reads the log Stata produced and determines whether the script succeeded (see Error Detection).
- Translate the outcome. Stata error codes become standard shell exit codes that any build tool understands.
Extras that matter in practice:
--timeout <seconds>kills a hung script (SIGTERM, then SIGKILL after a grace period) – useful for convergence loops on shared clusters.--parallelruns multiple scripts concurrently, each in its own Stata process; output prints as a grouped block per script on completion, and internal logs are uniquely named, somake -jand Snakemake can run same-named scripts safely.-c 'display ...'runs inline code without a script file.
Error Detection
The Problem
Stata’s batch mode always exits with code 0, even when scripts fail. Errors are only visible in log files.
stacy’s Solution
stacy parses the log from the end. It locates the last end of do-file marker – which corresponds to the outermost do-file in nested execution – and scans the lines after it for a return-code pattern (r(N);). Found: the script failed with that code. Not found: it succeeded.
This design handles the edge cases correctly:
| Scenario | Behavior |
|---|---|
Uncaptured error, e.g. r(601) | r(N); appears after the final marker – failure |
captured error | Doesn’t propagate past end of do-file – success |
| Error in a nested do-file | Propagates to the outermost marker – failure |
Script prints "r(199);" | Appears before the marker – ignored |
| Stata killed / crashed | No final marker at all – reported as error, never as success |
The error-detection logic is exercised by a test suite of 250+ cases covering nested do-files, captured errors, false positives from display output, and incomplete logs.
Error Descriptions
To describe an error rather than just number it, stacy extracts Stata’s own error descriptions from your installation at first run (stacy doctor --refresh re-extracts after a Stata upgrade). Where no description is available, it falls back to the documented range categories – see Exit Codes for the mapping.
Failures print a human-readable description plus a link to the official manual page, so you can diagnose a remote job from the error output alone:
FAIL broken.do (0.8s)
Error: r(199) - unrecognized command
See: https://www.stata.com/manuals/perror.pdf#r199
Package Isolation
The Problem
Stata packages install globally to ~/ado/plus/. Every project shares the same versions. When SSC updates a package, all projects change silently.
stacy’s Solution
stacy uses a global cache with per-project isolation:
~/.cache/stacy/packages/
├── estout/
│ ├── 2024.01.15/
│ │ └── estout.ado
│ └── 2024.03.15/
│ └── estout.ado
└── reghdfe/
└── 6.12.3/
└── reghdfe.ado
Multiple versions coexist in the cache, projects share it (disk-efficient), and cached packages install offline.
Runtime Isolation
When you run stacy run script.do, stacy reads stacy.lock, builds an S_ADO search path pointing at the exact cached versions, and launches Stata with it. Each project’s path is built from its own lockfile:
Project A (stacy.lock): Project B (stacy.lock):
estout = 2024.01.15 estout = 2024.03.15
reghdfe = 6.12.3 reghdfe = 6.12.3
Both use the same cache, but see different versions.
Two modes:
- Strict (default): only locked packages and Stata’s built-ins (
BASE) are visible. Nothing leaks in from your globalPLUSorPERSONALdirectories, so “works because of something installed on my machine” cannot happen. - Allow-global (
--allow-global): locked packages take precedence, but globally installed packages remain available. Useful during development or incremental migration.
Project-local .ado directories can be added to the path via the [paths] config section.
Lockfile Verification
The lockfile includes SHA256 checksums:
[packages.estout]
version = "2024.03.15"
checksum = "sha256:14af94e03edd..."
On stacy install, checksums are verified to ensure downloaded files match expected content, cached packages haven’t been modified, and SSC hasn’t silently updated the package. See Lockfile Format.
Build Cache
Pipelines often re-run scripts that haven’t changed. stacy run --cache skips that work:
- stacy hashes the script and every do-file it depends on (
do,run, andincludestatements, traced recursively – the same parser behindstacy deps). - If nothing changed since the last successful run, stacy replays the previous result (exit code, log path, duration) without launching Stata.
The cache is project-local (.stacy/cache/build.json) and opt-in. --force re-runs regardless; --cache-only fails when no cached result exists, letting CI require a pre-populated cache. Files outside the do-file graph – datasets, environment variables – are not tracked; use --force when they change.
Output Streaming
Real-time Output
Program output streams to stdout live by default – boilerplate-stripped (command echoes removed, blank runs collapsed), both in a terminal and when piped. Removed are the . command prompt, its > continuations, and the body lines of loops, programs and input. Results are kept as Stata printed them, including the ones that are shaped like echoes: list rows ( 1. | ... |), the . row of tabulate, missing, a value label that begins with . , and output that wraps onto a > line. A . prompt only counts as an echo where a command can start – never in the middle of a command’s output. Use -v (verbose) to stream the raw, unstripped log instead:
stacy run -v long_analysis.do
Streaming stops when the Stata process exits, so killed or timed-out runs terminate cleanly, and closed pipes (stacy run foo.do | head) end the stream without error.
Log Files
The batch log is internal: it gets a unique name per invocation (so concurrent runs never collide), is removed on success, and is kept on failure — with its path printed in the failure output so you can inspect it. --log <path> writes the raw log to a chosen location regardless of outcome. Machine-readable formats keep the log and report its path.
Progress Reporting
Without verbose mode, stacy shows periodic progress:
⠋ Running: analysis.do (45s elapsed)
Configure the interval in stacy.toml:
[run]
progress_interval_seconds = 30
Structured Logging
For automated pipelines, use --format json. Machine-readable formats imply quiet execution (no streaming). The batch log follows the same rule as in human mode — removed on success, kept on failure — so log_file names the kept log of a failed run and is empty for one that passed. Add --log <path> to keep the raw log either way:
stacy run --format json analysis.do
stacy run --format json --log run.log analysis.do
Machine Interface
stacy is designed to be a good Unix citizen: standard exit codes, machine-readable output, and no interactive surprises (update checks and colors are suppressed automatically in CI and piped output).
JSON Output
Every command supports --format json:
stacy run --format json analysis.do
stacy install --format json
stacy doctor --format json
See JSON Output for complete schemas.
Exit Codes
Stable, semantic exit codes for scripting:
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Stata error |
| 2 | Syntax error |
| 3 | File error |
| 4 | Memory error |
| 5 | Internal error |
| 6 | Statistical error |
| 10 | Environment error |
See Exit Codes for mapping details.
Build System Integration
stacy’s exit codes work with any tool that respects Unix conventions:
Make:
results.dta: analysis.do
stacy run analysis.do # Stops on non-zero
Shell scripts:
stacy run analysis.do || exit 1
CI pipelines:
- run: stacy run analysis.do # Fails job on error
Programmatic Usage
Python:
import subprocess, json
result = subprocess.run(
['stacy', 'run', '--format', 'json', 'analysis.do'],
capture_output=True
)
data = json.loads(result.stdout)
if not data['success']:
print(f"Failed: {data['errors']}")
R:
result <- system2("stacy", c("run", "--format", "json", "analysis.do"),
stdout = TRUE)
data <- jsonlite::fromJSON(paste(result, collapse = "\n"))
What stacy Does Not Do
Knowing the boundaries is as useful as knowing the features:
- It is not a build system. stacy decides whether a Stata step succeeded; Make, Snakemake, or statacons decide which steps run and in what order. They compose: point your build tool’s Stata rule at
stacy run. See Build Integration. - It does not manage the Stata version, data files, or other languages. The lockfile pins Stata packages. For full-stack reproducibility (OS, Stata itself, Python/R), use Docker – stacy works the same inside a container.
- It does not resolve transitive dependencies. SSC packages declare dependencies inconsistently, as free text, so automatic resolution would guess. If package A needs package B, add both:
stacy add A B.stacy doctorand post-install scanning warn about likely missing dependencies. - It does not replace interactive Stata. stacy wraps batch execution. For exploratory work, use Stata as usual – and run the finished script through
stacy runto verify it stands on its own.
See Also
- Exit Codes - Exit code reference
- JSON Output - JSON schemas
- Lockfile Format - Lockfile specification
- Stata Error Manual - Official documentation
Lockfile (stacy.lock)
Records installed package versions for reproducible environments. Auto-generated by stacy lock and stacy install.
Purpose
- Reproducibility: Ensures identical packages across machines and over time
- Verification: SHA256 checksums detect corruption or tampering
- Documentation: Records exact sources for each package
- Collaboration: Teammates get the same versions you tested with
Format Specification
The lockfile uses TOML format with a defined schema:
# Auto-generated by stacy. Do not edit manually.
version = "1" # Lockfile format version
[packages.<name>] # One section per package
version = "<version>" # Version string (date or semver)
checksum = "sha256:<hash>" # SHA256 of package contents
[packages.<name>.source] # Where the package came from
type = "SSC" | "GitHub" # Source type
name = "<name>" # Package name (SSC only)
repo = "<owner>/<repo>" # Repository (GitHub only)
tag = "<ref>" # Git ref (GitHub only)
Annotated Example
# Auto-generated by stacy. Do not edit manually.
# Lockfile format version - stacy checks compatibility
version = "1"
# ─────────────────────────────────────────────────────────────
# SSC Package
# ─────────────────────────────────────────────────────────────
[packages.estout]
# Version comes from SSC metadata (typically a date)
version = "2024.03.15"
# SHA256 hash of all .ado and .sthlp files concatenated
checksum = "sha256:14af94e03edd2e5f12021a8967afe1eee2dc7ebd..."
[packages.estout.source]
type = "SSC"
name = "estout"
# ─────────────────────────────────────────────────────────────
# GitHub Package
# ─────────────────────────────────────────────────────────────
[packages.reghdfe]
# Version from git tag or commit
version = "6.12.3"
checksum = "sha256:8f9234ab12cd56ef78901234567890abcdef..."
[packages.reghdfe.source]
type = "GitHub"
repo = "sergiocorreia/reghdfe"
# Tag, branch, or commit SHA
tag = "v6.12.3"
What the lockfile guarantees
The lockfile is an input to stacy install and stacy run, never an output of them:
stacy installinstalls what the lockfile pins. It does not re-resolve versions and it does not writestacy.lock. If the source no longer serves the pinned version, or serves different bytes under it, the install fails and the lockfile is left untouched. This holds with and without--frozen.stacy runchecks the cache against the lockfile before it starts Stata. If a cached package no longer hashes to the locked checksum, or a production package is not installed at all, the run fails instead of executing.- Only
stacy add,stacy update, andstacy lockwritestacy.lock. Moving a pin is an explicit act.
Which packages run requires
stacy install installs the production group by default, so that is the group run requires to be present: a locked production package that is not in the cache fails the run. dev and test packages are installed on request (stacy install --with dev,test), so run does not require them — but it does check them against their locked checksums when they are installed, since every locked package is on the ado-path.
A pinned version can become unfetchable
SSC serves only the current revision of a package. Once a pinned version leaves your package cache, it cannot be downloaded again. A cold-cache install of a superseded pin therefore fails:
estout: stacy.lock pins version 20240315, but SSC serves 20260413
SSC serves only the current revision of a package, so once a pinned version
leaves the package cache it cannot be downloaded again.
hint: run `stacy update estout` to move the pin to 20260413 (your results may
change), or restore 20240315 into the package cache from a machine that
still has it.
This is a real limit of SSC, not something stacy can work around. The choice is to fail loudly or to run a different package than the one you locked; stacy fails.
The version pin is only enforced where the package names its own version. A .pkg manifest without a Distribution-Date line names none — stacy add records the date it fetched the package, which says nothing about the contents — so for those packages the checksum decides whether the pin is satisfied. A cold-cache install of such a package succeeds as long as the bytes still hash to the locked checksum, whatever the date in the lockfile says.
How Checksums Work
Checksums verify that the installed package matches exactly what was recorded:
- On install: stacy downloads the package, computes SHA256 of the contents
- On lock: Computed hash is stored in
stacy.lock - On verify: Cached package is re-hashed and compared to lockfile
The checksum covers all .ado and .sthlp files in the package, sorted and concatenated. This catches:
- Corrupted downloads
- SSC updates that changed the package
- Manual modifications to cached files
Checksums are checked by stacy install and again by stacy run before every run. Verification on run is on by default: it reads and hashes the locked packages, which costs milliseconds against a Stata startup measured in seconds, and a default that silently runs modified code would not be a reproducibility guarantee.
--no-verify turns checksum verification off. It applies to both commands, and it has to: stacy install --no-verify caches whatever the source served, which by definition does not match the locked checksum, so stacy run on that cache needs --no-verify too. Prefer stacy update <package> to re-lock, and expect your results to change.
Fields Reference
| Field | Required | Description |
|---|---|---|
version | Yes | Lockfile format version (currently “1”) |
packages.<name>.version | Yes | Package version string |
packages.<name>.checksum | Yes | SHA256 hash prefixed with sha256: |
packages.<name>.source.type | Yes | "SSC" or "GitHub" |
packages.<name>.source.name | SSC only | Package name on SSC |
packages.<name>.source.repo | GitHub only | owner/repo format |
packages.<name>.source.tag | GitHub only | Git ref (tag, branch, or commit) |
Workflow
Creating a lockfile
# Add packages (creates/updates lockfile automatically)
stacy add estout reghdfe
# Or generate lockfile from existing stacy.toml
stacy lock
Installing from a lockfile
# Clone a project
git clone https://github.com/user/project
cd project
# Install exact versions from lockfile
stacy install
Verifying in CI
# Fails if lockfile doesn't match stacy.toml
stacy lock --check
Updating packages
# Update one package to latest
stacy update reghdfe
# Update all packages
stacy update
Version Control
| File | Commit? | Why |
|---|---|---|
stacy.toml | Yes | Declares dependencies |
stacy.lock | Yes | Ensures reproducibility |
~/.cache/stacy/packages/ | No | Cache, not source |
Always commit both stacy.toml and stacy.lock. The lockfile is what ensures everyone gets the same package versions.
Troubleshooting
“Lockfile out of sync”
The lockfile doesn’t match stacy.toml:
stacy lock # Regenerate
“Checksum mismatch”
The cached package differs from what’s in the lockfile:
stacy cache packages clean # Clear cache
stacy install # Re-download
If the re-download also mismatches, the source changed the package without changing its version. Run stacy update <package> to re-lock it, and expect your results to change.
“The package cache does not match stacy.lock”
stacy run reports this when a locked production package is missing from the cache, or when any locked package has been modified since it was installed:
stacy install # Install the locked packages
stacy cache packages clean # ...or clear a modified cache first
A cached package that no longer hashes to its locked checksum is reported as modified whatever group it is in, since every locked package is on the ado-path.
Merge conflicts in lockfile
After a git merge with conflicts:
# Resolve stacy.toml conflicts first, then:
stacy lock # Regenerate lockfile
See Also
- stacy install - Install packages from lockfile
- stacy lock - Generate/verify lockfile
- stacy add - Add packages
- Project Config - The stacy.toml file
Exit Codes
stacy uses consistent exit codes to indicate success or failure type.
Exit Code Table
| Code | Name | Description |
|---|---|---|
| 0 | Success | Operation completed successfully |
| 1 | Stata Error | Stata r() code detected in log |
| 2 | Syntax Error | Invalid Stata syntax |
| 3 | File Error | File not found, permission denied, data errors |
| 4 | Memory Error | Insufficient memory |
| 5 | Internal Error | stacy itself failed (not Stata) |
| 6 | Statistical Error | Convergence failure, model problems |
| 10 | Environment Error | Stata not found or configuration invalid |
Stata r() Code Mapping
The number inside r(N) is preserved in stacy’s output (JSON field r_code, stored result r(exit_code) in Stata). The shell exit code is a category derived from it, in two steps:
- Error database lookup. stacy extracts error descriptions and categories from your local Stata installation (run
stacy doctor --refreshafter a Stata upgrade). If the code is found there, its category decides the exit code. - Range fallback. Otherwise, the documented ranges from Stata’s Programming Reference Manual apply:
| Exit Code | Stata r() Codes |
|---|---|
| 1 | all r() codes not in other categories |
| 2 | r(100)-r(199), e.g. r(198), r(199) |
| 3 | r(600)-r(699), e.g. r(601), r(603) |
| 4 | r(900)-r(999), e.g. r(950) |
| 6 | r(400)-r(499) |
| 10 | r(800)-r(899) |
Usage
Shell
stacy run analysis.do
echo $? # 0 on success, 1-10 on failure
Makefile
results.dta: analysis.do
stacy run analysis.do # Stops on non-zero exit
This mapping is many-to-one by design: it compresses Stata’s hundreds of return codes into a small, stable set that build tools can branch on.
Stability
Exit codes 0-10 are stable and will not change meaning. New categories may be added with codes 11+.
See Also
JSON Output
stacy supports JSON output for machine-readable results.
Usage
Add --format json to any command:
stacy run --format json analysis.do
stacy install --format json
stacy env --format json
Output Schemas
stacy run
Success:
{
"success": true,
"script": "analysis.do",
"duration_secs": 12.45,
"exit_code": 0,
"log_file": ""
}
Failure:
{
"success": false,
"script": "analysis.do",
"duration_secs": 0.45,
"exit_code": 2,
"log_file": "/path/to/wd/analysis_12345_1715000000123_0.log",
"errors": [
{
"type": "StataCode",
"r_code": 199,
"name": "unrecognized command",
"line": 15,
"context": "reghdfe price mpg, absorb(make)"
}
]
}
| Field | Type | Description |
|---|---|---|
success | bool | Whether script completed without errors |
script | string | Path to script that was run |
duration_secs | float | Execution time in seconds |
exit_code | int | stacy exit code (0-10) |
log_file | string | Absolute path to the kept Stata log, empty when the run succeeded (a successful run’s log is removed). Each invocation gets a unique stem (<script>_<pid>_<nanos>_<n>.log) so concurrent runs from a shared cwd never collide. Pass --log <path> to keep the log of a passing run; log_file then reports that path. |
errors | array | Error details (only on failure) |
errors[].type | string | Error type (StataCode, Syntax, File) |
errors[].r_code | int | Stata r() code if applicable |
errors[].name | string | Human-readable error name |
errors[].line | int | Line number if detected |
errors[].context | string | Code that caused the error |
stacy install
{
"success": true,
"installed": [
{
"name": "estout",
"version": "2024.03.15",
"source": "SSC"
},
{
"name": "reghdfe",
"version": "6.12.3",
"source": "GitHub"
}
],
"already_cached": [
{
"name": "ftools",
"version": "2.49.0",
"source": "SSC"
}
],
"failed": []
}
| Field | Type | Description |
|---|---|---|
success | bool | Whether all packages installed |
installed | array | Packages downloaded this run |
already_cached | array | Packages found in cache |
failed | array | Packages that failed to install |
stacy list
{
"packages": [
{
"name": "estout",
"version": "2024.03.15",
"source": "SSC",
"locked": true
},
{
"name": "reghdfe",
"version": "6.12.3",
"source": "GitHub",
"locked": true
}
]
}
stacy env
{
"stata": {
"binary": "/Applications/StataNow/StataMP.app/Contents/MacOS/stata-mp",
"version": "18.0",
"flavor": "MP",
"source": "user config"
},
"project": {
"root": "/Users/user/projects/analysis",
"has_config": true,
"has_lockfile": true
},
"cache": {
"path": "/Users/user/.cache/stacy/packages",
"package_count": 12
}
}
stacy doctor
{
"ready": true,
"checks": [
{
"name": "Stata binary",
"status": "ok",
"message": "Found at /Applications/StataNow/StataMP.app/Contents/MacOS/stata-mp"
},
{
"name": "Stata version",
"status": "ok",
"message": "Stata 18.0 MP"
},
{
"name": "Project config",
"status": "ok",
"message": "Found stacy.toml"
},
{
"name": "Lockfile",
"status": "warning",
"message": "No stacy.lock found"
}
]
}
| Status | Meaning |
|---|---|
ok | Check passed |
warning | Non-blocking issue |
error | Blocking issue |
stacy deps
{
"status": "success",
"script": "master.do",
"dependencies": {
"path": "master.do",
"type": null,
"exists": true,
"is_circular": false,
"is_unresolved": false,
"line_number": null,
"children": [
{
"path": "config/settings.do",
"type": "do",
"exists": true,
"is_circular": false,
"is_unresolved": false,
"line_number": 3,
"children": []
},
{
"path": "reghdfe",
"type": "require",
"exists": true,
"is_circular": false,
"is_unresolved": false,
"line_number": 5,
"children": []
}
]
},
"summary": {
"unique_count": 2,
"has_circular": false,
"has_missing": false,
"circular_paths": [],
"missing_paths": [],
"unresolved_paths": [],
"circular_count": 0,
"missing_count": 0,
"unresolved_count": 0
}
}
status is success when the graph resolved and error when a dependency is
missing or circular — branch on it rather than on the summary counts.
is_unresolved marks a path that holds a Stata macro, such as
do "$root/prep.do". stacy reads scripts but does not run them, so it cannot
say where the path points. Such a path is listed but not looked up, and it does
not fail the command.
jq Examples
Check if a run succeeded
stacy run --format json analysis.do | jq '.success'
Get exit code
stacy run --format json analysis.do | jq '.exit_code'
Extract error codes
stacy run --format json analysis.do | jq '.errors[]?.r_code'
List installed package names
stacy list --format json | jq -r '.packages[].name'
Get Stata binary path
stacy env --format json | jq -r '.stata.binary'
Check if project has lockfile
stacy env --format json | jq '.project.has_lockfile'
Find failed doctor checks
stacy doctor --format json | jq '.checks[] | select(.status == "error")'
Count dependencies
stacy deps --format json master.do | jq '.files | length'
Get packages that need downloading
stacy install --format json | jq '.installed[].name'
Using JSON in Scripts
Shell
#!/bin/bash
result=$(stacy run --format json analysis.do)
if echo "$result" | jq -e '.success' > /dev/null; then
echo "Success!"
else
echo "Failed with errors:"
echo "$result" | jq -r '.errors[].name'
exit 1
fi
Python
import subprocess
import json
result = subprocess.run(
['stacy', 'run', '--format', 'json', 'analysis.do'],
capture_output=True,
text=True
)
data = json.loads(result.stdout)
if data['success']:
print(f"Completed in {data['duration_secs']:.2f}s")
else:
for error in data.get('errors', []):
print(f"Error r({error['r_code']}): {error['name']}")
R
library(jsonlite)
result <- system2("stacy", c("run", "--format", "json", "analysis.do"),
stdout = TRUE, stderr = TRUE)
data <- fromJSON(paste(result, collapse = "\n"))
if (data$success) {
cat(sprintf("Completed in %.2fs\n", data$duration_secs))
} else {
cat("Errors:\n")
print(data$errors)
}
Stability
The JSON schema follows semantic versioning:
- Core fields (
success,exit_code,errors) are stable from v1.0 - New fields may be added in minor versions (backward compatible)
- Field removal or type changes only in major versions
Tip: Use jq’s
-eflag to handle missing fields gracefully in scripts.
See Also
- stacy run - Running scripts
- Build Integration - CI/CD and build tools
- Exit Codes - Exit code meanings
Build Integration
stacy integrates with build systems through standard Unix exit codes. Any tool that stops on non-zero exit works with stacy.
Quick Examples
Make:
results.dta: analysis.do data.dta
stacy run analysis.do
Snakemake:
rule analysis:
input: "analysis.do", "data.dta"
output: "results.dta"
shell: "stacy run {input[0]}"
Shell:
stacy run step1.do && stacy run step2.do
GNU Make
Basic Makefile
STATA := stacy run
# Final output depends on analysis
results/tables.tex: src/03_tables.do results/estimates.dta
$(STATA) $<
# Estimates depend on clean data
results/estimates.dta: src/02_analysis.do data/clean.dta
$(STATA) $<
# Clean data depends on raw data
data/clean.dta: src/01_clean.do data/raw.dta
$(STATA) $<
.PHONY: all clean
all: results/tables.tex
clean:
rm -f data/clean.dta results/*.dta results/*.tex
With Package Installation
.PHONY: install
install:
stacy install
results.dta: analysis.do | install
stacy run $<
See GNU Make documentation for more patterns.
Snakemake
Basic Snakefile
rule all:
input: "results/tables.tex"
rule clean:
input: "src/01_clean.do", "data/raw.dta"
output: "data/clean.dta"
shell: "stacy run {input[0]}"
rule analysis:
input: "src/02_analysis.do", "data/clean.dta"
output: "results/estimates.dta"
shell: "stacy run {input[0]}"
rule tables:
input: "src/03_tables.do", "results/estimates.dta"
output: "results/tables.tex"
shell: "stacy run {input[0]}"
Parallel Execution
snakemake --cores 4
See Snakemake documentation for workflows, clusters, and more.
CI/CD
GitHub Actions
# .github/workflows/analysis.yml
name: Analysis
on: [push, pull_request]
jobs:
build:
runs-on: self-hosted # With Stata installed
steps:
- uses: actions/checkout@v4
- name: Install stacy
run: |
curl -fsSL https://stacy.janfasnacht.com/install.sh | bash
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Install packages
run: stacy install --frozen
- name: Run analysis
run: stacy run analysis.do
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: results
path: output/
Note:
--frozenfails if lockfile doesn’t match stacy.toml, catching uncommitted dependency changes.
GitLab CI
# .gitlab-ci.yml
analysis:
stage: build
before_script:
- curl -fsSL https://stacy.janfasnacht.com/install.sh | bash
- export PATH="$HOME/.local/bin:$PATH"
- stacy install
script:
- stacy run analysis.do
artifacts:
paths: [output/]
Caching Packages
- uses: actions/cache@v4
with:
path: ~/.cache/stacy/packages/
key: stata-packages-${{ hashFiles('stacy.lock') }}
Stata Licensing in CI
Stata requires a license. Options:
- Self-hosted runner with Stata installed
- Docker container with Stata
- Skip Stata steps in CI (validate config only)
See GitHub Actions docs or GitLab CI docs for more.
Best Practices
- Use
--frozenin CI to catch lockfile drift - Commit
stacy.lockfor reproducibility - Cache packages to speed up builds
- Use JSON output for programmatic checks:
stacy run --format json - Upload artifacts on failure for debugging
See Also
- How It Works - Exit codes and JSON
- Exit Codes - Code meanings
Migration Guide
How to adopt stacy in existing Stata projects.
Overview
stacy works with existing Stata scripts unchanged. Migration is incremental–start with error detection, add package management when ready.
| Current workflow | stacy equivalent |
|---|---|
stata -b do script.do | stacy run script.do |
ssc install pkg | stacy add pkg |
master.do | [scripts] section |
From Batch Mode to stacy run
Before
stata-mp -b do analysis.do
# Always exits 0, even on error
# Must manually check analysis.log
After
stacy run analysis.do
# Exits 1-10 on error
# Shows error with documentation link
What changes:
- Exit codes now reflect success/failure
- Errors display with Stata documentation links
- Build systems (Make, Snakemake) can detect failures
What stays the same:
- Your
.dofiles work unchanged - Output goes to the same log file
- Stata runs in the background
Updating scripts
For shell scripts that check logs:
# Before: parse log for errors
stata-mp -b do analysis.do
if grep -q "^r(" analysis.log; then
echo "Error!"
exit 1
fi
# After: just check exit code
stacy run analysis.do
if [ $? -ne 0 ]; then
echo "Error!"
exit 1
fi
# Or more simply
stacy run analysis.do || exit 1
From ssc install to Lockfiles
Before
* At the top of master.do or a setup script
ssc install estout
ssc install reghdfe
Problems:
- Different versions on different machines
- “It worked last month” failures
- No record of what’s actually installed
After
# One-time setup
stacy init
stacy add estout reghdfe
# Creates stacy.toml (what you want) and stacy.lock (what you have)
git add stacy.toml stacy.lock
git commit -m "Add stacy package management"
For collaborators:
git pull
stacy install # Gets exact same versions
Step-by-step migration
-
List current packages
ado dir -
Initialize stacy
stacy init -
Add each package
stacy add estout reghdfe ftools -
Remove ssc install lines from scripts Delete or comment out
ssc installcommands–stacy handles this now. -
Commit both files
git add stacy.toml stacy.lock git commit -m "Switch to stacy package management"
Handling GitHub packages
If you install from GitHub:
* Before
net install reghdfe, from("https://raw.githubusercontent.com/sergiocorreia/reghdfe/master/src/")
# After
stacy add github:sergiocorreia/reghdfe
From master.do to [scripts]
Before
* master.do
do "01_clean_data.do"
do "02_analysis.do"
do "03_tables.do"
Problems:
- Running one script requires editing master.do
- No parallelization
- Error in script 2 still runs script 3 (unless you add
capturelogic)
After
Add to stacy.toml:
[scripts]
clean = "01_clean_data.do"
analysis = "02_analysis.do"
tables = "03_tables.do"
all = ["clean", "analysis", "tables"]
Run individual tasks or sequences:
stacy task clean # Run just cleaning
stacy task analysis # Run just analysis
stacy task all # Run all in order
Benefits:
- Named tasks are self-documenting
- Each task stops on error by default
- Can run tasks individually for debugging
Keeping master.do
You don’t have to remove master.do. Both can coexist:
# Using stacy tasks
stacy task all
# Or using master.do through stacy (still get exit codes)
stacy run master.do
From Make to Make + stacy
If you already use Make:
Before
%.log: %.do
stata-mp -b do $<
After
%.log: %.do
stacy run $<
That’s it. Make now stops on Stata errors.
Adding package management
# Ensure packages are installed before running
.PHONY: install
install:
stacy install
results/analysis.dta: analysis.do install
stacy run analysis.do
Checklist
Minimal migration (exit codes only)
- Install stacy
- Replace
stata -b dowithstacy runin scripts/Makefile - Verify
stacy doctorpasses
Full migration (packages + tasks)
- Run
stacy init - Add packages with
stacy add - Remove
ssc installlines from scripts - Add
[scripts]section for common tasks - Commit
stacy.tomlandstacy.lock - Update CI to run
stacy installbefore tests - Tell collaborators to run
stacy installafter pulling
See Also
- Quick Start - Getting started guide
- Project Config - stacy.toml reference
- Build Integration - Make, Snakemake, CI/CD
FAQ
Getting Started
Do I need to change my Stata scripts?
No. stacy runs your existing .do files unchanged.
Can I use stacy with existing projects?
Yes. Run stacy init in any directory to create stacy.toml. Existing scripts work as before.
What if I don’t use Make or Snakemake?
stacy works standalone. You get error detection, lockfile packages, and the task runner:
[scripts]
clean = "clean_data.do"
analysis = "analysis.do"
all = ["clean", "analysis"]
stacy task all
How is stacy different from batch mode?
| Batch mode | stacy |
|---|---|
| Always exits 0 | Exits 1-10 on errors |
| Errors in log only | Errors shown with docs link |
| No package management | Lockfile pins versions |
Package Management
How is the lockfile different from net install?
net install gets whatever version exists today. The lockfile records exact versions with checksums. stacy install reproduces those exact versions.
Can I use packages not on SSC?
Yes:
stacy add github:sergiocorreia/reghdfe
stacy add github:user/repo@v1.2
What happens if SSC is down?
Packages are cached at ~/.cache/stacy/packages/. Cached packages work offline.
Where are packages stored?
Global cache at ~/.cache/stacy/packages/, organized by name/version. stacy sets S_ADO at runtime for project isolation.
Technical
How does stacy detect errors?
Runs Stata with -b -q, parses the log for r() patterns, returns appropriate exit code. See How It Works.
Does stacy modify my Stata installation?
No. stacy manages packages in its own cache and sets S_ADO at runtime.
What are the exit codes?
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Stata error |
| 2 | Syntax error |
| 3 | File error |
| 4 | Memory error |
| 5 | Internal error |
| 6 | Statistical error |
| 10 | Environment error |
Updates
Does stacy check for updates automatically?
Yes. On startup, stacy checks a local cache for available updates and prints a notification to stderr. The cache is refreshed in the background every 24 hours via the GitHub Releases API. This never blocks or slows down commands.
How do I disable update checks?
Either set update_check = false in ~/.config/stacy/config.toml, or set the STACY_NO_UPDATE_CHECK environment variable. Update checks are also suppressed automatically in CI and non-interactive environments. See User Config for details.
Compatibility
Does stacy work on Windows?
Yes. Windows, macOS, and Linux.
Which Stata versions are supported?
Stata 14+ (MP, SE, BE, StataNow).
Can I use stacy in Docker?
Yes. Set STATA_BINARY environment variable.
Can I use stacy without leaving Stata?
Yes. Installing the Stata wrappers (net install stacy, from("https://stacy.janfasnacht.com/stata")) makes every command available from the Stata console: stacy run analysis.do, stacy add estout, and so on, with help stacy documentation. Under the hood, stacy run still executes your script in a fresh batch-mode Stata process – that separate process is what makes the outcome observable and the environment isolated.
Troubleshooting
Installation
stacy: command not found
Add ~/.local/bin to PATH:
export PATH="$HOME/.local/bin:$PATH" # add to ~/.zshrc
Restart terminal.
Installation script fails
mkdir -p ~/.local/bin
curl -fsSL https://stacy.janfasnacht.com/install.sh | bash
macOS blocks the binary
xattr -d com.apple.quarantine ~/.local/bin/stacy
Stata Detection
Stata not found
Set path explicitly:
export STATA_BINARY=/path/to/stata-mp
Or create ~/.config/stacy/config.toml:
stata_binary = "/path/to/stata-mp"
Run stacy doctor to verify.
Wrong Stata version detected
stacy env # see what was found
export STATA_BINARY=/path/to/correct/stata-mp
Permission denied running Stata
chmod +x /path/to/stata-mp
Runtime
Exit code doesn’t match error
Possible causes:
- Script uses
captureto suppress errors - stacy only catches
r()errors, not warnings - Custom program writes non-standard messages
Script works in GUI but fails with stacy
Working directory: stacy runs from current shell directory.
Missing packages: Run stacy add packagename.
Profile.do: stacy uses -q flag, skipping profile.do.
Log file not found
Check write permissions. Try stacy run -v script.do for verbose output.
Packages
Failed to download from SSC
Check network: curl -I https://www.stata.com
Retry: stacy add packagename
Checksum mismatch
SSC updated the package: stacy update packagename
Or clear cache: stacy cache packages clean && stacy install
Package installed but Stata can’t find it
stacy list # verify it's listed
Check package docs for unlisted dependencies.
Lockfile
Conflicts after git merge
Resolve stacy.toml conflicts first, then:
stacy lock
Lockfile out of sync
stacy lock # regenerate
stacy lock --check # verify
Different results on teammate’s machine
- Verify lockfile committed:
git status stacy.lock - Both have same lockfile:
git diff origin/main -- stacy.lock - Reinstall:
stacy install - Check Stata versions:
stacy env
Update Notifications
How do I disable update notifications?
Set update_check = false in ~/.config/stacy/config.toml:
update_check = false
Or set an environment variable:
export STACY_NO_UPDATE_CHECK=1
Notifications don’t appear
Update notifications are suppressed when:
- stderr is not a terminal (piped output, cron, scripts)
CIorGITHUB_ACTIONSenvironment variable is setSTACY_NO_UPDATE_CHECKenvironment variable is setupdate_check = falsein user config
If you want to check manually: stacy --version and compare with the releases page.
Notification shows wrong upgrade command
stacy detects the install method from the binary path. If detection is wrong (e.g., after moving the binary), the fallback shows the GitHub releases URL.
Getting Help
- Run
stacy doctor - Run failing command with
-v - Open an issue
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[1.5.0] - 2026-07-13
Commands that could not finish their work used to exit 0. They now exit nonzero, which will surface failures a script or CI step previously ran past. See Changed.
Changed
- Package commands exit nonzero when their work did not complete, and report a non-success
statusin--format json/stata.install,lock,outdated,add,updateanddepsall treated a resolve, install or version-check failure as a warning and exited 0 (#94). stacy.tomlrejects unknown keys, naming the offending one. A misplaced or misspelled key — a dependency under[dependencies]instead of[packages.dependencies], a typo’dverisonpin — was dropped without a word (#100).[packages] ado_diris rejected along with them. Nothing has ever read it; local ado directories are[paths] ado. Remove the key if yourstacy.tomlcarries it (#100).stacy installno longer writesstacy.lock. It installs the pinned version and fails when the source no longer serves it, instead of re-resolving and moving the pin. SSC serves only the current revision, so a cold-cache install of a superseded pin now fails rather than silently installing something else (#96).stacy runchecks every locked package against the cache before starting Stata and fails on a missing or modified one.run --no-verifyskips the check (#97).- A successful run removes its log in every output format.
--format json/statakept it, so each in-Statastacy_runleft one behind (#98).
Fixed
stacy runno longer drops streamed output that looks like a command echo. Echo detection ran on the trimmed line, so indented result lines went with it: every data row oflist, the missing-value row oftabulate, missing,display "1. step". The log was never affected (#95).[run] log_diris honored. Per-script logs were written to the project root and kept after a successful run (#98).stacy envchecks the cache before calling a locked package installed. A cold cache reported every package as installed (#99).stacy add <name> --source local:<dir>requires the directory to hold the named package instead of installing whatever it contains (#100).stacy doctorno longer reports a package’s own shipped files as missing dependencies. ftools’ internal.matafiles were listed as missing SSC packages, with astacy addtip that could not work (#101).- A task that defines no work — a table without
scriptorparallel(e.g. a typo’d key, which TOML parsing silently drops) or an empty array — now fails with a config error instead of succeeding as a no-op (#92).
[1.4.0] - 2026-07-13
Added
- Post-install dependency hints now also read the
Requires:line of a package’s.pkgmanifest, catching author-declared SSC dependencies that static.adoscanning misses (#78). stacy addwarns when a package’s manifest declares a newer minimum Stata version than the one stacy last detected (#82).stacy test -C <dir>/--directoryand--cd: control the test working directory, matchingstacy run(#85).
Changed
stacy testruns tests with the project root as the working directory regardless of where it’s invoked (previously the inherited directory) (#85).
Fixed
.pkgmanifests with bare\r(classic-Mac) line endings no longer parse as a title with no files (#79).stacy taskfrom the Stata console no longer fails withr(199): machine-readable formats no longer stream script output to stdout (#84).- Line breaks in
--format statastring values are replaced with spaces.
[1.3.1] - 2026-07-10
Added
stacy lock --refresh: recompute lockfile checksums from the installed cache.
Fixed
- Packages whose
.pkgmanifest lists a file twice (e.g. reghdfe) no longer fail checksum verification (#68). Runstacy lock --refreshto repair lockfile entries recorded by older versions.
[1.3.0] - 2026-07-09
Changed
stacy runstreams program output to stdout live in piped mode, likeRscript(#24). Same content as before, just live; status and errors stay on stderr.stacy taskstreams too.- The log file is now internal: removed on success, kept on failure. Machine-readable formats keep it. Use
--log <path>for a durable artifact. --parallelprints each script’s output as a grouped block on completion instead of discarding it.
Added
stacy run --log <path>: write the raw Stata log to a chosen path (works with--quietfor silent file-artifact mode).
Removed
- Dead
log_reader::is_successful_completion(unused since the streaming rework, #65).
Fixed
- Task arrays accept script paths:
all = ["clean", "src/02_analyze.do"](#64). - Post-install hints comma-separate package names (#63).
- Failure context no longer loads the entire log into memory (#66).
- Log streaming no longer hangs when Stata is killed or fails to launch, recovers from truncated logs, and survives closed pipes (
| head). --traceno longer leaks its temp script and log.
[1.2.1] - 2026-05-06
Added
- Stata wrappers now verify that the
stacybinary they invoke is version-compatible (#35). On mismatch,_stacy_execaborts with a clear error and astacy_setup, forcehint instead of silently running against a stale binary. The check shells out once per Stata session (cached in$stacy_version_checked).
Fixed
- Surface Stata’s stderr on launch failures instead of the misleading “Log file incomplete” (#21). Distinguish “no log produced” (launch failure) from “log truncated” (killed mid-run).
stacy install --format stata(and--format json) no longer emits a success-shaped block when checksum verification fails (#38). Status is now computed before output, so wrappers seeglobal stacy_status "error"plusglobal stacy_error "<msg>"(and JSON gets matchingstatus/error/failedfields) instead of a stale success preceding the non-zero exit.- Stata wrappers failed with
command _stacy_check_version is unrecognizedbecause the generated_stacy_compat.adodefined programs that didn’t match its filename and wasn’t listed instacy.pkg(#37). Split into one file per program. - Parallel
stacy runinvocations on scripts that share a basename no longer collide on the log file (#20). Each run writes to a uniquely-named log in the working directory (<stem>_<pid>_<nanos>_<n>.log), so build orchestrators like Make-jand Snakemake can run same-stemmed scripts from a shared cwd safely. The path is reported in JSON output’slog_filefield. - Non-UTF-8 characters in Stata log files no longer crash the log parser
- Update check now compares against the running binary version, not a stale cached value
- Cargo upgrade instruction now shows correct crate name (
stacy, notstata-cli)
1.2.0 - 2026-03-15
Added
stacy run --timeout <seconds>to kill long-running scripts[paths]config section for project-local ado directories inS_ADO- Post-install dependency scanning: warn about missing implicit dependencies after
stacy addand instacy doctor - Package naming hints: suggest correct SSC package on 404 (e.g.
labmask→labutil) - Stata wrappers now expose all CLI flags (
AllowGlobal,Trace,Timeout,Parallel,Cache, etc.)
Changed
stacy initgenerates minimal config (no default values the user will delete)- Dependencies in
stacy.tomlsorted alphabetically stacy initandstacy addshow package cache location
Fixed
- Sync
commands.tomlschema with CLI (missing flags, stale args, missing exit code 6)
1.1.0 - 2026-02-22
Added
net:source type for arbitrary URL packages (stacy add grc1leg --source net:http://www.stata.com/users/vwiggins/)local:source type for vendored/local packages (stacy add myutils --source local:./lib/myutils/)- GitHub fallback: synthesize manifest from repository tree when
.pkgfile is missing - Post-install hints for packages with known implicit dependencies (reghdfe, ivreghdfe, ppmlhdfe, grstyle, etc.)
stacy doctornow surfaces available updates (reads version check cache)stacy depsnow parsesrequirestatements as package dependencies (includingcap requireandcapture require)
Changed
- Improved SSC error messages: distinguish “package not found” from “mirror gap” from “server unreachable”
1.0.2 - 2026-02-17
Fixed
- Fix SSC downloads always failing: use HTTP instead of HTTPS for
fmwww.bc.edu(the server does not support TLS, causing everystacy addfrom SSC to fall back to the GitHub mirror)
1.0.1 - 2026-02-16
Fixed
- Fix
net installfrom Stata: remove phantom files fromstacy.pkgthat caused r(601) - Fix
--format stataoutput: wrong global prefix, wrong quoting, bare types in syntax - Fix
stacy init--nameoption andstacy taskconfig section reference - Fix installation docs command name
Added
- 24 tests for
--format stataoutput and codegen correctness
Changed
- Regenerate all Stata wrappers with fixes
- Align docs and README with paper framing
1.0.0 - 2026-02-15
Initial public release.
Added
stacy run— Execute Stata scripts with proper error detection and exit codesstacy run -c— Run inline Stata codestacy run --parallel— Parallel execution of multiple scriptsstacy init— Initialize project withstacy.tomlstacy add/stacy remove— Manage dependenciesstacy install— Reproducible installs from lockfilestacy update/stacy outdated— Keep packages currentstacy lock— Generate and verify lockfilestacy task— Task runner (npm-style scripts instacy.toml)stacy deps— Script dependency analysisstacy env/stacy doctor— Environment diagnosticsstacy explain— Error code lookup- Error codes dynamically extracted from user’s Stata installation
- SSC and GitHub package sources (
github:user/repo@tag) - Global package cache at
~/.cache/stacy/packages/ --format jsonand--format stataoutput modes- Cross-platform support: macOS, Linux, Windows