When input is piped (CI, scripts, AI agents), /dev/tty won't exist.
Previously this crashed with 'cannot open /dev/tty'. Now we detect
the absence of a TTY and auto-enable --anonymous mode.
- Use flag.FlagSet with config struct instead of global flag
- Add version vars (name, version, commit, date, licenseYear, licenseOwner, licenseType)
- Add printVersion() with copyright and license
- Handle -V/--version/version/help/-help/--help before fs.Parse
- Handle flag.ErrHelp and fs.NArg() > 1 checks
In anonymous mode (-anonymous), jsonpaths now uses the deterministic
pipeline (RawPaths → Coalesce → Generate) instead of the legacy
analyzer. Interactive mode still uses the old analyzer with resolver.
snakeToPascal now splits on both underscores and hyphens, producing
valid Go identifiers like GenerationI, RubySapphire, BlackWhite instead
of invalid ones like Generation-i, Ruby-sapphire, Black-white.
- parsePath now uses brace-depth matching to correctly parse nested JSON
in sample type intros like {Root:{"name":"pikachu","id":25}}
- cleanTypeName strips sample data from type intros (Root:{...} → Root)
- isPrimitiveType recognizes sample leaf values (quoted strings, numbers)
- primitiveToGo maps sample values to proper Go types (string, int64, etc.)
- All three pipeline tests pass: raw→Go, coalesced→Go, and sample→Go
jsonsample emits the same flat path format as jsonrawpaths but with
actual JSON values at leaf nodes and compact JSON samples at type intro
lines (e.g., {Root:{"name":"pikachu","id":25,...}}). Output is auto-
coalesced: types deduplicated by key set, one sample per primitive type.
Updated coalescer to handle TypeName:Sample format in type intros,
using first { instead of last { for parsing (handles nested JSON braces).
Sample output is valid input for jsoncoalesce (idempotent round-trip).
Coalesce merges streamer output by deduplicating types with identical
key sets, collapsing {null} into nullable annotations ({string?}),
cleaning up type names (removing unnecessary numbers, deriving names
from keys when many types share a shape), and eliding duplicate type
definitions. Preserves {undefined} distinct from {null} per the
three-stage pipeline design.
Pokemon fixture: 241 raw lines → 140 coalesced lines.
Add RawPathsConfig with SampleLen to show truncated sample values
instead of type names at leaf nodes (e.g., {"Alice Anglerso..."} instead
of {string}). Add absentValue sentinel to distinguish fields absent
from a struct instance ({undefined}) vs explicitly null ({null}).
Type names now appear once on the intro line, and subsequent fields
use the bare path without repeating the type. Root no longer has a
leading dot.
Before: .{Root0}.rooms[]{RoomsItem1}._timing{Timing2}.account_ms{int}
After: .rooms[]._timing.account_ms{int}
The type intro line declares the type, making the path structure
cleaner and more readable while remaining lossless.
With ≤3 keys, one word-like key is enough to call it a struct.
With 4+ keys, a majority must be word-like — prevents a single
coincidental match (e.g., "beef" in hex keys) from misclassifying.
Also fixes short segment handling: 2-char abbreviations like "ms" in
"account_ms" are neutral rather than causing the whole key to fail
word detection.
Rewrote looksLikeMap from 6 layered heuristics down to 3 simple rules:
1. All numeric keys → map (certain)
2. Any key composed of words → struct (certain)
3. Everything else → map (safe default)
The asymmetry is deliberate: a map misidentified as a struct explodes
into hundreds of fields, while a struct misidentified as a map is
compact and easy to fix.
Word detection splits keys on _ and camelCase, checks segments for
pronounceability (vowel/consonant rhythm). Short words (id, at, on)
only count when paired with a strong 3+ char word.
Also adds raw path type naming with Item suffix for collections
(FriendsItem instead of Friend), real API test fixtures from PokeAPI
and SWAPI, and UUID-based test data replacing synthetic abc123 keys.
isBase64 matched common field names like "name" — too loose for map
detection. Replace with isPronounceable which checks vowel/consonant
rhythm: real field names have natural syllable structure (15-80% vowels,
no 5+ consonant runs), while tokens and IDs don't.
Also fix allLookLikeIDs to reject keys that don't match any ID pattern
instead of silently accepting them.
RawPaths walks JSON depth-first and emits flat paths with monotonically
numbered type names (Root0, People2, etc.). Each type intro immediately
precedes its fields, making the output streamable and order-stable.
Also improves map detection heuristics:
- All-numeric keys are always maps (even with 1-2 keys)
- Keys containing digits detected as IDs (e.g., emp_101)
- New isKnownFieldName with broad list of common field names
(timestamps, flags, relations, etc.) as strong struct signal
Separate library from CLI concerns:
- Add Resolver callback type with Decision/Response structs for all
interactive decisions (map/struct, type name, tuple/list, shape
unification, shape naming, name collision)
- Move terminal I/O (Prompter) from library to cmd/jsonpaths
- Add public API: New(), ParseFormat(), Generate(), AutoGenerate()
- Add Format type with aliases (ts, py, json-paths, etc.)
- Fix godoc comments to match exported function names
- Update tests to use scriptedResolver instead of Prompter internals
- Update doc.go and README with current API
Add tools/jsontypes library and tools/jsontypes/cmd/jsonpaths CLI.
Given a JSON sample (file, URL, or stdin), walks the structure,
detects maps vs structs, infers optional fields from multiple
instances, and produces typed definitions.
Output formats (--format):
- json-paths: flat type path notation (default)
- go: struct definitions with json tags and union support
- typescript: interfaces with optional/nullable fields
- jsdoc: @typedef annotations
- zod: validation schemas with type inference
- python: TypedDict classes
- sql: CREATE TABLE with FK relationships
- json-schema: draft 2020-12
- json-typedef: RFC 8927
Features:
- Interactive prompts for ambiguous structure (map vs struct, same
vs different types), with --anonymous mode for non-interactive use
- Answer replay: saves prompt answers to .answers files for iterative
refinement
- URL fetching with local caching and sensitive param stripping
- Curl-like auth: -H, --bearer, --user, --cookie, --cookie-jar
- Discriminated union support with sealed interfaces, unique-field
probing, and CHANGE ME comments for type/kind discriminators
- Extensive round-trip compilation tests for generated Go code