Forage Studio
Forage Studio is the Tauri-based desktop app for authoring .forage recipes interactively. It hosts the same runtime the CLI uses, embeds Monaco for editing, and ships an in-process daemon that schedules and persists production runs. The capture / iterate / publish loop happens in one window with the results visible side-by-side.
Everything the CLI can do, Studio can do. The difference is ergonomics, instead of forage record && forage run && forage publish across three shell windows, you do it all from one.
Install
Download the latest signed bundle from GitHub Releases: .dmg (macOS), .msi (Windows), .deb / .AppImage (Linux).
For development:
git clone https://github.com/foragelang/forage
cd forage/packages/studio-ui && npm install
cd ../../apps/studio && cargo tauri devcargo tauri dev boots Vite on :5173 and embeds it in a Tauri WebView with hot reload for the React layer and cargo rebuilds for the Rust backend.
Workspace
Studio operates on exactly one workspace at a time: ~/.forage/ by default, overridable via FORAGE_WORKSPACE_ROOT. A workspace is the directory marked by forage.toml; Studio drops an empty manifest on first launch.
Workspace contents:
forage.toml: name +[deps]table for hub packages.*.forageat any depth: source files. A file may carry a recipe header,shared declarations, file-scoped declarations, or any combination. File position is organizational, not load-bearing._fixtures/<recipe>.jsonl/_snapshots/<recipe>.json: workspace data keyed by recipe header name..forage/: runtime state owned by the daemon (daemon.sqlite, per-recipe output stores underdata/<recipe>.sqlite).
Sidebar
The sidebar carries:
- Workspace header: root path, click to switch.
- Runs: every
Runrow links to the Deployment view; hover-only play button triggers an ad-hoc fire. - Recipes: the list of recipes parsed from the workspace, keyed by header name. Clicking a row opens the file in the editor with the recipe's deployment surfaces (Run / Configure / Deploy) enabled.
- Dependencies:
[deps]entries fromforage.toml. - Files: the filesystem tree. Lets you open header-less declarations files alongside header-bearing recipes.
- Daemon footer: running indicator + active-count + version.
A file's place in the workspace determines whether the editor enables recipe-scoped affordances:
- Header-bearing file: Run / Configure / Deploy enabled; the active-recipe-name field tracks the parsed header.
- Header-less file: affordances disabled. The file is a declarations file contributing
shared types / enums / fns to the workspace catalog.
Editing
- Source: Monaco editor with Forage syntax highlighting, bracket auto-closing, comment toggle, and validation markers from the LSP. ⌘S saves and validates. The editor surface is path-based, files open by workspace-relative path, just like a normal editor.
- Capture (toolbar): opens a fresh Tauri WebView at a URL of your choice; records every fetch/XHR exchange. Saved captures land in
_fixtures/<recipe>.jsonlfor the active recipe.
Errors from the parser and validator appear in a panel beneath the editor, debounced ~500ms after each edit.
Running
Two run modes from the editor's toolbar:
- Run live:
steps use the live HTTP transport,visits a visible WebView. The polite UA and ~1 req/sec rate-limit apply. - Run replay: feeds
_fixtures/<recipe>.jsonlthrough the same evaluator. No network involved.
When the run finishes, the Snapshot panel populates with the produced records grouped by type.
The Deployment view tracks scheduled runs over time: a sparkline of recent runs by health (Ok / Drift / Failed), the schedule editor (Cron / Interval / Manual cadence), per-step run stats. The daemon runs in-process, so closing Studio also stops scheduled fires.
Diagnostic
The Diagnostic tab renders the DiagnosticReport from the most recent run. Sections:
- Stall reason: how the run terminated.
settled/completedis the happy path; anything else is a clue. - Unmet expectations:
expect { records.where(...) … }rules from the recipe that didn't hold against the produced snapshot.
Publishing
The Publish flow carries the form for pushing a recipe to the hub:
- Fill in display name, summary, tags, license. The hub-side slug is fixed by the recipe's header name.
- Click Validate to confirm the recipe parses + passes the validator.
- Click Preview payload to see the JSON body Studio would POST to
api.foragelang.com. - Click Publish.
Studio stores OAuth tokens on disk at ~/.forage/auth/<host>.json (chmod 600). The Sign in with GitHub button runs the device-code flow against api.foragelang.com.
Keyboard shortcuts
| Shortcut | Action |
|---|---|
| Cmd-N | New recipe |
| Cmd-S | Save current file |
| Cmd-R | Run live |
| Cmd-Shift-R | Run replay |
| Cmd-, | Preferences |
Native menu items use the same shortcuts via Tauri's menu API.