The supported agent polling loop uses flisk status --json; add --tilt to include Tilt's live resource list in tilt.resources. Use flisk resources --json for the discovered manifest view, flisk stacks --json for stack names and counts and flisk networks --json for routed URLs and reachability. flisk doctor --json reports environment readiness and actionable checks.
Every response uses this envelope:
{
"schemaVersion": 1,
"data": {},
"errors": []
}data is null when a command cannot produce its report. Errors have stable code and message fields. Diagnostics are written to stderr; stdout contains one JSON document. Exit code 0 means the report succeeded (doctor readiness permits warnings), 1 means a blocking finding or command failure, and 2 means invalid arguments or an unexpected internal error (flisk up also exits 2 when hand-edited generated files drift from service.json, while native Windows startup refusal exits 1). Consumers should check the exit code and still parse the envelope when one is emitted.
Command data:
status:tilt.available,tilt.resourcesQueried,tilt.readiness,tilt.tiltfileError,tilt.staleDisabled(resource names the Tiltfile enables that Tilt still reports asDisableduntilflisk down && flisk up), optional livetilt.resources, totalresourceCount, discovered manifestresources(name,stack,type,port),stacks(name,resourceCount,resourcescontaining resource names,healthyCount),unhealthy(names of failing or pending services),unassignedResources(resource names), andcapabilities(names of the project capabilities that are on, such asdatabase; a project withcapabilities.database: falsein.flisk/project.jsonhas nodatabasehere andsharedInfraisnull).--tiltcontrols the livetilt.resourceslist: without it,tilt.resourcesQueriedisfalseandtilt.resourcesisnull. The manifestresourcesarray is always available and does not report live health.tilt.readinessretains its existing behavior and may be populated whenever Tilt is available. If Tilt is unavailable,tilt.availableisfalseand no live resource-list query is made.resources:resourceswith stable service name, stack, type, port, and manifest path.stacks:stackswithnameandresourceCount.--servicesaddsservices(service names; its length equalsresourceCount), and--verboseaddsdescription;--servicesalone does not includedescription. A project with no stacks returnsstacks: []. Outside a project it emits the same envelope asresources --json(data: null, oneerrorsentry, exit 2).networks:serviceswith stable service name, stack, base path, URL, optional Windows loopback URL, service port, and reachability state (running,stopped, orunknown). An empty list means no discovered resource currently has a routable base path.projects:projectRoot(the absolute path of the project),configured(truewhen all master configuration files exist),missingFiles(the master files that are missing from.flisk/.flisk-out; empty whenconfiguredistrue),resources(the number of discovered resources, not their names),stacks(the stack names) andunassignedResources(the number of resources that do not belong to a stack). Useflisk resources --jsonorflisk stacks --jsonwhen you need the names and details.flisk info --jsonis an alias. See the example below.doctor:ready,inProject, orderedchecks,capabilities(names of the capabilities that are on), and aportsplan withhttp,https, andpostgresentries (nopostgresentry when the database capability is off, fromcapabilities.database: falsein.flisk/project.jsonor--no-database). Each port entry contains canonicalrequested, selectedchosen,explicit, andreasonfields. Doctor is read-only and does not persist the plan, unless--fixis given (see doctor-contract.md). Structurederrorscover usage/internal failures. Check entries preservename,didPass,message, optionalfix,isWarning, andisSkipped.isSkippedindicates that a check could not run meaningfully; skipped checks are non-blocking, even whendidPassisfalse.up:ok(truewhen all expected resources are built and running),stack(the stack name argument, ornull),services(the started service names), optionalrequestedanddependencies(when--onlyis used), andtiltUrl(http://localhost:<port>).flisk up --jsonemits one JSON object once startup reaches its ready state while Tilt continues running in the background. On timeout or resource failure,data.okisfalsewithdata.tiltUrlanddata.failures(a list of affected resources, each withnameandmessage), anderrors[].codeisUP_TIMEOUTorUP_RESOURCE_FAILED. On smoke-check failure,data.okisfalse,errors[].codeisUP_SMOKE_FAILED, and Tilt is stopped. Hand-edited generated file drift (DRIFT_DETECTED,DRIFT_EXIT_CODE = 2) and unknown--onlyservice names (UNKNOWN_SERVICE) exit with code2, whileflisk config verifyexits1for drift; other startup, preflight, and host/platform refusals (including native Windows startup refusal) exit with code1. Parsers should read the failure category fromerrors[].codeand the affected resources fromdata.failures. See the examples below.up --dry-run:ok,dryRun(true),stack(the stack name argument, ornull),services(the services the run would start, in discovery order) andcommand(thetilt upcommand line that would run). With--only, it also hasrequested(the names you passed) anddeclaredDependencies(services pulled in because a requested service lists them independsOn). See the example below.up --env-file: the path selects the project env file of the run (see environment.md). A path that is missing, unreadable, not a file, outside the project, or that misses a key the platform needs is refused before anything starts:errors[].codeisENV_FILE_INVALIDand the exit code is2. A project whose vendored engine predates the option is refused witherrors[].codeENV_FILE_ENGINE_TOO_OLDand exit code1. A stack that is already running keeps the env file it started with, so the option is refused witherrors[].codeENV_FILE_ALREADY_RUNNINGand exit code1. The path is named in the message and in the human output; no value from the file is ever printed.down:ok(true) andstoppedTilt(the process IDs of anytilt uprun thatflisk downended first; an empty list when none was running). With--prune-networks, it also hasremovedNetworks(the project's Docker networks that were removed) andkeptNetworks(networks left in place, each withnameandreason, for example because a container is still attached). Whentilt downfails (for example because the generated Tiltfile no longer evaluates),flisk downstops the project's containers withdocker compose downinstead and addscomposeFallback:tiltDownExitCodeandstopped(the Compose projects it stopped).flisk down --dry-run --jsonreturnsokanddryRun(true) and stops nothing. See the examples below.logs:services(selected service names),tail(defaults to200, accepts1–10000),since(duration such as30s,5m, or1h, ornullwhen omitted), andlines(normalized log entries). Each line containstime,resource,level,source, andtext; the first four fields may benull, whiletextis a string.
Example output for flisk logs --json --service orders-api --tail 50 --since 5m:
{
"schemaVersion": 1,
"data": {
"services": ["orders-api"],
"tail": 50,
"since": "5m",
"lines": [
{
"time": "2026-10-15T12:30:00Z",
"resource": "orders-api",
"level": "info",
"source": "stdout",
"text": "Request completed"
}
]
},
"errors": []
}This is an illustrative example. --tail limits the number of recent lines, and --since filters logs to the specified duration. JSON mode returns a snapshot and exits rather than following a live stream. On failure, data is null and errors contains entries with code and message, optionally with suggestions. Usage errors exit with code 2; other failures use the applicable command exit code. Error diagnostics are also written to stderr.
config verify:valid,errors,warnings, anddiffs. Drift or missing tracked master outputs exits 1 and includes a unified diff; a valid project exits 0. A missing project exits 1 with a structured command error; invalid project JSON or an unexpected failure exits 2 with a structured internal error. JSON verification does not regenerate files.config regenerate:dryRun,hasChanges,staleServiceOutputs(the service.json files whose generated compose and Dockerfile are older than the file;flisk upwrites those, not regenerate) andchanges, one entry per master file withfile,status(new,modifiedorunchanged) andlineDiff(new minus current lines,0for a new file). With--dry-runnothing is written; without it the files are rewritten (hand edits in them are discarded) andchangessays how each one differed before the run.config migrate:schemaVersion(the version the files now declare),migrated(theservice.jsonpaths that were rewritten, relative to the project root) andwarnings. When any file cannot be migrated, nothing is written:dataisnull,errorshas oneMIGRATE_FAILEDentry per problem, and the exit code is1.config enable-infraandconfig disable-infra:service,enabled(the new value) andwasEnabled(the value before the run). An unknown service, orverdacciowithout a license that grants it, is aCOMMAND_FAILEDerror withsuggestionsand exit code1.project:--jsonimplies--yesand--quiet, so it never prompts and takes the defaults (with--forceit overwrites.flisk/project.jsonwithout asking, like--force --yes).actionsays what it did.created(a new project) hasprojectRoot,projectName,autoEnabledStacks,generatedFiles,env(created, andaddedfor the keys added to an existing.env),missingEnv,gitignoreAdded,agentSkills(the files written) andruntime(hasDockerCli,hasTiltCli).regenerated(run inside an existing project) hasprojectRoot,autoEnabledStacks,generatedFiles,env,gitignoreAddedandruntime.cloned(with a template) hastemplate,path(the absolute directory it landed in) andruntime.check(with--check) hasprojectRoot,valid,hasProjectJsonandmissingFiles; an incomplete project also gets aPROJECT_INCOMPLETEerror and exit code1.resource:--jsonimplies--yes, so the name is required and, when several stacks exist,--stacktoo.action(created, orregisteredfor a directory that already had aservice.json),name,type,stack,port(nullfor an SDK),path(relative to the project root),frameworkandlanguage(nullwhen they do not apply) anddiscovered(whetherdiscovery.pathscoverspath, soflisk upstarts it). With--frameworks,datais the inventory instead:type(frontendorbackend),frameworks(the rows the text listing prints, as objects) and, for a backend,language.stack:--jsonnever prompts, so it needs--list, or a stack name with--resources.stacks(name,resourceCount) andunassigned(name,configPath) describe the project after the run,stackis the stack assigned to (nullwith--list) andassignedlists the resources this run assigned (name,configPath).importandimport compose(the same importer):--jsonimplies--yes. flisk-import runs with its output captured (and copied to stderr), anddatahasdir,dryRun,force,written(theservice.jsonpaths the importer reports writing) andoutput(what it printed). A failed import (nothing to import, an unreadabledocker-compose.yml, a core older than the importer needs, or nonpx) hasdata: null, oneIMPORT_FAILEDerror with the importer's message and hints, and the importer's exit code (127whennpxcannot run).import tiltfile: needs flisk-import 0.4.1 or later. It runsflisk-import tiltfilethe same way, so--jsonimplies--yesanddatahaspath(the path argument as given,Tiltfilewhen omitted),dryRun,force,written([".flisk/project.json"]when the importer reports writing it, empty in a dry run) andoutput(what it printed). A refusal (a missing Tiltfile, a path inside.flisk/, a path with a quote or control character, or a.flisk/project.jsonthat already names an orchestration source without--force) hasdata: null, oneIMPORT_FAILEDerror with the importer's message and hints, and the importer's exit code.eject:--jsonimplies--yes.dryRun,created(whether this run wroteEJECTED.md, or would in a dry run;falsewhen it already existed),ejectedFile,generatedFiles(the files under.flisk/.flisk-out, which stay where they are),userFiles(a rootTiltfile, when there is one) andtiltUpCommand. A project without generated Tilt files is aCOMMAND_FAILEDerror with exit code1.mcp check:url(the endpoint checked),resource(the resource it was resolved from, ornullfor a URL),iconUrl(ornullwith--no-icon),serverName,didPass,failuresandchecks(each{check, didPass, detail}, withisSkipped: trueon a check that did not run, such astools/callwithout--call). Any failing check adds oneMCP_CHECK_FAILEDerror whosesuggestionsname the failing checks, and the exit code is1; the report is still indata. A target that is neither an https URL (plain http only for loopback hosts) nor a resource of this project is an error withdata: null.os: starts FliskOS, the local desktop app (see the cheat sheet).via(commandfor thefliskoscommand,appforFliskOS.app),path(the executable or the app bundle),args(what it was started with),project(the project passed with--project, ornull),url(where FliskOS listens, read from the command's own output and left exactly as printed, so it carries FliskOS's access token in its query string: keep it out of logs and chats;nullfor the app, which opens its own window) andignored(the options the app cannot take, such as--port; empty for the command). The envelope goes out once FliskOS is listening andflisk oskeeps running until it stops, so read the first line and leave the process alone; the command's own output goes to stderr, and the exit code is that offliskosonce it ends. Neitherfliskosnor the app on the machine is aFLISKOS_NOT_INSTALLEDerror with the install steps insuggestionsand exit code1;fliskosexiting or staying silent before it listens (30 seconds) isFLISKOS_FAILED; a--portthat is not an integer from 0 to 65535, or a--projectthat is not a Flisk project, isCOMMAND_FAILED. Nothing is ever installed. With--no-open, FliskOS stops when its stdin closes, so start it with stdin open.run: runs the command without input.service,runtime(containerfordocker execin the running container,nativefor a local process),command(the argv given after--),exitCode(the command's, also flisk's exit code), andstdoutandstderr(the last 64 KB of each). An unknown service, a missing command, or a container service that is not running isRUN_FAILEDwith exit code1.restart:resource(the name) andrestarted(true) once Tilt accepted the trigger. An invalid--portorTILT_PORTis aUSAGEerror with exit code2; no running Tilt isTILT_NOT_RUNNINGand a trigger Tilt refused isTILT_TRIGGER_FAILEDwith Tilt's message, both with exit code1.agent sessions:agent(the resource name),port,sessions(each withsessionId,lastActivity,statusandcheckpointBytes; the last two may benull) andtotal. With--session-id <id>it isagent,portandsession(the checkpoint the agent returned). Failures carry their own code:USAGE,UNKNOWN_RESOURCE,NOT_AN_AGENT,NO_PORT,NO_ROUTE,AGENT_NOT_RUNNING,AGENT_REQUEST_FAILED, orSESSION_LIST_UNSUPPORTEDfor an agent scaffolded beforeGET /sessionsexisted.config explain:file(project-relative),ownership,reason,source(the input it is generated from, ornull),driftOnUp(whatflisk updoes when the file was edited),isEditedNow(whether it differs from its snapshot now) andnext(suggested steps, in order).runtime: prints a bare object rather than the envelope:ok,assetRoot(the directory the bundled engine and templates were found in, ornull),missing(required assets that are absent),layoutErrorsandtemplateErrors.okistrueonly when all three lists are empty and an asset root was found.
For project, resource, stack, import, eject and the config subcommands above, the human output goes to stderr without colour and stdout carries the envelope only. Outside a project each prints data: null and one COMMAND_FAILED error with suggestions, and exits 1. When a deeper check stops the command before it can report (for example the machine check of flisk project), the error code is the command's own failure code (PROJECT_FAILED, RESOURCE_FAILED, STACK_FAILED, IMPORT_FAILED, EJECT_FAILED, REGENERATE_FAILED, MIGRATE_FAILED, INFRA_TOGGLE_FAILED) with the message the check printed.
Example output for flisk resource orders-api --type backend --stack shop --json in a project with no other backend:
{
"schemaVersion": 1,
"data": {
"action": "created",
"name": "orders-api",
"type": "backend",
"stack": "shop",
"port": 4000,
"path": "services/shop/orders-api",
"framework": "hono",
"language": "bun",
"discovered": true
},
"errors": []
}networks --json previously returned a bare array. It now returns the versioned envelope. During migration, networks --json-legacy returns the old array and is scheduled for removal in CLI version 1.5.0; migrate consumers before that release to data.services. Existing text and raw output remain available.
Within schema version 1, additive fields are allowed. Clients should ignore unknown fields and reject unsupported major schema versions. Removing or renaming fields, changing their meaning, or changing collection shapes requires a versioned transition and migration notes. Text aliases and plural command aliases remain supported.
Minimal polling example:
if flisk status --json >status.json; then
jq -r '.data.stacks[] | [.name, .resourceCount] | @tsv' status.json
else
jq -r '.errors[]?.message, .data.resources[]? | strings' status.json >&2
fiExample output for flisk projects --json in a configured project with a shop stack and one resource that is not in any stack:
{
"schemaVersion": 1,
"data": {
"projectRoot": "/home/dev/orders",
"configured": true,
"missingFiles": [],
"resources": 3,
"stacks": ["shop"],
"unassignedResources": 1
},
"errors": []
}The master configuration files are TILT_TECH_STACK.star, TILT_RESOURCE_DEFAULTS.star and spec.master, which flisk project generates in .flisk/.flisk-out. In a project that has not been configured yet, configured is false and missingFiles lists the files that are not there. resources and stacks are still filled in from the service.json files that were discovered, so an unconfigured project looks like this:
{
"schemaVersion": 1,
"data": {
"projectRoot": "/home/dev/orders",
"configured": false,
"missingFiles": ["TILT_TECH_STACK.star", "TILT_RESOURCE_DEFAULTS.star", "spec.master"],
"resources": 0,
"stacks": [],
"unassignedResources": 0
},
"errors": []
}Without --check, flisk projects --json exits 0 in both cases, so read data.configured. With --check, the same response is printed and the exit code is 0 when every master file exists and 1 when any is missing, which makes it usable as a CI gate: flisk projects --json --check. --check only looks for the master files. It does not validate service.json files (use flisk config verify --json for that) or check that the containers are healthy.
Outside a project, flisk projects --json (with or without --check) prints data: null and one COMMAND_FAILED error that includes suggestions, writes the message to stderr and exits 1:
{
"schemaVersion": 1,
"data": null,
"errors": [
{
"code": "COMMAND_FAILED",
"message": "Could not find project root (no .flisk/project.json found)",
"suggestions": [
"Run this from within a Flisk project",
"Run `flisk project --yes` to initialize a new project"
]
}
]
}Example output for flisk up --dry-run --json --only orders-api, where orders-api lists orders-db in dependsOn:
{
"schemaVersion": 1,
"data": {
"ok": true,
"dryRun": true,
"stack": null,
"services": ["orders-api", "orders-db"],
"requested": ["orders-api"],
"declaredDependencies": ["orders-db"],
"command": "tilt up -f <path to Tiltfile> -- --focus=orders-api orders-api orders-db"
},
"errors": []
}A dry run reports the plan and exits. It does not start Tilt or any services. Read services as the list that would start, and declaredDependencies as the part of it that comes from dependsOn in service.json. Shared infrastructure that Tilt always enables, such as Postgres and Traefik, is not listed. The command string contains the Tiltfile path on your machine, and is just tilt up when neither a stack nor --only is given. Without --only, requested and declaredDependencies are absent. This is an illustrative example, and a dry run still runs the checks that flisk up runs first (for example, it refuses a project whose generated files were edited by hand). If one of them fails, data is {"ok": false} and errors explains why.
Example output for flisk up --json:
{
"schemaVersion": 1,
"data": {
"ok": true,
"stack": null,
"services": ["orders-api", "orders-db"],
"tiltUrl": "http://localhost:10350"
},
"errors": []
}With --only (for example, flisk up --json --only orders-api), the response also includes requested (the services explicitly requested) and dependencies (the additional services Tilt enabled that you did not name). services lists every service Tilt actually enabled, which is requested plus dependencies:
{
"schemaVersion": 1,
"data": {
"ok": true,
"stack": null,
"services": ["orders-api", "orders-db"],
"requested": ["orders-api"],
"dependencies": ["orders-db"],
"tiltUrl": "http://localhost:10350"
},
"errors": []
}Success means startup reached the command's ready state: every expected non-deferred Tilt resource is built and running, and its Docker health check (if declared) has passed. Tilt continues running in the background after the JSON output is emitted, so scripting workflows should run the command in the background (for example, flisk up --json &).
With --only, the readiness guarantee applies to the requested services only (excluding any that are deferred). Dependency services appear in services and dependencies because Tilt enabled them, but they are not checked for readiness, so a dependency that is still starting or has failed does not change ok. Consumers that need a dependency to be ready must check it themselves.
If one or more resources fail to start or the readiness timeout expires (FLISK_UP_READY_TIMEOUT_MS, default 15 minutes), data.ok is false with data.tiltUrl and data.failures, and errors reports UP_RESOURCE_FAILED or UP_TIMEOUT:
{
"schemaVersion": 1,
"data": {
"ok": false,
"tiltUrl": "http://localhost:10350",
"failures": [
{
"name": "orders-api",
"message": "Build failed: exit status 1"
}
]
},
"errors": [
{
"code": "UP_RESOURCE_FAILED",
"message": "Resources failed (orders-api: Build failed: exit status 1). Tilt is still running; inspect it or run: flisk down"
}
]
}When services declare a smoke check, the checks are executed against public ingress endpoints after readiness is reached. If a smoke check fails, Tilt is automatically stopped and the command emits UP_SMOKE_FAILED before exiting with status 1:
{
"schemaVersion": 1,
"data": {
"ok": false
},
"errors": [
{
"code": "UP_SMOKE_FAILED",
"message": "A smoke check failed; Tilt was stopped"
}
]
}Example output for flisk down --json:
{
"schemaVersion": 1,
"data": {
"ok": true,
"stoppedTilt": [48213]
},
"errors": []
}With --prune-cache (flisk down --json --prune-cache -y), the response also reports what was pruned from Docker's build cache:
{
"schemaVersion": 1,
"data": {
"ok": true,
"stoppedTilt": [],
"prunedCache": {
"until": "24h",
"reclaimed": "2.6GB"
}
},
"errors": []
}With --dry-run --prune-cache (flisk down --json --dry-run --prune-cache), data.wouldPruneCache reports { "until": "24h" } without executing pruning.
With --prune-networks (flisk down --json --prune-networks), the response also lists what happened to the project's Docker networks. A network that still has a container attached is kept, and reason is the first line of the Docker error:
{
"schemaVersion": 1,
"data": {
"ok": true,
"stoppedTilt": [],
"removedNetworks": ["orders_default"],
"keptNetworks": [
{
"name": "orders_shared",
"reason": "Error response from daemon: error while removing network: network orders_shared has active endpoints"
}
]
},
"errors": []
}removedNetworks and keptNetworks are only present when --prune-networks is given and the networks could be checked. Without the flag, or when the project root or its configuration cannot be read, both fields are absent, so check for them before reading them. In JSON mode the human-readable "Removed networks" and "Kept network" lines are not printed, and a failed network cleanup does not change ok. The process ID and network names above are illustrative.
A failed shutdown is reported in the same envelope. If tilt down exits with a non-zero code, or a prerequisite check fails (Docker or Tilt missing or not responding), flisk down writes the details to stderr, exits with that code (the code from tilt down, or 1 for a missing prerequisite), and stdout contains one object with data.ok set to false and a DOWN_FAILED error:
{
"schemaVersion": 1,
"data": {
"ok": false
},
"errors": [
{
"code": "DOWN_FAILED",
"message": "flisk down exited with code 1"
}
]
}When the command printed an error before it stopped (for example Could not find project root), message is that error and suggestions lists its hints, so a script gets the real reason from the JSON alone. Otherwise message ends with the exit code of the command. A failed shutdown has no stoppedTilt or network fields, and containers may still be running, so run flisk status --json or flisk doctor --json to see what is left. Outside a project, flisk down --json does not use this shape: data is null, errors has one COMMAND_FAILED entry with suggestions, and the exit code is 1.
New resources created by flisk resource include a per-file $schema reference to the hosted service manifest schema. VS Code's JSON language service uses that reference for completion and validation; the generator leaves .vscode/settings.json untouched, so existing project editor settings remain intact. Resource registration adds the reference only when one is absent and preserves a user's existing $schema value.