--json and --jsonl
Every verb that reports on files takes --json (one object on stdout) or --jsonl (one object
per target, hit, file, count row or edit, followed by one trailing object carrying stats and
omitted). The same information renders as text, --json or --jsonl — a field exists in all
three or in none. Shapes below are copied from real lets 0.0.1 output (LETS_NO_STATS=1, which
is why tokens_est reads null).
show --json
{"targets":[{"target":"usage.ts","path":"usage.ts","start":1,"end":9,"total":9,
"sha":"14dc685cb937","lines":[{"number":1,"marker":"none","text":"import { usageCap } from './config'"}]}],
"omitted":[],"stats":{"lines":9,"bytes":189,"tokens_est":null}}
A #symbol target adds a "resolver" key ("tree-sitter" or "via heuristic (plaintext)"
text, depending on the verb). --jsonl prints one such object per target, no targets wrapper,
then {"stats":{...},"omitted":[]}.
find --json
{"targets":[{"target":"usage.ts","path":"usage.ts",
"lines":[{"number":1,"marker":"hit","text":"import { «usageCap» } from './config'"}]}],
"omitted":[],"stats":{"lines":1,"bytes":40,"tokens_est":null}}
--files is {"files":["usage.ts"],"omitted":[],"stats":{...}}. --count is
{"counts":[{"count":1,"path":"usage.ts"}],"omitted":[],"stats":{...}}. Over the cap, targets
is empty and the top-files map appears under its own key, named in omitted too:
{"targets":[],
"omitted":[{"hit_cap":{"hits":60,"cap":50}},{"top_files":{"shown":1}}],
"stats":{"lines":0,"bytes":0,"tokens_est":null},
"top_files":[{"count":60,"path":"many.txt"}]}
edit --json, one file
{"path":"usage.ts","replacements":1,"lines":[5],"match":"exact",
"region":{"start":3,"end":7,"lines":[
{"number":3,"marker":"none","text":"export function usage(id: string) {"},
{"number":5,"marker":"replaced","text":" const cap = 20"}]},
"check":{"layer":"structure","status":"ok","errors_before":0,"errors_after":0},
"sha":{"before":"14dc685cb937","after":"9d77b7d8a9f6"},
"omitted":[],"stats":{"lines":5,"bytes":178,"tokens_est":null}}
An insert carries "inserted" and "anchor" in place of "replacements". A line’s "marker"
is "none", "replaced", or (for an insert) "inserted". --jsonl on a multi-file batch prints
one such object per file, then the trailing {"stats":...,"omitted":[]} record.
transform --json
{"path":"config.json","format":"json","operations":[{"op":"set","key":"review.threads"}],
"lines":[6],
"region":{"start":4,"end":8,"lines":[{"number":6,"marker":"replaced","text":" \"threads\": 3"}]},
"check":{"layer":"json","status":"ok","errors_before":0,"errors_after":0},
"sha":{"before":"5e9ec954d099","after":"13adcd49eab3"},
"omitted":[],"stats":{"lines":5,"bytes":42,"tokens_est":null}}
write --json
{"path":"new.txt","outcome":"created","lines":1,"bytes":6,"sha":"8e4c7c1b99db",
"omitted":[{"check_skipped":{"reason":"no grammar for .txt"}}],
"stats":{"lines":1,"bytes":6,"tokens_est":null}}
"outcome" is "created", "overwritten", or (on a failed call that still had something to
report, such as a refused overwrite) "exists".
guide --json and version --json
These two carry their text alone, with no omitted/stats — there is nothing to omit and no
file-derived cost to report:
{"guide":"lets — Locate · Edit · Transform · Show ..."}
{"version":"0.0.1"}
The omitted array
The machine-readable half of the footer: a unit omission is a bare string (e.g. "normalized"),
one with detail is a single-key object. [] means nothing was left out. Observed shapes:
| Shape | Means |
|---|---|
{"hit_cap":{"hits":60,"cap":50}} |
find was over its cap |
{"top_files":{"shown":10}} |
the over-cap map showed this many files |
{"check_skipped":{"reason":"no grammar for .txt"}} |
no checker ran for this file type |
"normalized" |
a --normalize match was used |
{"partial_batch":{"written":["src/a.ts"]}} |
some files of a batch landed before a failure |
The error shape
A call that fails with no other stdout to report — every target missing, a batch that never got
to write anything — still produces one JSON object, so a --json caller never has to fall back to
parsing stderr:
{"error":{"slug":"not_found","message":"--old not found in usage.ts\n nearest: line 5\t const cap = 20"},
"omitted":[],"stats":{"lines":0,"bytes":0,"tokens_est":null}}
error.slug is the same slug ERROR_CODE= prints on stderr — see exit codes
for the full table. A call that produces some real output alongside a partial failure keeps its
normal shape instead of this one.
stats.tokens_est
Bytes ÷ 4, always an estimate. LETS_TOKEN_RATIO overrides the divisor; LETS_NO_STATS=1 makes
it null instead, for a caller diffing two runs that doesn’t want the estimate to be part of the
diff.