[PR #1413] [MERGED] perf(cli): write compact JSON when output is not a terminal #2867

Closed
opened 2026-10-17 21:11:12 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1413
Author: @kaoru0822-kitauji
Created: 10/10/2026
Status: ✅ Merged
Merged: 10/10/2026
Merged by: @kaoru0822-kitauji

Base: main ← Head: perf/cli-compact-json-when-piped


📝 Commits (1)

  • 9d95ce2 perf(cli): write compact JSON when output is not a terminal

📊 Changes

5 files changed (+53 additions, -19 deletions)

View changed files

📝 docs/cli.md (+4 -1)
📝 scripts/verify/javascript/output.mjs (+3 -3)
📝 src/cli/jsonOutput.ts (+15 -3)
📝 tests/acceptance/tools/historicalCaptureResources.test.ts (+16 -12)
📝 tests/boundary/cli/jsonOutput.test.ts (+15 -0)

📄 Description

Problem

--json / --format json always writes its streamed result with two-space indentation (jsonParts(value, format === "json")). The main readers of that output are agents and scripts, which consume it through pipes and redirects. The skill's CLI fallback and the docs both use --json. For them, indentation carries no information but costs bytes and model tokens.

Measured on real results (--format jsonl output re-encoded both ways; token counts with the o200k_base tokenizer as a proxy, not a model-host measurement):

Command / target Compact bytes → indented Tokens compact → indented Token overhead
decode-interface-builder XLD.app 7,381,786 → 11,465,771 2,177,237 → 2,874,138 +32.0%
inspect-native-dispatch-metadata Stash.app 2,556,305 → 4,379,581 744,451 → 1,063,339 +42.8%
trace-dylib-resolution Stash.app 1,612,524 → 2,237,700 451,178 → 583,657 +29.4%
inspect-asset-catalog Stash.app 560,945 → 806,831 182,760 → 245,152 +34.1%
inspect-artifact Stash.app 3,749,732 → 4,980,118 1,478,963 → 1,719,228 +16.2%

Change

writeJsonOutput indents json only when the destination is a TTY. Piped or redirected json is the same value written compactly, like gh --json:

$ rea list-architectures /Applications/XLD.app --json          # terminal
{
  "evidence_id": "ev_0740…",
$ rea list-architectures /Applications/XLD.app --json | head -c 60   # pipe
{"evidence_id":"ev_0740…","subject":{"name":"XLD","digest":{…

Unchanged: the JSON value and exit status, jsonl, --filter-output, --full-output, and the --token-count/--token-limit/--token-offset paths (Incur still formats those). Small errors formatted by Incur itself also keep their existing layout. docs/cli.md documents the behavior and jq . for indented files.

Compatibility: any consumer that parses the JSON is unaffected. A script that matched indented text (for example grep '"code": "') sees a different layout when piped. The one repository test that did this now parses the JSON. I used perf: rather than !; please re-tag if you treat output whitespace as a breaking contract.

Tests

  • tests/boundary/cli/jsonOutput.test.ts: new case, indented for a terminal destination and compact otherwise. The encoding cases and the beyond-string-limit streaming cases mark their destination as a terminal, so they still cover the indented encoder.
  • tests/acceptance/tools/historicalCaptureResources.test.ts parses the CLI JSON instead of matching '"code": "resource_constraint"'.
  • scripts/verify/javascript/output.mjs redirects to a file, so it now expects compact output. Ran locally for both formats: json_output_verified: true, 536,961,629 bytes past the 536,870,888-character string limit, peak RSS about 168 MB.
  • Full vitest run: 6213 passed. The remaining failures fail identically on unmodified main on this Mac: ibtool NIB fixtures, hdiutil refusal, and three Node-discovery path tests.
  • npm run check:fast, npm run format:check, npm run docs:check
  • Manual: script -q /dev/null rea … --json (TTY) is indented; the same command piped is compact.

No Hopper/Ghidra verification needed. Refs #723, #1065.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1413 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/10/2026 **Status:** ✅ Merged **Merged:** 10/10/2026 **Merged by:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Base:** `main` ← **Head:** `perf/cli-compact-json-when-piped` --- ### 📝 Commits (1) - [`9d95ce2`](https://github.com/morluto/rea/commit/9d95ce2a758af6a15fd397597098bc20a7b71be9) perf(cli): write compact JSON when output is not a terminal ### 📊 Changes **5 files changed** (+53 additions, -19 deletions) <details> <summary>View changed files</summary> 📝 `docs/cli.md` (+4 -1) 📝 `scripts/verify/javascript/output.mjs` (+3 -3) 📝 `src/cli/jsonOutput.ts` (+15 -3) 📝 `tests/acceptance/tools/historicalCaptureResources.test.ts` (+16 -12) 📝 `tests/boundary/cli/jsonOutput.test.ts` (+15 -0) </details> ### 📄 Description ## Problem `--json` / `--format json` always writes its streamed result with two-space indentation (`jsonParts(value, format === "json")`). The main readers of that output are agents and scripts, which consume it through pipes and redirects. The skill's CLI fallback and the docs both use `--json`. For them, indentation carries no information but costs bytes and model tokens. Measured on real results (`--format jsonl` output re-encoded both ways; token counts with the `o200k_base` tokenizer as a proxy, not a model-host measurement): | Command / target | Compact bytes → indented | Tokens compact → indented | Token overhead | | --- | ---: | ---: | ---: | | `decode-interface-builder` XLD.app | 7,381,786 → 11,465,771 | 2,177,237 → 2,874,138 | +32.0% | | `inspect-native-dispatch-metadata` Stash.app | 2,556,305 → 4,379,581 | 744,451 → 1,063,339 | +42.8% | | `trace-dylib-resolution` Stash.app | 1,612,524 → 2,237,700 | 451,178 → 583,657 | +29.4% | | `inspect-asset-catalog` Stash.app | 560,945 → 806,831 | 182,760 → 245,152 | +34.1% | | `inspect-artifact` Stash.app | 3,749,732 → 4,980,118 | 1,478,963 → 1,719,228 | +16.2% | ## Change `writeJsonOutput` indents `json` only when the destination is a TTY. Piped or redirected `json` is the same value written compactly, like `gh --json`: ``` $ rea list-architectures /Applications/XLD.app --json # terminal { "evidence_id": "ev_0740…", $ rea list-architectures /Applications/XLD.app --json | head -c 60 # pipe {"evidence_id":"ev_0740…","subject":{"name":"XLD","digest":{… ``` Unchanged: the JSON value and exit status, `jsonl`, `--filter-output`, `--full-output`, and the `--token-count`/`--token-limit`/`--token-offset` paths (Incur still formats those). Small errors formatted by Incur itself also keep their existing layout. `docs/cli.md` documents the behavior and `jq .` for indented files. **Compatibility:** any consumer that parses the JSON is unaffected. A script that matched indented text (for example `grep '"code": "'`) sees a different layout when piped. The one repository test that did this now parses the JSON. I used `perf:` rather than `!`; please re-tag if you treat output whitespace as a breaking contract. ## Tests - `tests/boundary/cli/jsonOutput.test.ts`: new case, indented for a terminal destination and compact otherwise. The encoding cases and the beyond-string-limit streaming cases mark their destination as a terminal, so they still cover the indented encoder. - `tests/acceptance/tools/historicalCaptureResources.test.ts` parses the CLI JSON instead of matching `'"code": "resource_constraint"'`. - `scripts/verify/javascript/output.mjs` redirects to a file, so it now expects compact output. Ran locally for both formats: `json_output_verified: true`, 536,961,629 bytes past the 536,870,888-character string limit, peak RSS about 168 MB. - Full `vitest run`: 6213 passed. The remaining failures fail identically on unmodified `main` on this Mac: ibtool NIB fixtures, `hdiutil` refusal, and three Node-discovery path tests. - `npm run check:fast`, `npm run format:check`, `npm run docs:check` - Manual: `script -q /dev/null rea … --json` (TTY) is indented; the same command piped is compact. No Hopper/Ghidra verification needed. Refs #723, #1065. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-17 21:11:12 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#2867
No description provided.