# BiggSkyCLI Manual

For BiggSky v0.6.0. BiggSkyCLI runs BiggSky's stacking and deconvolution, and its tool
commands, with no window. It is included in the BiggSky SSE Pro, DSO and Universe editions,
and in the free trial.

**For AI agents:** this file is written to be handed to an agent as-is. Section 11,
"Machine interface (`--json`)", says how to drive BiggSky: check the license, discover
the parameters, dry-run, run, and read the results.

This file is installed beside `BiggSkyCLI.exe`, and published at
https://biggsky.com/cli-manual.html and https://biggsky.com/BiggSkyCLI-Manual.md.

---

## 1. What it is

`BiggSkyCLI` runs BiggSky's **stacking** and **deconvolution** with no window.
- **Targets:** Lunar, Planetary and Solar. DSO is not supported.
- **Engine:** it is the same engine the app uses. A compiled CLI run gives results
  byte-identical to the same run from MATLAB source.
- **One-step processing:** stack then deconvolve, the CLI's equivalent of the app's
  **Do It All** for these targets.

Typical uses:

| Use | How |
|---|---|
| Process a night's captures unattended | `BiggSkyCLI process D:\Captures\2026-10-10 -t planetary --settings user` |
| Deconvolve an AutoStakkert stack | `BiggSkyCLI process jupiter_AS4.tif -t planetary` |
| Re-run a session with fixed, documented settings | a **job file** (section 9): `BiggSkyCLI run night.json` |
| Drive BiggSky from a script, capture program or AI agent | `--json` events and exit codes (sections 10–11) |

### Requirements

- **License:** the command line is part of the **SSE Pro**, **DSO** and **Universe**
  editions, and of the free trial. SSE Lite, SSE Free and Free do not include it. A
  commercial license includes it with Universe only. On a DSO license the `mosaic` and
  `derotate` commands are still refused - those are solar system tools.
- **License activation:** activate in the **BiggSky app** on the same computer, under the
  **same Windows user account** (on a Mac or Linux, the same user).
  - The license seat is tied to that account.
  - Running the CLI as another user, as a Windows service or as SYSTEM looks like a
    different machine.
  - To schedule runs, use Task Scheduler with "Run as" set to your own account.
- **Installation:** the BiggSky installer puts `BiggSkyCLI.exe` beside `BiggSky.exe`, in
  `C:\Program Files\BiggSky\application\`, and both use the same MATLAB Runtime. Nothing
  else to install. Beside them are `BiggSkyClient.exe` (section 3, "Server") and this manual
  as `BiggSkyCLI-Manual.md`. Add that folder to your `PATH`, or type the full path, to run it from
  any command prompt:

  ```
  "C:\Program Files\BiggSky\application\BiggSkyCLI.exe" version
  ```
- **On macOS** the installer puts `BiggSkyCLI.app` beside `BiggSky.app` in
  `/Applications/BiggSky/application/`, with a `BiggSkyCLI` command that runs it,
  `BiggSkyClient` and this manual. The program inside `BiggSkyCLI.app` cannot be started by
  itself (it needs the MATLAB Runtime found for it), so always use the `BiggSkyCLI` command.
  In Terminal:

  ```
  cd /Applications/BiggSky/application
  ./BiggSkyCLI version
  ```

  Or add the folder to your `PATH` (in `~/.zshrc`:
  `export PATH="/Applications/BiggSky/application:$PATH"`) and type `BiggSkyCLI` anywhere.
  Everything in this manual works the same way. Paths are macOS paths
  (`~/Captures/jup.ser`), and quoting is the shell's (`'My Captures/jup.ser'`).
- **On Linux** the installer puts the `BiggSky` and `BiggSkyCLI` commands, `BiggSkyClient`
  and this manual in `/usr/BiggSky/application/`, and the programs themselves in its `bin/`.
  The commands find the MATLAB Runtime R2024b (in `/usr/local/MATLAB/MATLAB_Runtime/R2024b`,
  `/opt/MATLAB/MATLAB_Runtime/R2024b` or your home folder) and start the program. Always use
  them, not `bin/BiggSkyCLI` (which cannot start without the runtime on its library path) or
  `bin/run_BiggSkyCLI.sh` (which prints a banner on stdout and so breaks `--json`):

  ```
  /usr/BiggSky/application/BiggSkyCLI version
  ```

  Add the folder to your `PATH` (`export PATH="/usr/BiggSky/application:$PATH"` in
  `~/.bashrc`) to type `BiggSkyCLI` anywhere. A runtime installed somewhere else:
  `export MCRROOT=/path/to/MATLAB_Runtime/R2024b`.
- **Start-up time:** every invocation spends about **15–20 s** starting the MATLAB Runtime.
  Pass many inputs to one call rather than calling once per file, or keep BiggSky running
  as a server (section 3).

---

## 2. Quick start

```
BiggSkyCLI license
BiggSkyCLI process D:\Captures\2026-10-10\jupiter.ser --target planetary
BiggSkyCLI process D:\Captures\2026-10-10 --target planetary --settings user
BiggSkyCLI process D:\Captures\Moon\*.ser -t lunar --ops stack
BiggSkyCLI process D:\AS4\jupiter_stack.tif -t planetary --ops decon
BiggSkyCLI process D:\Captures\Saturn\*.ser -t planetary --ops analyze
```

- With no `--out`, results go into a `BiggSky` folder beside each capture, the same place
  the app's "use image folder" setting puts them.
- The command prints where each result went.

---

## 3. Commands

| Command | What it does |
|---|---|
| `process <inputs...> [options]` | Stack and/or deconvolve each input. If the first argument is not a command, `process` is assumed. |
| `run <job.json>... [options]` | Run one or more job files (section 9). |
| `script <commands.txt> [--stop-on-error]` | Run a file of `BiggSkyCLI` command lines in **one** process (below). |
| `params --target T [--expert\|--all] [--settings …] [--json]` | List the named parameters (`--set`, `--decon`, `--save`) with their tier and resolved value for target T. Needs no license. `params --command <tool>` lists a tool command's options; `params --widgets` lists the app controls `--widget` can set. |
| `info`, `compare`, `render`, `sharpen`, `mosaic`, `derotate`, `dewarpmovie` | Tool commands (below). |
| `profile list \| show [NAME] \| save NAME \| path` | Settings profiles (section 6). `save` takes `--target`, `--settings`/`--config` (a snapshot) or `--set`/`--decon`/`--save` values (an update), and `--description`. |
| `license [--json]` | Show the license status. Offline; never contacts the server. |
| `LicenseCheck [--json]` | Validate with the BiggSky license server **now**, then save the result. The only command that goes online. |
| `version` | Print the version. |
| `help` | Print the full usage. |

Command names are not case-sensitive: `LicenseCheck`, `licensecheck` and `license-check` all work.

### Scripts: several commands in one process

Every start of `BiggSkyCLI.exe` costs time before any work: the MATLAB Runtime starts, and
the first command in a process then loads the app's settings, figures and the GPU. `script`
runs a file of command lines in one process, so that is paid once:

```
# commands.txt - a night: decon the stacks, look at one, derotate them
BiggSkyCLI process D:\Night\PNG -t planetary -o D:\Night\out
BiggSkyCLI render D:\Night\out\PNG.mat --opt View=Decon --opt Format=JPG --opt Bits=8
REM one derotated image
BiggSkyCLI derotate D:\Night\out\DeconLRGB_View_16b --opt Mode=image
```

```
BiggSkyCLI script D:\Night\commands.txt --json
```

- **Each line** is a whole command line, as typed at a prompt: it starts with `BiggSkyCLI`
  (or `BiggSkyCLI.exe`, or a path to it), and quoting is the command prompt's
  (`"C:\My Captures\jup.ser"`, `--set SuperRes="1 2"`). Blank lines and lines starting with
  `#`, `REM` or `::` are skipped. A script cannot run another script.
- **Every line is checked first.** A usage error on any line (an unknown option, an unknown
  `--opt` name, a line that is not a BiggSkyCLI command) is exit 2 for the script, with its
  line number, and nothing runs.
- **The lines run in turn**, each exactly as a separate call would: its own license check,
  log, record and `result`. `--stop-on-error` stops at the first line that does not exit 0.
- **With `--json`** every line gets `--json` too. After each line comes a
  `{"event":"script","line":…,"command":…,"exit":…,"seconds":…}` event, and at the end a
  `summary`.
- **Exit code:** 0 when every line exited 0, 1 when every line failed, 3 when some did, 2 for a
  usage error in the file.

Measured with the compiled exe, 7 commands (info, two renders, sharpen, compare, derotate to
an image, decon of a stack):

| | separate calls | one script |
|---|---|---|
| info | 6.2 s | 0.2 s |
| render (JPG) | 29.7 s | 3.3 s |
| render (PNG) | 30.1 s | 1.0 s |
| sharpen | 30.9 s | 1.4 s |
| compare | 6.7 s | 0.1 s |
| derotate | 53.8 s | 44.2 s |
| decon of a stack | 33.9 s | 19.3 s |
| **total** | **191.4 s** | **76.5 s** (60% less) |

