Earn 14 free days when your bug report or suggestion is accepted — how it works

Configuration

You almost never need to edit any of this. npx codeloop initwrites sensible defaults that work for 95 % of projects. Open this page only when you want to override the defaults — everything below is optional.

Every CodeLoop knob is in a small set of files under .codeloop/ in your project root, plus a handful of environment variables. npx codeloop initseeds sensible defaults — this page documents everything you can change.

File layout

your-project/
├── .codeloop/
│   ├── config.json        # core config (this page)
│   ├── plugins.json       # custom CLI runners — see /docs/plugins
│   ├── figma.json         # Figma frame map — see /docs/design-compare
│   ├── sections.json      # multi-section state — see /docs/multi-section
│   ├── baselines/         # visual baselines — see /docs/visual-review
│   └── runs/              # local artifact directory (gitignored)
├── designs/               # local design references (optional)
├── .cursor/
│   ├── mcp.json           # auto-generated
│   └── rules/*.mdc        # auto-generated
└── .claude/
    ├── settings.local.json # auto-generated
    └── agents/*           # auto-generated

config.json — full reference

{
  "project_type": "auto",
  "runners": {
    "build": { "command": "npm run build", "timeout_ms": 60000 },
    "lint":  { "command": "npm run lint",  "timeout_ms": 30000 },
    "test":  { "command": "npm test",      "timeout_ms": 120000 }
  },
  "evidence": {
    "target_app": "Photometry DB"
  },
  "screenshots": {
    "enabled": true,
    "tool": "playwright",
    "base_url": "http://localhost:3000",
    "wait_for": "networkidle",
    "full_page": true,
    "viewports": [
      { "name": "mobile",  "width": 375,  "height": 812  },
      { "name": "tablet",  "width": 768,  "height": 1024 },
      { "name": "desktop", "width": 1440, "height": 900  }
    ],
    "urls": ["/", "/about", "/checkout"]
  },
  "visual_review": {
    "threshold": 0.02,
    "baseline_dir": ".codeloop/baselines",
    "ignore_regions": [
      { "screen": "home", "rect": [0, 0, 1440, 64] }
    ]
  },
  "design_compare": {
    "threshold": 0.85,
    "scoring": "weighted_lab"
  },
  "recording": {
    "enabled": true,
    "max_duration_ms": 300000,
    "fps": 30
  },
  "gate_check": {
    "min_confidence": 0.94,
    "require_all_tests": true,
    "allow_lint_warnings": true,
    "visual_severity": "warning",
    "design_severity": "warning"
  },
  "sections": {
    "enabled": false,
    "spec_file": "docs/specs/_master.md",
    "acceptance_dir": "docs/acceptance"
  }
}

Field reference

FieldDefaultDescription
project_type"auto"Auto-detect from project files. Override with flutter, react, nextjs, vue, angular, django, rails, xcode, android, dotnet.
runners.<name>.commandAuto-detectedShell command to run for this check. build, lint, and test are first-class; add custom names for additional checks.
runners.<name>.timeout_ms60000 / 120000Hard timeout per runner.
evidence.target_appAuto-detected on initThe window title (or process name) of the desktop application CodeLoop should attach to for screenshots, video capture, and codeloop_interact. Auto-populated by npx codeloop init from AssemblyName (.NET) / PRODUCT_NAME (Xcode) / android:label (Android) / productName (Electron / Tauri) / displayName (React Native) / name (Flutter pubspec.yaml). When set on a desktop project, codeloop_capture_screenshot refuses to capture the IDE silently — it only attaches to the named app. 0.1.49 hardening: required for every desktop-UI framework, not just .NET/Xcode/Android.
screenshots.enabledtrueEnable screenshot capture for UI projects.
screenshots.tool"playwright"playwright, puppeteer, maestro (Flutter), simctl (iOS), adb (Android), screencapture (macOS native), powershell (Windows native).
screenshots.base_urlhttp://localhost:3000Where the dev server lives during verify.
screenshots.wait_for"networkidle"load, domcontentloaded, networkidle, or a CSS selector to wait for.
screenshots.full_pagetrueCapture the full scrollable page (vs. just the viewport).
screenshots.viewportsmobile / tablet / desktopPer-viewport capture matrix.
screenshots.urlsFrom codeloop_discover_screensExplicit URL list. If absent, CodeLoop crawls.
visual_review.threshold0.02Pixel-difference fraction allowed before a diff fails. See Visual review.
visual_review.baseline_dir.codeloop/baselinesWhere canonical baselines live.
visual_review.ignore_regions[]Per-screen rectangles to exclude from diffing. Useful for timestamps, ads, A/B variants.
design_compare.threshold0.85Minimum match score against Figma frames. See Design compare.
design_compare.scoringweighted_labpixel, weighted_lab, or structural.
recording.enabledtrueAllow codeloop_start_recording on this project.
recording.max_duration_ms300000Hard cap on a single recording.
recording.fps30Target frames-per-second.
gate_check.min_confidence0.94Minimum overall score to pass the gate.
gate_check.require_all_teststrueBlock on any failing test, not just touched-file tests.
gate_check.allow_lint_warningstrueLint warnings count as info, not blocker.
gate_check.visual_severitywarningSet to blocker to fail the gate on visual regressions.
gate_check.design_severitywarningSet to blocker to fail the gate on design drift.
sections.enabledfalseTurn on multi-section orchestration.
sections.spec_filedocs/specs/_master.mdMaster spec path.
sections.acceptance_dirdocs/acceptancePer-section acceptance directory.

Deep-E2E journey — the e2e block

A full codeloop_verify on a UI project automatically runs the deep-E2E journey as its final phase: it boots the device or browser, launches the app, drives the planned interactions (Maestro labels on mobile, Playwright on web, CGEvent / user32 on desktop), and captures per-screen screenshots, video, and app logs. The e2e block in .codeloop/config.json controls every part of that launch-and-drive phase. All keys are optional — the defaults auto-detect the right target.

{
  "e2e": {
    "auto_journey": true,
    "target": "ios_simulator",
    "platforms": ["ios", "android"],
    "android_avd": "Pixel_8_API_35",
    "ios_device": "iPhone 16 Pro",
    "device_id": "emulator-5554",
    "boot_device": true,
    "web_url": "http://localhost:3000",
    "flutter_driver": false,
    "launch_timeout_seconds": 1800,
    "launch_stall_seconds": 180,
    "boot_timeout_seconds": 180,
    "ios_scheme": "MyApp-Staging",
    "ios_configuration": "Debug",
    "gradle_install_task": ":app:installDevDebug",
    "android_app_id": "com.example.myapp.dev"
  }
}
KeyDefaultDescription
e2e.auto_journeytrueAuto-run the deep-E2E journey (launch + drive + screenshots + video) as the final phase of every full verify on a UI project. Set false to restore the directive-only behavior (e.g. CI runners that cannot boot a device).
e2e.targetAuto (mobile-first)Pin the journey to one platform: ios_simulator, android_emulator, browser, or desktop (synonyms ios / android / web accepted). Without it, a Flutter app that ships ios/ or android/ is treated as mobile-first; an explicit codeloop_run_journey target_type argument still wins for one-off runs.
e2e.platformsunsetDrive MULTIPLE platforms sequentially in one verify, e.g. ["ios", "android"] — each gets its own launch, drive, screenshots, and video, and the interaction-evidence gate requires driven evidence from EVERY listed platform. gate_check reports a per-platform confidence breakdown. Overrides e2e.target.
e2e.android_avdFirst availableAndroid Virtual Device id to boot. List yours with emulator -list-avds.
e2e.ios_deviceFirst available iPhoneiOS Simulator name or UDID to boot.
e2e.device_idunsetPin a SPECIFIC connected device/emulator (adb serial, simulator UDID, or flutter devices id). For physical devices, CodeLoop skips emulator boot and launches directly on it.
e2e.boot_devicetrueSet false to require an already-booted device instead of auto-booting one.
e2e.web_urlhttp://localhost:3000URL the browser journey opens for web targets.
e2e.flutter_driverfalseOpt in to the widget-key-based Flutter integration_test driver instead of the default label-based Maestro driver.
e2e.launch_timeout_seconds1800HARD CAP on the app-launch wait. The wait is stall-aware: while the build keeps producing output it keeps waiting, so a 12-minute cold build is fine; this cap is the absolute upper bound even with output.
e2e.launch_stall_seconds180Launch fails after this many seconds of build/launcher SILENCE (no new output). Raise for toolchains that go quiet; lower to fail fast in CI.
e2e.boot_timeout_seconds180Emulator/simulator boot timeout. First-ever cold boots on slower machines can need more.
e2e.ios_schemeFirst non-test schemeXcode scheme for native-iOS journey builds — pin it on multi-scheme workspaces (white-label apps, Dev/Staging/Prod).
e2e.ios_configurationDebugXcode build configuration for native-iOS journey builds. The .app bundle is scanned from the matching Products/<Configuration>-iphonesimulator directory.
e2e.gradle_install_task:app:installDebugGradle install task for native-Android journey builds. Flavored builds need e.g. :app:installDevDebug. A configured task that fails does NOT silently fall back — the error names this key.
e2e.android_app_idParsed from gradleExplicit applicationId to launch + liveness-check + hand to Maestro, for builds whose final id is computed at build time (applicationIdSuffix, manifest placeholders).

Per-OS toolchain setup for the journey lives in the macOS + iOS Simulator and Windows + Android emulator quickstarts. What works on which host OS is documented honestly in the compatibility matrix.

Platform recipes

Flutter

{
  "project_type": "flutter",
  "runners": {
    "build": { "command": "flutter build apk --debug" },
    "test":  { "command": "flutter test" },
    "lint":  { "command": "dart analyze" }
  },
  "screenshots": {
    "tool": "maestro",
    "device": "Pixel_6_API_33"
  }
}

Next.js / React

{
  "project_type": "nextjs",
  "runners": {
    "build": { "command": "npm run build" },
    "test":  { "command": "npx jest" }
  },
  "screenshots": {
    "tool": "playwright",
    "base_url": "http://localhost:3000",
    "urls": ["/", "/dashboard", "/settings"]
  }
}

Vue / Nuxt

{
  "project_type": "vue",
  "runners": {
    "build": { "command": "npm run build" },
    "test":  { "command": "npx vitest run" }
  }
}

Django / Rails / Go (via plugin)

For non-built-in stacks, use .codeloop/plugins.jsonto wire your existing CLI test runner into the loop. Plugins behave exactly like first-party runners — failures count toward the gate and surface in the dashboard.

iOS (Xcode)

{
  "project_type": "xcode",
  "runners": {
    "build": { "command": "xcodebuild -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 15'" },
    "test":  { "command": "xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 15'" }
  },
  "screenshots": { "tool": "simctl" }
}

Android (gradle)

{
  "project_type": "android",
  "runners": {
    "build": { "command": "./gradlew assembleDebug" },
    "test":  { "command": "./gradlew test" }
  },
  "screenshots": { "tool": "adb", "device": "emulator-5554" }
}

.NET / WPF

{
  "project_type": "dotnet",
  "runners": {
    "build": { "command": "dotnet build" },
    "test":  { "command": "dotnet test" }
  },
  "screenshots": { "tool": "powershell" }
}

Desktop-app mode (0.1.49+)

Whenever codeloop init detects a desktop UI framework, it sets evidence.target_app in config.jsonand every screenshot / video / interaction call from then on attaches to THAT process — not the IDE, not the primary monitor.

Frameworks classified as desktop-app projects (and therefore covered by this auto-protection):

  • .NET — WPF, WinForms, MAUI, Avalonia, WinUI, UWP (anything with <UseWPF>true</UseWPF>, <UseWindowsForms>true</UseWindowsForms>, MAUI / Avalonia / WinUI SDK refs, or <OutputType>WinExe</OutputType>). Pure ASP.NET / Blazor Server projects are not considered desktop-app projects — they ship as web apps.
  • Xcode — any iOS / macOS / tvOS / watchOS project.
  • Android — any project with an AndroidManifest.xml or build.gradle(.kts) at the root.
  • Node desktop wrappers — Electron (electron dependency), Tauri (@tauri-apps/cli), React Native (react-native), Expo (expo).
  • Flutter desktop — any Flutter project with a windows/, macos/, or linux/ sub-target.

If the target app is not running when codeloop_capture_screenshot is called, CodeLoop attempts to launch it via launch_app. On Windows that includes parsing <OutputPath> and <BaseOutputPath> out of the picked .csproj, and a fallback to Get-StartApps for MSIX / Microsoft Store apps.

Run codeloop_self_testas your first MCP call on any new desktop project — it walks the auto-detection, target-app resolution, and PNG decoder paths in < 1 s and tells you exactly which preflight check would have failed.

codeloop_interact coords reference (0.1.49+)

codeloop_interact accepts a coords enum that tells the MCP server how to interpret x / y:

ModeMeaningWhen the agent picks it
auto (default)Heuristic — if (x,y) falls inside the target window’s client rect, treat as window-relative; otherwise treat as screen-absolute.Most common case. Safe default.
windowCoordinates are relative to the target window’s client area. CodeLoop adds the window origin (and applies the inverse DPI factor) before sending the click.Agent inspected the window via accessibility tree / browser DOM and computed in window space.
screenCoordinates are absolute screen pixels. CodeLoop sends them through unchanged.Agent already computed in screen space.
screenshotCoordinates were computed against the captured PNG that CodeLoop returned. CodeLoop scales by (actual_window_width / screenshot_width) and adds the window origin (and inverse DPI). Pass screenshot_path so CodeLoop can read the dimensions.The MCP transport sometimes downscales screenshots; this mode unwinds that.

DPI awareness: window_bounds on the capture response now includes dpi_x and dpi_yfor high-DPI displays (Retina, 200 % Windows scaling, Linux GDK_SCALE).codeloop_interact applies the inverse factor in window and screenshotmodes so a click on a 4K monitor at 200 % lands at the right pixel.

Environment variables

VariableRequiredDescription
CODELOOP_API_KEYYesYour CodeLoop API key.
CODELOOP_API_URLNoOverride backend URL. Defaults to https://api.codeloop.tech.
CODELOOP_PROJECT_DIRNoForce the project root path. Auto-written into .cursor/mcp.json by npx codeloop init as a workspace pin. 0.1.49 hardening: at MCP boot, CodeLoop validates that this path exists on disk and contains .codeloop/config.json; if stale (e.g. you renamed the workspace), it logs a single warning and falls back to discovery instead of getting wedged. Re-run npx codeloop init from the new path to refresh the pin.
CODELOOP_MODENocloud (default) or local (self-host).
CODELOOP_OFFLINENotrue skips backend calls; usage queues locally.
CODELOOP_LOG_LEVELNodebug, info, warn, error.
FIGMA_API_TOKENFor Figma comparePersonal access token for the Figma REST API. See Design compare.
DASHBOARD_PORTNoOverride the default 3737 port for the local dashboard.
BACKEND_URLNoRead by codeloop doctor to verify backend reachability.

Plugin configuration

Drop a .codeloop/plugins.json in the project root to wire any CLI test runner into the verify suite. See the full schema in Plugin SDK. Plugin entries appear next to first-party runners in codeloop_verify output and contribute to the gate score.

Multi-section configuration

Enable sections.enabled = true and create per-section spec / acceptance files. codeloop_section_status reads and writes the state machine; see Multi-section orchestration for the full lifecycle.

Design references

Two paths:

  • Figma .codeloop/figma.json + FIGMA_API_TOKEN env var. See Design compare.
  • Local PNG designs/<screen>/<viewport>.png. No credentials, no network.

OSS plan project marker

If you applied for the free OSS plan via /oss-application, your approval ties the API key to your repo. There is no per-project config to add — the backend resolves the plan from the key itself.

Self-host configuration

Point CODELOOP_API_URL at your stack and set CODELOOP_MODE=local. See Self-host runbook for the full Docker Compose env reference.

Next Steps