Drag in any data file - JSON, NDJSON, CSV, TSV, Parquet, SQLite - and explore the real rows. Filter, reshape, edit, export. No jq. No SQL.
Most tools for poking at a data file assume you already know jq or SQL. shape
is for everyone who bounces off them. Open a file and you get a fast, virtualized
table of the actual rows beside a structure map of every field - then a
click-to-build visual filter, a global search, in-place cell editing, and a
one-click export, with the equivalent jq and SQL shown for whatever you built
so you can take the query with you.
It reads in a single streaming pass with bounded memory, so a multi-gigabyte file
opens without loading into RAM (past 16,384 distinct values per field it switches
to an approximate mode - HyperLogLog cardinality + Space-Saving top-k). The same
cgo-free core also ships as a command-line profiler (schema inference + breaking-
change diffs) and a hoijunkim/shape@v1 GitHub Action for CI.
→ What the desktop app does · Install
Desktop app (the explorer). Download the build for your platform from the
Releases page and run it -
shape-gui_<version>_windows_amd64.zip, ..._darwin_universal.zip, or
..._linux_amd64.tar.gz. The binaries are unsigned, so the first launch needs
"Open anyway" (macOS Gatekeeper) or "More info → Run anyway" (Windows SmartScreen).
CLI (shape). The command-line profiler ships separately:
go install github.com/hoijunkim/shape@latest # any platform with Go
brew install --cask hoijunkim/tap/shape # macOS / Linux
or grab a shape_<version>_<os>_<arch> archive from the same
Releases page.
shape profile data.ndjson
Reads JSON, NDJSON, CSV, TSV, Parquet, or SQLite (format is auto-detected
from the extension, or pass --format) and prints a per-field shape summary:
types, null rate, distinct count, min/max, and top values. Pass - to read
from stdin. Add --json for machine-readable output.
shape schema data.ndjson -o schema.json
Infers a JSON Schema (Draft 2020-12) from the same input formats and writes
it to a file (-o/--out) or stdout.
shape diff old.ndjson new.ndjson --fail-on breaking
Diffs two snapshots and reports what changed: field additions/removals, type
widening/narrowing, nullability changes, and enum drift. --fail-on controls
the exit code: breaking (default) exits 1 only on breaking changes, any
exits 1 on any change, none never fails. Add --json for machine-readable
output.
Gate a pull request on breaking data-shape changes:
- uses: hoijunkim/shape@v1
with:
old: baseline.ndjson
new: current.ndjson
fail-on: breakingSee action.yml for the full set of inputs.
A Wails v2 desktop app under gui/ reuses the same Go core as
a data explorer: drop in any supported file and browse the actual rows, no
jq or SQL required.
- Explore - a virtualized table over the real rows, with a structure map of the file's fields alongside it. Files larger than memory stream instead of loading, and counts that are estimates say so.
- Filter - a visual condition builder (type-aware operators, AND/OR), applied live, with a cancellable exact match count.
- Search - a global search box: type any text and the rows narrow to those where any field's value contains it (case-insensitive, no column to pick), combined with the filter and reflected in the count, the export and the code.
- Expand - click a truncated object/array cell to open its full value as a collapsible tree, with a Copy button for the exact JSON.
- Row detail - click a row's number to open the whole record as a collapsible tree - the full, untruncated nested value (the row-level companion to Expand), with the same Copy button.
- Column stats - expand a field in the structure map to see its full profile
in place: a distribution histogram for numbers, a top-values chart for
categorical fields, type mix, presence/null meters, quantiles and health flags
- all from the single open-time profiling pass, fetched on demand, no rescan.
- Sort - click a column header to sort by it (ascending → descending → off), exact over the whole result on every tier - even a multi-gigabyte streaming file, via a bounded keys-only index. Row numbers stay the true source ordinals (so they read non-contiguously under a sort), which keeps editing and cell lookups pointing at the right record.
- Reshape - choose, reorder and rename the columns you want.
- Saved views - save the current filter, search, sort and reshape under a name from the header's Views menu, and re-apply it anytime - across restarts. Views are global (they apply to whatever file is open) and live in a plain JSON file in your config dir.
- Edit - double-click a scalar cell to change its value in place. Edited cells are highlighted (and the row flagged in the gutter); an "Edited only" toggle lists just the changes as was → now, each revertable. Number literals keep their exact text, so a 19-digit id never loses a digit. Editing is limited to unambiguous scalar columns (a single, non-array leaf), and it never touches the file on disk - see Save a copy below.
- Save a copy - write the whole file back out with your edits applied, as JSON or NDJSON, to a new file. The original is left untouched, the nested structure is preserved (edits land at the source path, not a flattened one), and every row is written - not the filtered/reshaped view. The dialog reports how many edits applied and warns if any could not be. (Overwrite-in-place and CSV/Parquet saving are deliberately out of scope for now; object key order may change on rewrite.)
- Export - write the filtered, reshaped result to JSON, NDJSON, CSV, TSV or Parquet. The export is always the complete result, never the windowed view, and it lands atomically: a cancelled or failed export leaves no partial file.
- Take the query with you - the Code panel shows the equivalent
jqexpression and SQL query for whatever you built by clicking, ready to copy, with the places the three engines genuinely differ called out rather than glossed over. On a SQLite source shape also runs that SQL: a filter it can translate exactly is pushed into the database (measured ~12x faster on a 200k-row count), and anything it cannot vouch for falls back to the same Go predicate every other format uses, so the answer never changes.
First launch opens a "?" help overlay explaining every feature; the header's
"?" button reopens it anytime. It still exports the inferred JSON Schema too (the
header's "Schema" button).
Build it with wails build (see gui/README.md for the required build order).
JSON, NDJSON, CSV, TSV, Parquet, SQLite.
Made by Hoijun Kim (hoijunkim).
Copyright © Hoijun Kim.
Open source: AGPL-3.0. Run it, read it, modify it, fork it, redistribute it - commercial use included. The condition that matters in practice: if you modify shape and let others use it over a network, you owe them the modified source. Full terms in LICENSE.
Commercial licensing: contact the copyright holder, Hoijun Kim <hoijun.kim00@gmail.com> - for shipping shape inside a closed-source product, or running a modified version as a service without publishing the changes. See LICENSING.md.
Releases up to 0.1.1 were published under the PolyForm Noncommercial License 1.0.0 and keep it; 0.1.2 onward is AGPL-3.0.