Short commands gain the most. A long stack or decon gains the same fixed ~15-25 s.

### Server: BiggSkyCLI stays running (`serve` and `BiggSkyClient`)

A script needs every command known up front. When commands come one at a time - from an
agent, a capture program, or a person at a prompt - run BiggSky as a **server** instead, and
send it commands with **`BiggSkyClient.exe`**, a small program that sits beside
`BiggSkyCLI.exe` and starts in a fraction of a second (on macOS and Linux, `BiggSkyClient`
beside the `BiggSkyCLI` command):

```
BiggSkyClient process D:\cap\jup.ser -t planetary -o D:\out --json
BiggSkyClient render D:\out\jup.mat --opt View=Decon --opt Format=JPG
```

```
# macOS, in /Applications/BiggSky/application (Linux: /usr/BiggSky/application)
./BiggSkyClient process ~/Captures/jup.ser -t planetary -o ~/out --json
```

- **Same arguments, same output, same exit code** as `BiggSkyCLI`. Swap the program name and
  nothing else changes: `--json` gives the same events, and the exit code is the command's.
- **The first command starts the server** (`BiggSkyCLI serve`, hidden, about 6 s) and leaves it
  running; every later command skips the start-up. A server started by hand works the same:
  `BiggSkyCLI serve` in a window of its own.
- **Relative paths** are from the folder the client runs in, as with `BiggSkyCLI`.
- **One command at a time.** Commands sent while one runs wait their turn, in order.
- **Control:**

| | |
|---|---|
| `BiggSkyClient --status` | Is a server running, and what is it doing: `{"event":"info","topic":"serve","running":true,"state":"busy","command":…,"served":…}`. Exit 1 when none is. |
| `BiggSkyClient --cancel` | Cancel the running command, as the app's Cancel button. Ctrl+C in the client does the same. |
| `BiggSkyClient --shutdown` | Stop the server. |
| `--no-start` | Fail (exit 1) rather than start a server. |
| `--cli <path>` | The `BiggSkyCLI.exe` to start, if not the one beside the client (or set `BIGGSKY_CLI`). On macOS, `BiggSkyCLI.app` or the `BiggSkyCLI` command; on Linux, the `BiggSkyCLI` command. |
| `--start-timeout S` | How long to wait for a server it starts (default 300 s). |

- **`BiggSkyCLI serve` options:** `--port N` (default 52817), `--idle-exit M` (stop after
  M minutes with nothing to do; default never), `--queue <dir>` (below).
