feat: Add --json output to every sheets CLI command - #2
Merged
andrei-hasna merged 1 commit intoJul 31, 2026
Merged
Conversation
Add --json output to every sheets CLI command
The `sheets` CLI in @hasna/sheets has no machine-readable output mode: `info`, `get` and the other inspection commands print prose that a script or agent can only screen-scrape.
Problem, in full. `src/cli/index.ts` registers around nine commands over workbook JSON, CSV and XLSX documents. Output is written as formatted prose. For example the `info` command does:
console.log(`Workbook ${workbook.id} (${workbook.sheets.length} sheet(s))`);
for (const sheet of workbook.sheets) {
const count = Object.keys(sheet.cells).length;
const active = sheet.id === workbook.activeSheetId ? " [active]" : "";
console.log(` - ${sheet.name}: ${sheet.rows}x${sheet.columns}, ${count} cell(s)${active}`);
}
and the shared `emit(content, out)` helper either writes a file or prints the raw string. A repository-wide search for `--json` in `src/` returns zero hits — the flag exists on no command and not on the program.
To read a workbook's sheet dimensions programmatically, a caller must parse `" - Sheet1: 100x26, 12 cell(s) [active]"`, which breaks as soon as a sheet name contains a colon, a comma, or the literal text ` [active]`. Sheet names are user-controlled, so that is reachable rather than theoretical. `get` has the same problem: a computed cell value is printed bare, so an empty cell, the string "null", and a genuine null are indistinguishable on stdout.
This is a parity gap against the rest of the Hasna CLI surface, where `--json` is the standard way an agent consumes output, and the same family of work has already been carried out for other CLIs in this codebase.
Expected behaviour after the fix. A global `--json` flag is accepted, and every command that produces output honours it by emitting one well-formed JSON document on stdout carrying the structured data the command already holds — for `info`, the workbook id and an array of sheet objects with id, name, rows, columns, cell count and active flag; for `get`, the resolved sheet, the A1 reference, and the computed value with its real type preserved. Commands whose output is a document body (workbook JSON, CSV, XLSX) keep writing that body unchanged, since it is already machine-readable; `--json` must not wrap or corrupt it. Human output without the flag is unchanged character for character. Errors under `--json` are emitted as a JSON object on stderr with a non-zero exit code.
Reproduce. Run the `info` command against a workbook whose sheet is named `Q1: revenue, draft` and observe the printed line cannot be parsed back into fields. Run `get` on an empty cell and observe the output is indistinguishable from a cell containing an empty string. Confirm `sheets --help` offers no JSON option.
Acceptance criterion. A regression test, written first and confirmed failing against the current code, that runs `info` with `--json` against a workbook containing a sheet name with a colon and a comma and asserts the parsed JSON carries the exact name and dimensions; a test for `get --json` asserting an empty cell and a cell holding the string "null" are distinguishable; tests asserting the non-JSON output of both commands is unchanged; and a test asserting a failing command under `--json` emits a JSON error on stderr with a non-zero exit. Then make it pass across every command in the CLI.
Keep the change minimal. Confine changes to `src/cli/index.ts` and any small shared output helper. Do not change the workbook model, the formula recalculation, or the CSV/XLSX serializers in `src/lib`.
X-Factory-Run: run_1fd244465266
X-Factory-Task: 075690b1-74ab-4a1e-b9b4-4dc4aab010b3
andrei-hasna
deleted the
factory/075690b1-74ab-4a1e-b9b4-4dc4aab0-fa2b524d
branch
July 31, 2026 09:42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Objective
Add --json output to every sheets CLI command
The
sheetsCLI in @hasna/sheets has no machine-readable output mode:info,getand the other inspection commands print prose that a script or agent can only screen-scrape.Problem, in full.
src/cli/index.tsregisters around nine commands over workbook JSON, CSV and XLSX documents. Output is written as formatted prose. For example theinfocommand does:and the shared
emit(content, out)helper either writes a file or prints the raw string. A repository-wide search for--jsoninsrc/returns zero hits — the flag exists on no command and not on the program.To read a workbook's sheet dimensions programmatically, a caller must parse
" - Sheet1: 100x26, 12 cell(s) [active]", which breaks as soon as a sheet name contains a colon, a comma, or the literal text[active]. Sheet names are user-controlled, so that is reachable rather than theoretical.gethas the same problem: a computed cell value is printed bare, so an empty cell, the string "null", and a genuine null are indistinguishable on stdout.This is a parity gap against the rest of the Hasna CLI surface, where
--jsonis the standard way an agent consumes output, and the same family of work has already been carried out for other CLIs in this codebase.Expected behaviour after the fix. A global
--jsonflag is accepted, and every command that produces output honours it by emitting one well-formed JSON document on stdout carrying the structured data the command already holds — forinfo, the workbook id and an array of sheet objects with id, name, rows, columns, cell count and active flag; forget, the resolved sheet, the A1 reference, and the computed value with its real type preserved. Commands whose output is a document body (workbook JSON, CSV, XLSX) keep writing that body unchanged, since it is already machine-readable;--jsonmust not wrap or corrupt it. Human output without the flag is unchanged character for character. Errors under--jsonare emitted as a JSON object on stderr with a non-zero exit code.Reproduce. Run the
infocommand against a workbook whose sheet is namedQ1: revenue, draftand observe the printed line cannot be parsed back into fields. Rungeton an empty cell and observe the output is indistinguishable from a cell containing an empty string. Confirmsheets --helpoffers no JSON option.Acceptance criterion. A regression test, written first and confirmed failing against the current code, that runs
infowith--jsonagainst a workbook containing a sheet name with a colon and a comma and asserts the parsed JSON carries the exact name and dimensions; a test forget --jsonasserting an empty cell and a cell holding the string "null" are distinguishable; tests asserting the non-JSON output of both commands is unchanged; and a test asserting a failing command under--jsonemits a JSON error on stderr with a non-zero exit. Then make it pass across every command in the CLI.Keep the change minimal. Confine changes to
src/cli/index.tsand any small shared output helper. Do not change the workbook model, the formula recalculation, or the CSV/XLSX serializers insrc/lib.Verification
Run
run_1fd244465266· backendcodewith· task075690b1-74ab-4a1e-b9b4-4dc4aab010b3🏭 Generated by @hasnaxyz/factory
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.