Skip to content

CLI JSON output

Alexis edited this page Aug 8, 2026 · 11 revisions

CLI JSON output

Note

Last updated for: v0.3.0

I am working through adding support for enabling JSON output to the CLI, allowing light-phone-cli-tui to be more easily used as a dependency in non-Python projects.

--json support roadmap

Stable

Read commands (fully wired, safe to depend on)

  • music list (+ regex filters)
  • podcasts list
  • notes list
  • tools list
  • devices list

Unstable

  • music update — has --yes, but it's bespoke: never checks is_json_mode(), several console.print() calls run unconditionally (would corrupt --json stdout), no --dry-run, and the final result isn't render()-wrapped. Passing --json --yes today will not give you clean output.
  • podcasts delete — just got --id support, but confirmation is a plain click.confirm() — no --yes/--dry-run/--json at all yet (this is what we're mid-way through wiring up).

Unimplemented

No --json/--yes/--dry-run for these yet.

  • music upload
  • music delete-all
  • music delete (regex path; the interactive picker path is human-only by nature)
  • music sort (non-destructive, but silent — no --json acknowledgment of what changed)
  • podcasts add (no render() at all — plain console.print)
  • notes add, notes download
  • tools remove

JSON schema

A schema.json is provided in the repo root you to autogenerate types. You can also generate this yourself with light schema.

The schema includes a $hash field for detecting when the saved schema goes out of sync with the CLI's actual output. It is recommended to do a runtime check on this, e.g.

import { execFileSync } from "node:child_process";
import { readFileSync } from "node:fs";

const savedSchema = JSON.parse(readFileSync("schema.json", "utf-8"));
const currentHash = execFileSync("light", ["schema", "--hash"], { encoding: "utf-8" }).trim();

if (savedSchema["$hash"] !== currentHash) {
  console.warn("schema.json is stale - regenerate with `light schema > schema.json`, and regenerate types");
}

Run this once at startup (or in CI) to catch drift before it causes confusing runtime errors.

--json output

Every --json-enabled command wraps its output as:

{ "data": <command-specific payload>, "error": null }

On failure, data is null and error is a human-readable message:

{ "data": null, "error": "No podcast found with title: Foo" }

Destructive commands

Commands that modify data (upload, delete, update, etc.) normally prompt for confirmation before proceeding. Since an interactive prompt can't be answered by a script, --json requires pairing with one of:

  • --yes - skip confirmation and apply the change immediately.
  • --dry-run - preview what would change, without applying it.

A typical integration flow:

// 1. Preview what would happen
const preview = runLight<UploadPlan>(["music", "upload", "track.mp3", "--dry-run"]);

// 2. Render your own confirmation UI from `preview`

// 3. Apply it
runLight<UploadPlan>(["music", "upload", "track.mp3", "--yes"]);

Clone this wiki locally