- **Its folder** holds `serve.json`, `serve.log` and the `cancel` file:
  `%LOCALAPPDATA%\BiggSkyCLI\` on Windows, `~/Library/Application Support/BiggSkyCLI/` on
  macOS, `~/.local/state/BiggSkyCLI/` on Linux (or `$XDG_STATE_HOME/BiggSkyCLI/`).
- **Security.** The server listens on 127.0.0.1 only, never the network. Each request must
  carry a random token the server writes to `serve.json` in that folder, which only this user
  can read. One server per user; a second `serve` is refused (exit 2).
- **Its log** is `serve.log` in that folder: every command, its exit code and time. On macOS
  and Linux, a server the client started also writes its console output to `serve.out` there,
  so a server that stops at start says why.

Measured with the compiled programs, the same 7 commands as the script table above, one
`BiggSkyClient` call each:

| | separate `BiggSkyCLI` calls | `BiggSkyClient`, warm server |
|---|---|---|
| info | 6.2 s | 0.2 s |
| render (JPG) | 29.7 s | 3.3 s |
| render (PNG) | 30.1 s | 1.3 s |
| sharpen | 30.9 s | 1.6 s |
| compare | 6.7 s | 0.2 s |
| derotate | 53.8 s | 46.1 s |
| decon of a stack | 33.9 s | 32.3 s first, then 12-19 s |
| **total** | **191.4 s** | **85.0 s**, plus 6 s once to start the server |

The first decon in a server starts the worker pool; later ones reuse it.

#### Queue folder

`BiggSkyCLI serve --queue D:\Night\jobs` also watches `D:\Night\jobs\queue\`. Drop a file in
it and the server runs it, in name order, between client commands:

- a **`.txt`** is a script (one `BiggSkyCLI` line per line, as `script`); relative paths in it
  are from `D:\Night\jobs`
- a **`.json`** is a job file (section 9)

The file moves to `running\` while it runs, then to `done\` (exit 0) or `failed\`, with its
`--json` output beside it as `<name>.ndjson`. To add a file safely, write it under another
name (`.tmp`) and rename it into `queue\` when complete. `D:\Night\jobs\cancel` (any file of
that name) cancels the one running.

### Tool commands

Seven more commands work on images and projects BiggSky has made, without stacking or
deconvolving them again. Each takes its own options as `--opt Name=Value`;
`BiggSkyCLI params --command <tool> [--json]` lists them with their defaults and choices,
and an unknown name is a usage error (exit 2). The common options (`--out`, `--target`,
`--settings`, `--render`, `--gpu`, `--workers`, `--json`, `--log`) work as for `process`.
`info` and `compare` need no license; the others are gated like `process`.

Every tool ends with one `result` event (section 11) listing what it wrote in `outputs`
and what it measured in `info`. The processing tools also write a run record,
`<name>_<tool>_BiggSky.json`, and a log beside their outputs.

| Command | What it does | Main outputs |
|---|---|---|
| `info <file\|folder>...` | Describes, as JSON, what an image (size, channels, bit depth, statistics, a noise estimate), a capture (frames, size, bit depth, camera), a project (every dataset: frames, stack/decon/PSF/noise files, ROI, crop, stack and decon settings), a decon record (the `.mat` beside a decon when run from MATLAB source: settings, blind fit, PSF ROI, version) or a folder holds. A decon image also shows its outcome (section 8). | `info` events, one per input |
| `compare <a> <b>` | Pixel differences: `identical`, `maxAbs`, `rms`, `relRmsPct`, `psnr`, `differingPct`. `--opt Diff=on` writes the difference image. | `info`; `diff` |
| `render <image\|project\|folder>` | Renders data to a View image, as the app displays and saves it: the dataset's crop, white balance and the view's render mode (`--render View=Mode`). | `view`: the PNG/JPG/TIFF |
| `sharpen <image\|project\|folder>` | The app's Unsharp Mask (Amount, Radius in capture pixels, Passes, RollOff auto, halo Clamp) on the chosen view. A folder of images is sharpened frame by frame. | `sharpened` (32-bit TIFF), `view` (PNG) |
| `mosaic <tiles\|folder>...` | Stitches two or more overlapping tiles (any order) into one image, as the app's Mosaic. CPU only. | `mosaic` (16-bit PNG), `mosaic32`, `layout` |
| `derotate <folder\|images\|project>` | **Interpolate Decons to Movie** (`--opt Mode=movie`) or **Combine Decons to Image** (`--opt Mode=image`): derotates timed planetary images. Jupiter, Saturn (globe and rings), Mars. | `frames` + `movie` (MP4), or `image` |
| `dewarpmovie <capture\|project>` | A **Dewarp Movie**: a block of frames stacked on the side, drawn registered, dewarped and as the turbulence map. | `movie`, `dataset` (`.mj2`), `project` |

#### info and compare

```
BiggSkyCLI info D:\out\BiggSky\jupiter.mat --json
BiggSkyCLI compare run1\DeconLRGB_32b\jup_DeconLRGB_32b.tif run2\DeconLRGB_32b\jup_DeconLRGB_32b.tif --opt Diff=on
```

`info` options: `Stats` (on: image statistics and the noise estimate) and `Datasets` (on: a
project's datasets, their files and settings). `compare` options: `Tolerance` (0: the largest
absolute difference still counted as equal) and `Diff` (off: also write the absolute
difference image as a 32-bit TIFF).

#### render

```
BiggSkyCLI render D:\out\BiggSky\jupiter.mat --opt View=Decon --render Decon=ArcSinh --opt Format=JPG --json
BiggSkyCLI render D:\AS4\jupiter_stack.tif -t planetary --opt Bits=8
```

| Option | Default | Meaning |
|---|---|---|
| `View` | `Auto` | `Auto` (Decon, else Calc, else Stack, else Frames), `Frames`, `Stack`, `Decon`, `Calc`. An image input is its Frames view. |
| `Bits` | `16` | `8`, `16` or `32`. |
| `Format` | `PNG` | `PNG`, `JPG` (8-bit) or `TIF` (8, 16 or 32-bit). |
| `Frame` | `1` | A multi-frame dataset (a folder of images): which frame; `0` = every frame. |
| `Dataset` | `0` | A project input: the dataset (0 = the one the project has selected). |
| `BorderCrop` | `-1` | Pixels trimmed from each edge. `-1` = as the app saves the view: the decon's guard band for Decon and Calc. |
| `Rotate`, `FlipLR`, `FlipUD` | `0`, off, off | Turn what is written clockwise by `90`, `180` or `270` degrees, then mirror it left-right or up-down. The data and the project keep the camera's orientation. |

The output is `<out>\<name>_<View>_View_<bits>b.<ext>` (a frame of a folder takes its file's
name). Rendering a project's decon reproduces the decon's own View image to within 1 DN.

#### sharpen

```
BiggSkyCLI sharpen D:\out\BiggSky\jupiter.mat --opt Amount=0.6 --opt Radius=3
BiggSkyCLI sharpen D:\out\BiggSky\DeconLRGB_32b -t planetary          (every image in the folder)
```

| Option | Default | Meaning |
|---|---|---|
| `View` | `Auto` | As for `render`. |
| `Amount` | `0.5` | Peak gain − 1, 0 to 5 (GIMP's Amount for one pass). |
| `Radius` | `4.4` | Capture pixels, 0.3 to 50; scaled by the stack's SuperRes factor. |
| `Passes` | `1` | 1 to 10: peak gain (1 + Amount)^Passes. |
| `RollOff` | `auto` | Finest detail sharpened, 0.02 to 0.5 cycles/pixel. `auto` = where the signal falls to SNR 3 (the stack's noise when it has one, else the image's own floor). |
| `Clamp` | `auto` | Halo clamp, % of the display range; `-1` = off. `auto` is off on Planetary, where the clamp flattens fine structure such as Saturn's rings, and 2 otherwise. |
| `ProtectSky` | on | Fade the sharpening out on the sky and the night side, as the dialog's **Protect sky**, so the noise there is not sharpened. `off` = sharpen everywhere. |
| `Frame` | `0` | A multi-frame dataset: one frame, or `0` = every selected frame. |
| `Bits` | `16` | The View image's bit depth (8 or 16). |
| `Dataset` | `0` | A project input: the dataset. |

#### mosaic

```
BiggSkyCLI mosaic D:\Moon\panels -o D:\Moon\mosaic
BiggSkyCLI mosaic p1.png p2.png p3.png --opt Blend=feather --opt Crop=covered
```

Options: `Name` (default `<tiles folder>_Mosaic`), `Model` (`similarity` | `affine` |
`translation`), `Blend` (`multiband` | `feather` | `none`), `Photometric` (on), `Vignetting`
(on), `Margin` (8 px), `Crop` (`none` | `covered`), `Levels` (0 = automatic), `Interp`
(`cubic` | `linear`). The result's `info` gives the registration error (`registrationRmsPx`),
the tile gains and the vignetting. The mosaic runs on the CPU (`--gpu` does nothing), with
the tile pairs matched on the thread pool (`--workers`). Six 16-megapixel tiles take well under
a minute and a few GB of memory. The output was verified identical to the app's for the same
tiles. The [Mosaic manual](https://biggsky.com/mosaic-manual.html) explains the settings and how to capture the
tiles.

#### derotate

```
BiggSkyCLI derotate D:\out\BiggSky\DeconLRGB_View_16b                    (a movie, 1 frame/min)
BiggSkyCLI derotate D:\out\BiggSky\DeconLRGB_View_16b --opt Mode=image --opt Time=03:30
BiggSkyCLI derotate D:\out\BiggSky\night.mat --opt Source=DeconLRGB_View_16b
```

The keys are two or more images whose names carry their time as WinJUPOS writes it:
`yyyy-mm-dd-hhmm_t` (t = tenths of a minute, UT), e.g. `2023-10-29-0250_4_Jup….png`. A good
source is the decon View images of a night's stacks (`process <folder of stacks>` makes
them). Give one image per time: several frames with the same time are not keys.

| Option | Default | Meaning |
|---|---|---|
| `Mode` | `movie` | `movie` or `image`. |
| `StepMin` | `1` | Movie: minutes between frames. |
| `Time` | middle | Image: the time, `hh:mm` UT or `yyyy-mm-dd-hhmm_t`. |
| `FPS` | `10` | Movie frame rate; `0` = frames only. The MP4 is H.264 when ffmpeg is on the PATH, otherwise MATLAB's own writer. |
| `Planet` | `auto` | `auto` (a WinJUPOS `.ims` beside the keys, or the names' words), `jupiter`, `saturn`, `mars`, `none`. Mars needs an `.ims.xml`. |
| `Clock` | `rotation` | Key times from the measured rotation (or Jupiter's moons), or `ims`. |
| `Moons`, `DiscMoons` | on | Jupiter: moon and shadow transients, and moons on the disk. |
| `ColorBoost` | `1` | Color gain on the output (1 = none). |
| `Lum`, `MaxDtMin`, `Quality` | `auto`, `25`, `1` | Saturn, Mars and mono Jupiter: the luminance filter, the time window, quality weighting. |
| `Source`, `Dataset` | | A project input: a decon folder to take the keys from, and the dataset. |
| `Rotate`, `FlipLR`, `FlipUD` | `0`, off, off | Turn the written frames, image and movie clockwise by `90`, `180` or `270` degrees, then mirror it left-right or up-down. The data and the project keep the camera's orientation. |

Outputs go to `<out>\DRI_<StepMin>min\` (frames and MP4) or `<out>\DRC\` (the image).
Mono images through filters (Jupiter R, G, B; Saturn; Mars) are combined per filter: Mode image
writes each filter's image and the color one, listed in `outputs.products`, with
`outputs.image` the color image. `--opt Planet=none` registers and averages without turning
anything - the way to compose one R, G, B set, or derotated stacks after their decon.

#### dewarpmovie

```
BiggSkyCLI dewarpmovie D:\Captures\jupiter.ser -t planetary --opt Layout=pair --opt Frames=100
```

| Option | Default | Meaning |
|---|---|---|
| `Start` | `best` | First frame, or `best`: the block with the best mean frame quality (the frames are analyzed first). |
| `Frames` | `200` | Frames in the block (at least 10). |
| `Layout` | `quad` | `fused` (registered, colored by the turbulence), `map`, `pair` (registered \| dewarped), `register` (registered \| map), `triple`, `quad` (all four). |
| `Portrait`, `Bin` | off, `1` | Stack the panels vertically; bin by 1, 2, 3, 4, 6 or 8. |
| `Style`, `MagMax` | the stack table's | Map style (`light`/`dark`) and scale; default from `AlignDewarpMap*` (`--set`). |
| `Labels`, `Key`, `Title` | on, on, `BiggSky` | Panel labels, color key, caption. |
| `MovieFormat`, `FPS` | `MPEG-4`, `25` | Also `Motion JPEG AVI`, `Uncompressed AVI`, `GIF`, `PNG`. |
| `AddDataset` | on | Also the lossless `.mj2`, added to a saved project. |
| `KeepWork`, `Reuse` | off, on | Keep the block's stack; reuse a kept one (another layout in seconds). |

Outputs go to `<out>\DewarpMovie\`. The project's own settings are not changed. The frames
are toned by the Frames view's render (`--render Frames=…`).

---

## 4. Inputs

`process` accepts any mix of these:

| Input | Treatment |
|---|---|
| Capture: `.ser`, `.ser.mj2`, `.ser.zip`, `.avi` | Stacked, then deconvolved (`--ops all`). A new one-dataset project is built for it, as **File > New Project** does in the app. |
| Single image: `.tif`, `.fits`, `.png`, … (e.g. an AutoStakkert stack) | **Deconvolved only.** A single image has nothing to stack. With `--ops stack` or `--ops analyze` it is skipped and counted as success. |
| BiggSky project `.mat` | Processed with its own settings and dataset (`--dataset N` picks one). Its target comes from the project unless `--target` is given. **The project file is never rewritten** unless you pass `--save-project on`. |
| Folder | Its `.ser`/`.avi` captures. Subfolders are included only with `--recursive`. BiggSky's own outputs (a `BiggSky` folder **below** the one given, `*_Align1.ser`) are skipped; the folder given, and those above it, may be named anything, so `…\BiggSky\Stack_16b` is a valid input. |
| Folder of images, no captures | **One** input: a multi-frame dataset of its images, in name order, each **deconvolved** - the app's **Process Frames** (e.g. a night's AutoStakkert or WinJUPOS stacks). Each frame gets its own PSF ROI and decon; the decons are listed in `outputs.decon` and added to the saved project as `<name>_Decon`. Output: `<folder>\BiggSky`. `--frames off` takes each image as its own input instead. Checked against the app: 6 of 7 frames of a Jupiter series identical. |
| Wildcard: `D:\cap\*.ser`, `2024-10-19-101*.ser`, `D:\cap\**\*.avi` | The matching files; `**` searches subfolders too. BiggSky's own outputs are skipped, as for a folder. |

How the inputs are run:
- Each input is processed **on its own, one after another**, with its own project, log
  and run record.
- **Mono RGB filter sets:** each filter's SER is stacked and deconvolved independently.
  The CLI does not merge channels.
- **Inputs that don't work:** an input that does not exist, or a file that cannot be
  read, counts as a failure. The other inputs still run.

---

## 5. Options

| Option | Values (default first) | Meaning |
|---|---|---|
| `--target`, `-t` | `lunar` \| `planetary` \| `solar` (also `moon`, `planet`, `sun`) | Required, except for a project that records its own target. |
| `--ops` | `all` \| `stack` \| `decon` \| `analyze` | `all` = stack then deconvolve. `analyze` = frame quality and shifts only, no stack. |
| `--out`, `-o` | folder | Where results go. Default: `<input folder>\BiggSky`. May contain the tokens `{inputDir}`, `{name}`, `{target}`, `{date}` (today, yyyy-MM-dd) and `{jobDir}`, e.g. `--out "D:\Results\{date}\{target}"`. |
| `--settings` | `factory` \| `user` \| `<BiggSkyConfig .mat>` | Which app settings the run uses (section 6). |
| `--profile NAME` | name, `.json` path, or `none` | A settings profile (section 6). Default: `default.json`, when it exists and no `--settings` is given. |
| `--config <file.mat>` | BiggSkyConfig .mat | Use this config file **instead of the app's own** for this run (section 6). It takes precedence over the `BiggSkyConfig2File.mat` pointer, and the pointer is **not** changed. The license is read from it, `--settings user` means its settings, and `license`, `LicenseCheck` and `--freeze-settings` use it too. Command line only: not a job-file key. |
| `--set Name=Value` | repeatable | A stacking parameter (section 7), e.g. `--set FramePercent=10`, `--set SuperRes="1 2"`. `--set ForceBayer=rggb` (or `bggr`, `grbg`, `gbrg`; `off` lets the file decide) debayers with that pattern whatever the file says - for a SER whose header says mono but holds raw Bayer data, or an AVI whose pattern is detected wrong. It sets the Stacking panel's Force Bayer checkbox and pattern; an explicit `--widget` for either wins. |
| `--decon Name=Value` | repeatable | A deconvolution parameter of any tier, e.g. `--decon DenoiseImagePct=50`, `--decon ImageIterations=20`, `--decon PSF_SymPct=30`. |
| `--psf FILE` | `.tif`, `.fits`, `.xisf`, `.png`, or a decon record `.mat` | A PSF the deconvolution reuses **in place of its blind fit**, as the app's **Dataset > Load PSF...** (it ticks ReUse PSF). The path is taken relative to the current folder, or to the job file when it is a job-file key. The PSF must be sampled as the image is - a 2x SuperRes stack needs a 2x PSF, and nothing checks this. An odd-sized PSF is padded by one row and column so it stays centred. A loaded PSF clears any per-tile PSFs from an earlier tiled run. |
| `--save Name=Value` | repeatable | A Save Options setting, e.g. `--save Data16=on`, `--save View8Format=PNG`. |
| `--render View=Mode`, `--render View.Param=Value` | repeatable | How a view's View images are stretched (section 7), e.g. `--render Decon=ArcSinh`, `--render Decon.Gamma=1.2`. Views: Frames, Stack, Decon, Calc. |
| `--gpu` | `auto` \| `on` \| `off`; `--no-gpu` | `auto` uses the GPU when MATLAB can use it. A bare `--gpu` means `on`. If you ask for `on` and there is no usable GPU, the CPU is used with a warning. |
| `--workers` | `auto` \| N | CPU work (when not on the GPU) runs on N threads. `auto` = the performance cores, at most 8 before MATLAB R2025b. `--workers 1` = one thread. Ignored on the GPU. |
| `--format` | `float32` \| `int16` | Format of the deconvolved image. |
| `--dataset N` | integer, list or `all` | Project inputs only: which dataset(s) to process: `--dataset 3`, `--dataset 2,3,4`, `--dataset all` (every dataset that is not itself a decon result). Each is processed in turn; the outputs are lists. |
| `--crop x,y,w,h` \| `auto` | | A crop ROI, applied before the stack and the decon, as the app's **Crop to ROI**: x,y = the 1-based top-left pixel, w,h = the size. `auto` = the app's Auto ROI on the first frame (Lunar/Solar: the most detailed square; Planetary: the disc and a margin). A crop that does not fit the frame is an error. |
| `--frames` | `auto` \| `off` | How a folder of images is taken (section 4). |
| `--widget Name=Value` | repeatable | Set any app control by its name - the escape hatch for a setting with no named parameter, e.g. `--widget TiledProcessingCheckBox=1`. `params --widgets` lists the controls with their factory values. The name's case is corrected; an unknown name is a usage error. Prefer named parameters: they are checked and documented. |
| `--opt Name=Value` | repeatable | A tool command's option (section 3). |
| `--diag autoiter` | | Write the decon's auto-stop tests, at every check, to the log: why each decon stopped when it did. |
| `--save-project` | `auto` \| `on` \| `off`; `--no-save-project` | Save a BiggSky project of the result. `auto` = yes for captures, no for project inputs. A project input with `--out` is saved as a **copy** in `--out`. |
| `--recursive`, `-r` | flag | Include subfolders of folder inputs. |
| `--json` | flag | Machine-readable output: one JSON event per line on stdout (section 11). |
| `--dry-run` | flag | Resolve inputs, settings and output folders, check everything, and process nothing. |
| `--verbose` | flag | Show the engine's own console output instead of only logging it. Not for use with `--json`. |
| `--log <file>` | file | One log for the whole run. Default: one log per input, beside its results. |
| `--host <name>` | text | Name of the program calling BiggSkyCLI. Recorded in each run record. |
| `--save-job <file.json>` | file | Write this command line as a job file and **stop**. Nothing is processed. |
| `--freeze-settings` | flag | With `--save-job`: write into the job, by name, every parameter that `--settings` (or `--config`) and the profile change from the factory settings, plus `"profile": "none"`, so the job stands alone (section 6). |

Options may come before or after the inputs. `--name=value` and `--name value` are both accepted.

---

## 6. Settings: factory, user, frozen or a profile

A run's settings are built in layers. Later layers win:

```
app factory defaults  <  the target's per-panel defaults  <  --settings  <  --set / --decon / --save
```

| `--settings` | What it means | When to use it |
|---|---|---|
| `factory` (default) | The app's built-in defaults. Unaffected by anything changed in the app. | Reproducible scripted runs. |
| `user` | The settings the BiggSky app last **saved** (when it closed) on this computer, including the per-target denoise settings, the per-target stacking table (Stack dropdown's Set Target and table edits, e.g. `SaveNoiseMap`) and the calibration libraries. With `--config`, that file's settings instead. | "Process the rest of the night the way I tuned it in the app." |
| a `.mat` path | A specific saved BiggSky config file. | Several setting profiles. |
| frozen | The app's settings written **into a job file as named parameters**: every stacking, decon and save parameter whose value differs from the factory setting. Made by `--freeze-settings` or the app's **File > Save Settings as Job File**. The job then has no `settings` key (it is factory plus those values). | A job that runs the same next month or on another computer, and says by name what is special about it. It does **not** carry the calibration libraries (darks, flats), or GPU and core choices. |

`user` changes whenever the app saves its settings. If a run must stay repeatable, freeze the settings.

### Profiles: the command line's own settings

The app saves its settings every time it closes, so `--settings user` changes whenever you use the
app. A **profile** is the command line's own settings: a JSON file of named parameters
(section 7) that the app never writes.

```
%USERPROFILE%\BiggSkyCLI\profiles\<name>.json
```

The folder follows `BIGGSKY_PROJECTDIR` when that is set, and `BIGGSKY_CLI_PROFILES` overrides it.

```json
{
  "schemaVersion": 1,
  "description": "My planetary settings",
  "save":  { "Data16": true },
  "decon": { "DenoiseImagePct": 60 },
  "Planetary": {
    "stack": { "FramePercent": 15, "SuperRes": [1, 2] },
    "decon": { "ImageIterations": 20 }
  },
  "Lunar": { "decon": { "DenoiseImagePct": 30 } }
}
```

- **Sections:** the top-level `stack` / `decon` / `save` / `render` apply to every target, and a `Lunar`,
  `Planetary` or `Solar` section (with the same four keys) applies on top for that target.
- **Validation:** names are checked when the profile is read. A profile with an unknown name is
  refused (exit 2), with the file and the name in the message.
- **The default profile:** `default.json` is used for every run that gives **neither `--profile`
  nor `--settings`**. Asking for the app's settings explicitly is never overridden by a default.
  `--profile none` turns it off for one run.
- **Order, weakest first:**

```
factory  <  --settings  <  profile  <  job file / command line
```

**Making and changing profiles:**

```
BiggSkyCLI profile save default -t planetary --settings user          # snapshot the app's settings
BiggSkyCLI profile save default -t lunar --config D:\cfg\Lunar.mat    # ... or another config file's
BiggSkyCLI profile save default -t planetary --decon DenoiseImagePct=60   # change one value
BiggSkyCLI profile save default --save Data16=on                      # a value for every target
BiggSkyCLI profile list
BiggSkyCLI profile show default
BiggSkyCLI profile path
```

- **A snapshot** (`--settings` or `--config`) replaces that target's section with every named
  parameter that differs from the factory settings. A snapshot was verified to resolve to exactly
  the app's settings.
- **An update** (only values typed) changes those values and keeps the rest of the section.
- **Other sections** of the file are always kept.
- **From the app:** tune the settings in BiggSky, close it (it saves them), then snapshot them
  with `profile save NAME -t <target> --settings user`.

**Using one:** `--profile NAME` on the command line, or `"profile": "NAME"` in a job file (a
`.json` path is relative to the job file). `params --profile NAME` shows what it resolves to.

A run record names the profile used (`"profile"`). Its job already holds the profile's values
(with `"profile": "none"`), so re-running the record does not depend on the profile still
existing or being unchanged.

### Which config file

The app keeps all its settings, and normally the license too, in one **config file**.
BiggSkyCLI finds it the way the app does:
1. `<user folder>\BiggSkyConfig2File.mat` (a pointer), if it exists, names the config file.
2. Otherwise the config is `<user folder>\BiggSkyConfig2.mat`.

The user folder is `%USERPROFILE%`, or `BIGGSKY_PROJECTDIR` if that environment variable is set.

`--config <file.mat>` overrides both, **for that run only**:
- **The pointer:** BiggSkyCLI never rewrites it, so the app keeps using its own config.
- **`LicenseCheck --config F`:** saves the refreshed license into `F`. It leaves the
  pointer, and the user folder's `BiggSkyLicense.mat`, exactly as they were.

**The license token** is read from the config. If the config holds no license at all, the
`BiggSkyLicense.mat` that activation saves is used instead, which is the same recovery the
app makes at startup. That file is looked for beside a `--config` file first, then in the
user folder.

A license still only works on the computer and Windows account it was activated for.
Pointing `--config` at a copy of another machine's config does not license this one.

---

## 7. Parameters

Every setting that changes a result has a **name**, so nothing needs the app's internal control
names.
- There are three sections, matching the app's panels. Each has its own option and its own
  job-file key.
- Names mean the same thing everywhere: on the command line, in job files, in `params` and in
  run records.

| Section | Option / job key | What it covers |
|---|---|---|
| Stacking | `--set` / `"stack"` | The Stack tab: its panel controls and its parameter table. |
| Deconvolution | `--decon` / `"decon"` | The Deconvolution panel (Algorithm, Color, Run, Tile), the WB tab's white balance, and the Expert table. |
| Save Options | `--save` / `"save"` | The Save Options panel. It applies to both stacking and decon output. |

Each stacking and decon parameter belongs to one **tier**. All tiers are accepted; the tier only
says how specialized a parameter is.

| Tier | What it is | Listed by |
|---|---|---|
| **Standard** | What the panels show: frames %, auto frames, super-res and calibration; iterations, denoise, decon %, color boost, luminance, tiles, white balance | `params` |
| **Expert** | The parameter tables (the decon Expert table, the stack table) | `params --expert` |
| **AdvancedExpert** | Tuning that only makes sense with the measurements behind it, plus A/B switches | `params --all` |

`BiggSkyCLI params --target planetary` shows every Standard parameter and Save Option with its
current value, and with `--settings user` (or `--config`) marks what differs from the factory
setting. With `--expert` or `--all`, each regular Expert-table row is followed by a line or two
saying what it does, the same help the app's Expert table shows. `--json` lists every tier with
its name, tier, label, kind, choices, value and factory value, and `help` where there is one.

**Unknown names are refused before anything runs** (exit 2), so a misspelled parameter never silently
does nothing.

### Stacking (`--set`), Standard

| Name | App control | Planetary default |
|---|---|---|
| `FramePercent` | Frames % | 25 (used when `AutoFrames=0`) |
| `AutoFrames` | Auto | 1: BiggSky picks the frame count from the quality curve |
| `SuperRes` | Super Res | 1, or a list such as `"1 2"` for several stacks. Each stack is deconvolved, as the app's Do It All does (section 8). |
| `AutoFlat`, `AutoDark` | Auto Flat, Auto Dark | 0 |
| `FixHotPixels`, `FixPatternNoise` | Fix Hot/Cold Pixels, Fix Pattern Noise | 1 |
| `StackSuffix` | Suffix | empty. Tags the stack, and so its decon, once (`<name>_Stack2_<suffix>_F1709_Decon_32b.tif`), and the run's project, record and log (section 8). |
| `Derotate`, `DerotateAvg` | Derotate, Avg # | 0 |

### Deconvolution (`--decon`), Standard

| Name | App control |
|---|---|
| `PSFIterations`, `ImageIterations` | PSF Iter., Image Iter. (`-1` = automatic on that stage) |
| `AutoIterations` | Auto Iter. |
| `DenoisePSFPct`, `DenoiseImagePct` | Denoise PSF / Image (%) |
| `DeconPct` | Decon % (strength) |
| `MoreDecon` | More Decon |
| `ColorBoost` | Color Boost (1 = off) |
| `PSFFromLuminance`, `LuminanceOnly`, `ChannelAlignment` | PSF from Luminance, Process Luminance Only, Channel Alignment |
| `Tiled`, `TileSize`, `TileOverlap`, `MaskBackground` | Tiled Processing, Tile Size (native pixels), Overlap, Mask Bkg |
| `Background`, `CaptureGamma` | Bkg, Capture Gamma (1 = none) |
| `BlendDecon`, `BlendDeconPct` | Blend Decon %, and the amount |
| `ReusePSF` | ReUse PSF |
| `PSFFromROI` | PSF from ROI. On by default, off when `Tiled` is on (each tile then fits its own PSF). |
| `StarsOnly`, `UseStarless` | Stars Only, Use Starless (DSO; off for Lunar, Planetary and Solar) |
| `DeconSuffix` | Suffix (the Decon panel's own). Empty by default. An extra tag on the decon, View and PSF names and folders (`Decon_<suffix>_32b`). For a decon-only run of an image or a project, this is the suffix to use: `StackSuffix` there tags nothing. |
| `WhiteBalance`, `AutoWhiteBalance`, `AutoBackground` | The WB tab: Use White Balance, Auto W.B., Auto Background (the old name `BackgroundBalance` still works) |

The per-target defaults come from the app's own target handler. For example, Lunar processes
luminance only, so its decon is `Decon_32b` rather than `DeconLRGB_32b`.

The blind PSF is fitted where the app's Do It All fits it (`PSFFromROI`, on by default):

- **Lunar and Solar:** an automatic square on the most detailed part of the disk, sized from
  `TileSize` and the SuperRes scale. The log line `PSF ROI 562x562 at [75 63]` shows it.
- **Planetary:** the disk plus a margin, which the decon picks itself (`PSF ROI chosen from the image`).
- **Tiled** (`--decon Tiled=1`): `PSFFromROI` goes off, as ticking Tiled Processing does in the app,
  and each tile fits its own PSF. `--decon PSFFromROI=1` keeps it on. The app then fits one PSF on the
  square and reuses it for every tile, but the CLI does not do that two-pass run yet.

Up to v0.5.34 no CLI decon used the lunar square, so lunar results differed from the app's.

### Save Options (`--save`)

| Name | App control | Default |
|---|---|---|
| `Data32`, `Data32Format` | 32 bit Data | on, `TIF` (also `TIF PS`, `FITS`, `XISF`, `MAT`) |
| `Data16`, `Data16Format` | 16 bit Data | off, `TIF` (also `FITS`, `XISF`, `PNG`, `MAT`) |
| `View8`, `View8Format` | 8 bit View | off, `TIF` (also `FITS`, `XISF`, `PNG`, `JPG`, `MAT`) |
| `View16`, `View16Format` | 16 bit View | on, `TIF` |
| `View32`, `View32Format` | 32 bit View | off, `TIF PS` |
| `UseImageFolder`, `SubFolders` | Use Image Dir, Sub-Dir | on |
| `CreateProject` | Create Project | on |
| `AutoSave` | Auto Save (Settings tab) | on |

On/off values may be written `on`/`off`, `1`/`0` or `true`/`false`. `--format float32|int16` is a
shorthand for Data32 or Data16; `--save` wins over it.

**View images vs Data images.** The 8/16/32-bit View images are the result as the app displays
it: stretched with the render settings and white-balanced, and ready to share. The Data images are
the unstretched values, for further processing. Command-line runs write both. The View images use
the render settings the app saved when the run uses `--settings user` (or `--config`), and the
app's defaults for a new dataset otherwise.

### Render (`--render`): how the View images are stretched

Each view has a **render mode** and that mode's parameters. These are the app's Render tab
settings, kept per view (Frames, Stack, Decon, Calc) and per target. They decide how the
8/16/32-bit **View** images are stretched. They never change the Data images.

```
BiggSkyCLI process jup.ser -t planetary --render Decon=ArcSinh --render Decon.Gamma=1.2
```

```json
"render": { "Decon": { "Mode": "ArcSinh", "Gamma": 1.2 }, "Stack": { "ClipHigh": 0.05 } }
```

| Mode | Parameters (planetary defaults) |
|---|---|
| `Clip` | `ClipLow` 0.1, `ClipHigh` 0.01, `Gamma` 1 |
| `SoftClip` | `ClipLow`, `ClipHigh`, `DisplayLow` 10, `DisplayHigh` 90, `Gamma` |
| `AutoRange`, `FullRange`, `Range8bit`, `Range16bit` | `Gamma` |
| `ManualRange` | `Min`, `Max`, `Gamma` |
| `Linear` | `MaxPercent` 100 |
| `Direct` | `Max` 1 |
| `Reinhard` | `Key` 0.18, `Gamma` 1.5, `ClipLow` 1, `Offset` 10 |
| `Logarithmic` | `MinThreshold` 10, `ClipLow`, `ClipHigh` 0.1, `Gamma` |
| `ArcSinh` | `Stretch` 5 (50 for DSO), `ClipLow` 0.1, `ClipHigh` 0.1, `Gamma` 1, `ColorPreserve` 1 |
| `CLAHE` | `Source` (`Clip`, `ArcSinh` or `Reinhard`; `Clip` for planetary, lunar and solar, `Reinhard` for DSO), `ClipLimit` 0.005, `NumTiles` 8, `Distribution` (`uniform`, `rayleigh` or `exponential`; `exponential`, `rayleigh` for Lunar/Solar) |

`params --json` lists them with the exact defaults for the target (`renderModes`). The modes and
defaults come from `renderModeDefaults`, the same definition the app uses.

How render settings apply:
- **Modes and parameters:** `--render View=Mode` sets the mode; `--render View.Parameter=Value`
  sets a parameter. Parameters you don't give keep their current value if the mode is unchanged,
  and otherwise come from the new mode's defaults.
- **Unknown names:** a parameter the mode doesn't have, an unknown mode or an unknown view is
  refused before anything runs (exit 2). If you give a parameter without a mode, it is checked
  against the mode that view will have under the run's settings.
- **Profiles:** `profile save` refuses a parameter without a mode if the factory mode for that
  view doesn't have it, e.g. `--render Decon.Key=1` (`Key` is Reinhard's). Give the mode as well:
  `--render Decon=Reinhard --render Decon.Key=1`.
- **What a run starts from:** each view starts from what the app would use. That is the
  dataset's own render settings if it has them, else the Render tab's **Default** for the target
  and view, else the target's default. `--settings user` and `--config` bring in the app's saved
  Defaults.
- **Layering:** in profiles and job files, a layer that changes a view's mode replaces that view
  from the layers below. Another mode's parameters never mix in.
- **What a run records:** every view's mode and parameters, in `params.render`.

### One home per parameter (changed 2026-10-11)

Nine decon parameters used to exist twice: as a panel control and as an Expert-table row. Which
one counted depended on the parameter. For example, with Expert on, the table's denoise
percentages were silently ignored.

Each now lives in its panel control only, and the Expert table no longer shows those rows. The
decon always took the iteration counts and Auto Iter. from the controls, so no result changed.

The old table names `DeTunePercent`, `SaveToImgDir` and `SaveFramesSubDir` are still accepted and
are applied to their new homes (`DeconPct`, `UseImageFolder`, `SubFolders`).

### Renamed parameters

A parameter that is renamed keeps working under its old name, on the command line, in profiles,
in job files and in run records, so nothing written for an earlier version breaks:

| Old name | New name |
|---|---|
| `SharpenAmount`, `SharpenRadius`, `SharpenHaloClamp`, `SharpenPasses`, `SharpenRollOff` | `UnsharpAmount`, `UnsharpRadius`, `UnsharpHaloClamp`, `UnsharpPasses`, `UnsharpRollOff` (the decon's post-sharpen rows, named after the Unsharp Mask dialog they belong to) |
| `BackgroundBalance` | `AutoBackground` (the WB tab) |

---

## 8. Output

Results are laid out exactly as the app lays them out, under the output folder:

```
<out>\
  Stack_16b\<name>_Stack2_F0100.tif                   the stack
  Decon_32b\ or DeconLRGB_32b\<…>_Decon…_32b.tif      the deconvolved image
  Decon_32b\ or DeconLRGB_32b\<…>_Decon…_32b.json     what that decon did
  Decon…_PSF_32b\<…>_PSF_32b.tif                      the PSF
  NoiseMap\<name>_<dataset>_F0100_NoiseModel.mat      the noise model
  Analyze\<name>_<dataset>_StackAnalyze.csv / .png    frame-quality analysis
  <name>.mat                                          a BiggSky project of the result (opens in the app)
  <name>_BiggSky.json                                 the RUN RECORD
  <name>_BiggSkyCLI_<yyyyMMdd_HHmmss>.log             the engine log
