Project folder and config

For AI agents: this page is Markdown at https://squiggle.sh/docs/reference/project.md. Every docs page is listed at https://squiggle.sh/llms.txt, and all of them are one file at https://squiggle.sh/llms-full.txt.

Squiggle keeps everything for a project in a .squiggle folder at the project root: your flows, your settings, and what each run records. Here is what each part holds, what to commit, and every setting in config.jsonc.

What’s in .squiggle

Pick an entry to see what it holds and whether it belongs in git. Studio, the squiggle command and the MCP server all use the same folder.

  • .squiggle/

flows/

Commit it

Your flows, as YAML. This is source: commit it. When a selector heals, the fix is written here, so you review it in a diff.

What to commit

Commit flows/, interfaces/ and config.jsonc. Everything else is recorded on your Mac by use, and can grow to several gigabytes. squiggle init writes .squiggle/.gitignore, which ignores everything except those three and itself. You can edit it: Squiggle never rewrites it.

A .gitignore rule doesn’t apply to files git already tracks. If you committed part of the folder before, untrack it:

Terminal
git rm -r --cached .squiggle/runs

Declare a screen’s interface

Before you build a screen, you can write down the controls it will have, one file per screen in .squiggle/interfaces/. The report card then checks the built screen against it, and squiggle agent validate accepts a flow that targets the declared controls before the screen exists.

.squiggle/interfaces/settings.json
{  "route": "settings",  "path": "/settings",  "controls": [    { "testID": "settings-save", "role": "button", "leadsTo": "back" },    { "testID": "settings-delete-account", "role": "button", "destructive": true }  ]}
KeyWhat it holds
routeThe screen’s route name, the name flows and screen memory use: settings for app/settings.tsx. The navigator chain, such as (tabs)/explore, works too.
pathThe path that opens it, when it differs from the route.
controls[].testIDRequired. The testID the control renders with.
controls[].roleIts accessibilityRole: button, link, switch, textbox and so on.
controls[].labelIts accessibility label.
controls[].leadsToThe route a press opens, or back for a control that returns to the screen before.
controls[].destructivetrue for a control that does something irreversible. Mark it in your code with agentHint({ destructive: true }) too.

The card fails a row for a declared control the screen doesn’t render, a testID it renders that isn’t declared, a control that opens a different screen, and a different role. A file that isn’t JSON, has no route, or has a control with no testID fails a row too.

How Squiggle finds the folder

The squiggle command and the MCP server look in the current directory, then each parent in turn, so a command run from a subdirectory uses the same folder. Studio uses the project you opened in it.

config.jsonc

Settings for everyone who works on the project. Comments are allowed. Flags on the command line override these settings for one run.

.squiggle/config.jsonc
{  // the app under test, when this repo has one  "bundleId": "com.acme.shop",  "shards": 4,  // --profile ci applies profiles.ci on top of the settings above  "profiles": {    "ci": { "relay": false, "shards": 2 }  }}
KeyWhat it sets
bundleIdThe app under test, when the project has one.
budgetsPerformance limits per screen, keyed by route name, with "*" for every screen. squiggle init writes a default set. See Set a budget per screen. A screen’s sizes say how big it must get without dropping frames; see Keep a screen off its cliff.
appsThe app under test on each platform, for a project that builds both, for example { "ios": "com.acme.shop", "macos": "com.acme.shop.mac" }. The platform picks which; bundleId is the fallback. squiggle init fills it in from your ios/ and macos/ projects.
platformios or macos, the platform commands use when you don’t pass --platform. squiggle init sets macos for an app with no ios/ folder.
flowsDirWhere flows live, if not .squiggle/flows.
shardsHow many simulators squiggle flows uses at once.
udidThe simulator to use when several are running.
portThe port squiggle waits on for the app (default 7777). SQUIGGLE_PORT overrides it for one shell, and squiggle flow honours both. The app must connect to the same port: set EXPO_PUBLIC_SQUIGGLE_WS_URL=ws://localhost:<port> in its .env.
relayConnect through Studio instead of directly, so Studio keeps showing the app during a run.
relayUrlThe address to connect through. Setting it turns relay on.
profilesNamed sets of the settings above. --profile ci applies profiles.ci on top.
telemetryfalse turns off anonymous usage events for this project.
crashReportsfalse turns off crash reports for this project.
prEvidencetrue lets your agent attach a recording or screenshot to a pull request on its own. Off by default; squiggle flow --attach-pr never needs it.
leaderboardtrue lets a benchmark score be published to the public leaderboard. Off by default.
updateChannelThe release channel squiggle update follows. squiggle update --channel sets it.

Telemetry

Squiggle sends anonymous usage events, never before it has shown you a one-time notice. To see exactly what is sent, run squiggle telemetry. To turn it off, run squiggle telemetry off, or set DO_NOT_TRACK=1 or SQUIGGLE_TELEMETRY=0.