```

With a suffix, the last three carry it: `<name>_<suffix>.mat`, `<name>_<suffix>_BiggSky.json` and
`<name>_<suffix>_BiggSkyCLI_….log`. That is `StackSuffix` when the input is stacked and `DeconSuffix`
when it is deconvolved, both if both are set. So a suffixed run does not overwrite an earlier run's
project and record. A project input's own `--save-project` copy keeps the project's name. The
stack's suffix appears once in the stack and decon names, and the noise model and analysis names
carry it too (`<name>_Frames_<suffix>_F1709_NoiseModel.mat`).

- **Run record** (`<name>_BiggSky.json`). It contains:
  - the BiggSky version, the time (UTC), the input, the target, the license product and GPU use
  - the **job exactly as it applied to this input**
  - `params`: every resolved stacking and decon parameter, and the decon controls actually used
  - `outputs` and `seconds`
  - `decon`: for each decon, its auto PSF and image iteration counts, why the image loop
    stopped (`stop`) and the PSF ROI it used - so a run can be judged without the log
  - on failure, `ok: false` and `error`

  It is also a job file: `BiggSkyCLI run <name>_BiggSky.json` repeats that input with the
  same settings.
  - **Exact repeats need one thread.** With `--workers 1`, a re-run was verified
    byte-identical.
  - **The default thread pool** (`--workers auto`) gives results that differ between runs
    by rounding: at most 1 DN, 0.0002% rms.
- **Frame analysis** (`_StackAnalyze.csv`). One row per frame, in capture order, with a header:
  - `Frame`: 1, 2, ...
  - `Quality`: the stacker's score. It grows with brightness squared, so `Quality / Brightness^2`
    is the score with the light taken out.
  - `Brightness`: planets, the light above 10% of the frame's peak, over the frame's median, per
    pixel of the frame; other targets, the frame's mean level. `NaN` when the frames were analyzed
    by an earlier version and not analyzed again.
  - `Stacked`: 1 for a frame chosen for the stack. With a frame sweep, the largest stack's frames;
    with time points, every time point's so far.

  Up to v0.6.0 this file was one column of quality with no header.

  `--ops analyze` runs only this part, as the app's **Analyze** button does: it always measures the
  frames again, and writes this file and its plot. `Stacked` is then the frames the frame rule would
  keep from the whole capture, because no stack runs and time points do not apply. A later stack or
  analysis of the same dataset replaces both files. The project and record are
  `<name>_Analyze.mat` and `<name>_Analyze_BiggSky.json`, so an earlier full run's stay as they
  are. For a project input, add `--save-project on` to keep the measurements in the project.
- **Decon outcome** (`<decon name>.json`, beside each decon). Plain JSON, written by every run from
  the app and from the command line, for every decon except a tiled one:
  - `psfIterations`, `imageIterations` (one per channel when the channels are deconvolved
    separately; the count the auto-stop chose, which can be below the loop it stopped at), and
    whether the PSF was reused (`psfReused`, `psfFrom`)
  - `stops`: each loop that stopped automatically, in order, with its `stage`, `iterations`,
    `reason` and `noiseBandwidth`. The last with stage `image` is why the decon ended.
  - `psfTwoStage`: the limb-fit `edgeSigmaPx` and the `interiorWiden` the data supported
  - `psfROI`: the PSF ROI actually used, when one was chosen
  - `deconFile`, `stack`, `noiseModel`, `dataset`, `version`, `saved`

  `info <decon image> --json` includes it as `deconOutcome`.
- **Log.** All engine output goes here. When a run fails, read the log; the console only
  says that it failed.
- **A SuperRes list** (`--set SuperRes="1 1.5"`) makes one stack per scale, and each is deconvolved
  in the same run, with its own noise model, as the app's Do It All does: one decon, PSF and View
  image per stack. The 1.5x files carry `_SuperRes15` in their names. In the project each scale is a
  dataset of its own (`Frames_Stack`, `Frames_Stack_SuperRes15`), after the capture's `Frames`. (Up to
  v0.5.33 only the 1x stack was deconvolved.) A frame sweep (`--set FramePercent="5 10"`) still
  deconvolves only the largest stack; `outputs.stack` lists every stack of the sweep.
- **Time points** (`--set MultiTimePoint=1 --set MaxClipTimeSec=60`: one stack per 60 s clip;
  `MultiTimePoint=5` stops after the first 5) make one stack per clip, `…_Stack2_T01_F….tif` and on,
  each named with WinJUPOS-style time (`2025-01-01-2317_5-U-…`), and **each is deconvolved** with its own
  noise model, as the app's Do It All does (Process Frames on the time-point dataset). `outputs.stack`
  and `outputs.decon` list every one, and the record's `decon` has one entry per time point. (Up to
  v0.5.42 only the last time point was deconvolved.)
- **Existing outputs** with the same names are overwritten, as in the app. That includes the run
  record: a later run on the same input name and suffix into the same folder - for example a decon-only
  run on the saved project (`process <name>.mat --ops decon`) with no `DeconSuffix` - replaces `<name>_BiggSky.json`, so the
  record always describes the images the last run wrote (the stacking settings are also kept in the
  project). To keep both records, give the later run another `--out` or copy the record first. With a
  SuperRes list there is no need for that second run any more.

---

## 9. Job files

A job file is JSON. It has optional shared `defaults`, then a list of `jobs`, each with its own
inputs and any option from section 5 as a key.

```json
{
  "schemaVersion": 1,
  "description": "Jupiter and the Moon, 2026-10-10",
  "_comment": "keys starting with _ are comments",
  "defaults": {
    "target": "planetary",
    "settings": "user",
    "out": "D:/Results/{date}/{target}/{name}"
  },
  "jobs": [
    { "inputs": ["Jupiter/*.ser"], "stack": { "FramePercent": 15, "AutoFrames": 0 } },
    { "input": "Moon", "target": "lunar", "ops": "stack", "workers": 6 }
  ]
}
```

```
BiggSkyCLI run night.json
BiggSkyCLI run night.json --dry-run
BiggSkyCLI run night.json --out D:\Elsewhere --set MinFrames=3 --json
```

The keys:
- `inputs` (or `input` for one), `recursive`, `target`, `ops`, `out`, `settings`, `profile`,
  `gpu`, `workers`, `format`, `dataset`, `saveProject`
- `stack`, `decon` and `save`, each an object `{ "Name": value }` of the named
  parameters in section 7
- `render`, an object keyed by view: `{ "Decon": { "Mode": "ArcSinh", "Gamma": 1.2 } }`

Rules:
- **Without `jobs`,** the top level is a single job.
- **Relative paths are relative to the job file's folder,** so a job file can sit beside
  its captures and move with them. Paths typed after `run` stay relative to the current folder.
- **Values** may be numbers, text, `true`/`false`, or lists (`"SuperRes": [1, 2]`).
- **Strict checking.** All of these are refused before anything runs, with exit 2 and a
  message naming the file and entry (e.g. `night.json jobs[2]: …`):
  - an unknown key (other than `_` comments)
  - a bad value
  - an unknown parameter name
  - an unknown `{token}` in `out`
  - a missing `inputs`
  - invalid JSON
  - a newer `schemaVersion` than this BiggSkyCLI reads
- **Options typed after `run` override the file.** `--set`, `--decon` and `--save`
  add to the file's parameters rather than replacing them.
- **One numbered list.** All jobs' inputs are numbered together as `[k/N]`, and one
  summary covers the whole run.

There are three ways to make a job file:
1. **From a command line:** add `--save-job night.json`. Add `--freeze-settings` to embed
   the app settings.
   ```
   BiggSkyCLI process D:\Captures\*.ser -t planetary --settings user --set FramePercent=10 --freeze-settings --save-job night.json
   ```
2. **From the app:** **File > Save Settings as Job File…**. It saves:
   - a frozen copy of the app's settings
   - every stacking parameter and control that differs from the target's defaults
   - the Expert decon entries, if Expert is on

   Its input is the current capture. Replace it with a folder or wildcard to process a
   whole session.
3. **From a run record:** any `<name>_BiggSky.json`.

Only settings that differ from the defaults are written, so a job file reads as a list of what is
special about the run.

---

## 10. Exit codes

| Code | Meaning |
|---|---|
| 0 | Every input succeeded. Also returned for `--dry-run`, `--save-job`, `params`, `license` when licensed, `version` and `help`. |
| 1 | Failure: every input failed, nothing was found to process, or an internal error. |
| 2 | Usage error: a bad option, value, parameter name or job file. Nothing was run. |
| 3 | Some inputs succeeded and some failed. |
| 77 | **Not licensed:** no license, expired, the wrong tier, not activated for this user or machine, or the license lapsed part-way through a batch. The same code RC-Astro uses. Nothing further was run. |

---

## 11. Machine interface (`--json`)

With `--json`, **stdout carries only JSON objects, one per line**, each flushed as it is written.
- **Errors** are events on stdout too, and stderr stays empty.
- **The engine's own output** goes to the log file, never to stdout.
- **Unknown keys and event types** may be added in later versions. Ignore what you don't
  recognize.

Every object has an `"event"` field.

| `event` | Fields | When |
|---|---|---|
| `info` / `topic: "version"` | `cliVersion`, `schemaVersion` | Always the first line. |
| `info` / `topic: "license"` | `valid`, `product`, `license`, `expires`, `daysLeft` (null = perpetual, or unknown when not valid), `email`, `seat`, `reason` (when not valid) | Before processing. |
| `info` / `topic: "jobFile"` | `files`, `jobs` | `run`, after the job files are read and checked. |
| `info` / `topic: "dryRun"` | `file`, `files`, `input`, `job` (index), `out`, `resolved` (the job for this input), `gpu` | `--dry-run`, one per input. |
| `info` / `topic: "jobSaved"` | `file` | `--save-job`. |
| `info` / `topic: "profile"` | `job`, `name`, `file`, `default`, `applied` (how many values it added) | A profile was applied to a job. |
| `info` / `topic: "profiles"`, `"profileSaved"`; `profile` | the profile list / the saved file / a profile's sections | `profile list`, `profile save`, `profile show`. |
| `info` / `topic: "licenseCheck"` | `reached`, `saved`, `serverMessage`, `error` | `LicenseCheck`. |
| `params` | `target`, `settings`, and `stack`, `decon`, `save`: lists of `{name, tier, label, kind, choices?, value, factory}` | `params --json`. |
| `status` | `phase`: `loading` (with `input`), `stacking`, `analyzing`, `decon`, `skipped` (with `message`) | Lifecycle of each input. |
| `progress` | `stage` (`stack`/`decon`), `done` (0–100, **of the current step**), `eta` (s, optional), `message` (the step, e.g. "Stacking images (812/2000)…Pass 1") | During processing. Throttled to about 5% steps or one per second. `done` restarts at 0 for each step (loading, analysis, pass 1, pass 2, decon), so read `message` to tell the steps apart. |
| `warning` | `message` | Non-fatal, e.g. the license expires within 7 days, or there is no GPU. |
| `error` | `message`, optional `input`, optional `code` | Fatal for that input, or for the run. |
| `result` | `ok`, `input`, `outputs` {`stack`, `decon`, `psf`, `noisemap`, `noisemodel`, `other` [list], `project`, `record`}, `decon` [list], `seconds` | One per input. An output is a path, or a list of paths for a SuperRes list, `--dataset` list or Process Frames (`stack`, `decon`, `psf`, `noisemodel`). `other` lists only files this input wrote. `decon`: per decon `dataset`, `psfIterations`, `imageIterations`, `stop`, `psfROI`. |
| `result` (tools) | `ok`, `command`, `input` [list], `outputs` (per tool: `view`, `sharpened`, `mosaic`, `layout`, `frames`, `movie`, `image`, `diff`, `record` ...), `info` (what it measured), `seconds` | One per tool run (section 3). |
| `info` / `topic: "file"` | `file`, `info` | The `info` command: one per input. |
| `summary` | `files`, `ok`, `failed`, `skipped`, `seconds` | End of `process` or `run`. |

In a run with more than one input, `status`, `progress`, `warning`, `error` and `result` also carry `file` and
`files` (1-based index and total).

Example (one capture, trimmed):

```
{"event":"info","topic":"version","cliVersion":"v0.5.30","schemaVersion":1}
{"event":"info","topic":"license","valid":true,"product":"BIGGSKY_UNIVERSE","license":"PERPETUAL","expires":"Perpetual","daysLeft":null,"email":"…","seat":"1 of 1 (Max 3)"}
{"event":"status","phase":"loading","input":"D:\\Downloads\\2024-01-18-1810_6-Jupiter.ser"}
{"event":"status","phase":"stacking"}
{"event":"progress","stage":"stack","done":43.8,"eta":2.2,"message":"Stacking Analyze..."}
{"event":"progress","stage":"stack","done":100,"message":"Stack Pass 1 (100/100)..."}
{"event":"status","phase":"decon"}
{"event":"progress","stage":"decon","done":60,"eta":15}
{"event":"result","ok":true,"input":"D:\\Downloads\\2024-01-18-1810_6-Jupiter.ser","outputs":{"stack":"…\\Stack_16b\\…_Stack2_F0100.tif","decon":"…\\DeconLRGB_32b\\…_DeconLRGB_32b.tif","psf":"…","noisemodel":"…","other":["…StackAnalyze.csv","…StackAnalyze.png"],"project":"…\\2024-01-18-1810_6-Jupiter.mat","record":"…\\2024-01-18-1810_6-Jupiter_BiggSky.json"},"seconds":42.2}
{"event":"summary","files":1,"ok":1,"failed":0,"skipped":0,"seconds":42.3}
```

### Guidance for automation and AI agents

To have an AI agent drive BiggSky, give it this manual as one Markdown file:
`BiggSkyCLI-Manual.md`, installed beside `BiggSkyCLI.exe` and published at
[biggsky.com/BiggSkyCLI-Manual.md](https://biggsky.com/BiggSkyCLI-Manual.md). It is written to be handed to an
agent as-is. These rules are the ones to follow:

1. **Always pass `--json`, and parse stdout line by line.** Never parse the human-mode text;
   its wording can change.
2. **Check the license first:** `BiggSkyCLI license --json`.
   - If `valid` is false, report `reason` to the user. Exit 77 is never fixed by retrying.
   - Run `LicenseCheck` **only when the user asks** (e.g. after renewing). It contacts the
     license server and rewrites the app's saved config.
   - Never run `LicenseCheck` while the BiggSky app is open.
3. **Discover parameters, don't guess them:** `BiggSkyCLI params --target <t> --json`.
   Use only names from that list. Prefer Standard-tier names; reach for Expert and
   AdvancedExpert only when the user asks for that tuning. For a tool command,
   `params --command <tool> --json`; for app controls, `params --widgets --json`.
4. **Look before you process, and check after,** without MATLAB or an image viewer:
   - `info <file|project> --json` tells you what an input is (a capture's frames, a
     project's datasets and their files, a decon record's settings and stop reasons).
   - `render <result> --opt Format=JPG --opt Bits=8` gives you a View image to show the
     user (or to look at yourself); its path is `result.outputs.view`.
   - `compare <a> <b> --json` says whether two results differ, and by how much -
     re-runs, settings A/B tests, CLI against app.
   - The run record's `decon` entries say how each decon stopped.
5. **Dry-run first:** `--dry-run --json` resolves every input and output folder and runs
   every check without processing. Show the user what will happen before starting a long batch.
6. **Don't pay the start-up per command.** Every `BiggSkyCLI.exe` call costs 15-30 s before
   any work. Use **`BiggSkyClient`** with the same arguments (section 3, "Server"): the first
   call starts a server, later calls take 0.2 s for a quick command. When the commands are
   known up front, `BiggSkyCLI script <file> --json` does the same in one process.
7. **Check for a default profile:** `BiggSkyCLI profile list --json`. If `default` exists, every run
   without `--settings`/`--profile` uses it; pass `--profile none` for factory settings.
8. **Prefer a job file for anything non-trivial.** It is reviewable and repeatable, and it
   becomes part of each run record. Write it, validate it with `run job.json --dry-run --json`,
   then run it.
9. **Use the exit code as the verdict:** 0, 1, 2, 3 or 77 (section 10). For per-input
   detail, read the `result` events, or the `ok` field of each `<name>_BiggSky.json`.
10. **Check the outputs exist** on disk at the paths in `result.outputs` before using them.
11. **On failure, read the log** named beside the record. The `error` message is a summary only.
12. **Don't touch the user's files beyond what was asked:**
   - Write to a `--out` folder the user chose.
   - Do not pass `--save-project on` for a project input unless the user asked to update
     that project.
   - Outputs with the same names are overwritten.
13. **Run one BiggSkyCLI at a time.** Runs are GPU- and memory-heavy. Two at once have not
    been tested, and the second will compete for the same GPU. The server runs one command
    at a time by design: send the next when the last has answered, or queue them.
    Don't run `BiggSkyCLI.exe` beside a busy server.
14. **Budget time.** Each `BiggSkyCLI.exe` invocation spends about 15–20 s starting up
    (`BiggSkyClient` to a running server: none). Measured examples
    (single-threaded on the CPU, before `--workers` defaulted to `auto`):
    - stack plus decon of a 2000-frame color SER at 5% of the frames: about 40 s on the CPU
    - a 4000-frame mono SER at 5%: about 15 s on the GPU
    - a lunar SER at 10%: about 33 s on the GPU

    Runs using the full frame count, and large lunar frames, take minutes.
15. **To cancel** a command sent with `BiggSkyClient`, run `BiggSkyClient --cancel` (or press
    Ctrl+C in the client): the command stops at its next check, reports `"Cancelled."` and exit 1,
    and inputs not yet run are `skipped`. The server stays up for the next command. A
    `BiggSkyCLI.exe` run is canceled by ending its process. Partially written outputs may
    remain for the input that was running.
16. **Use `--host <your-name>`** so each run record says which program made it.

---

## 12. Licensing details

- **Every check is offline.** The license stored by the app (a token signed by the
  BiggSky license server, kept in the config file or in `BiggSkyLicense.mat`; see section 6)
  is verified on each run, along with this machine's hardware and this user account. BiggSkyCLI never contacts the server except in `LicenseCheck`.
- **Expiry is checked before every input,** so a batch or a process left running stops
  cleanly when the license lapses. The inputs not yet run are reported as `skipped`, with
  exit 77.
- **Within 7 days of expiry,** every run warns: "run BiggSkyCLI LicenseCheck". After
  renewing a subscription, run `LicenseCheck`, or open the app and press Validate, to
  fetch the new expiry.
- **Moving the computer's clock back** does not extend a license.

---

## 13. Troubleshooting

| Symptom | Cause, and what to do |
|---|---|
| Exit 77, "No BiggSky license on this computer for this user" | Open the BiggSky app once, under this Windows account, and activate. |
| Exit 77, "<file> holds no license, and there is no BiggSkyLicense.mat…" | The `--config` file has no license. Use the app's own config, or put the `BiggSkyLicense.mat` beside it. |
| Exit 2, "Config file not found" / "is not a BiggSky config file" | Check the `--config` path. It must be a BiggSkyConfig .mat saved by the app. |
| Exit 77, "activated for a different BiggSky installation or Windows user account" | Run BiggSkyCLI as the user who activated BiggSky. Not as a service or SYSTEM. |
| Exit 77, "expired on …" | Renew, then `BiggSkyCLI LicenseCheck`. |
| Exit 77, "… does not include the BiggSky command line" | The license tier is SSE Lite, SSE Free or Free. The CLI needs Universe, SSE Pro or DSO. |
| Exit 2, "unknown stacking / deconvolution / Save Options parameter(s)" | Check spelling with `BiggSkyCLI params --target … --all`. |
| Exit 2, "a target is required" | Add `--target` (or `"target"` in the job). Only project inputs can omit it. |
| A result is missing from `outputs` | Read the log. Single images are deconvolved only. `--ops stack` writes no decon. |
| `--gpu on` warns "no usable GPU" | This MATLAB release cannot use this GPU (e.g. RTX 50-series needs the R2026a build). The run continues on the CPU. |
| Slow on the CPU | `--workers auto` is the default. Check the record's `gpu` field, and whether `--workers 1` was set. |
| `BiggSkyClient`: "the server stopped at start (exit 77)" | The server checks the license when it starts. Run `BiggSkyCLI license` to see why. On macOS and Linux the client also names `serve.out`, the server's own output. |
| Linux: "BiggSkyCLI needs the MATLAB Runtime R2024b, which was not found" | Run the BiggSky installer again (it fetches the runtime), or `export MCRROOT=/path/to/MATLAB_Runtime/R2024b`. |
| Linux: `bin/BiggSkyCLI` stops naming a missing `.so`, or `--json` output starts with "Setting up environment variables" | That is the program itself or MCC's `run_BiggSkyCLI.sh`. Use the `BiggSkyCLI` command in `/usr/BiggSky/application`. |
| macOS: `BiggSkyCLI.app/Contents/MacOS/BiggSkyCLI` dies with "Library not loaded: @rpath/libmwlaunchermain.dylib" | The program inside the bundle cannot be started by itself. Use the `BiggSkyCLI` command beside the bundle. |
| `BiggSkyClient` hangs with no output | A long command is running, or another client's command is ahead of it. `BiggSkyClient --status` shows what the server is doing. |
| `BiggSkyCLI serve`: "already running for this user" | One server per Windows user. Use it, or `BiggSkyClient --shutdown` first. |
| `BiggSkyCLI serve`: "Port 52817 … is in use" | Another program has the port. Use `--port`; the client reads the port from `serve.json`. |

---

## 14. Not in this version

- DSO targets, the app's color/merge steps for mono RGB sets, and star removal.
- Process Frames on one capture file (a SER deconvolved frame by frame, unstacked), and a frame
  sweep deconvolved frame by frame (a folder of stacked images, and a time-point series, are
  supported).
- A tiled decon with PSF from ROI switched back on does not do the app's two-pass run (one PSF
  on the ROI, reused for the tiles).
- Derotate Stacks (time-point stacking with derotation) beyond what `--set Derotate=1` does.
- A watch folder that processes **captures** as they arrive (the server's queue folder runs
  command files, not captures).
- An MCP server for AI agents (planned). Until then, agents use the CLI as described in section 11.
- Graceful cancel of a `BiggSkyCLI.exe` process (use the server and `BiggSkyClient --cancel`),
  and running several BiggSkyCLI processes or servers at once.
