lambe 0.5.0
lambe: ^0.5.0 copied to clipboard
Query JSON, YAML, TOML, HCL, and Markdown files with a composable pipeline DSL. Like jq but multi-format, with cleaner syntax. CLI tool + Dart library + MCP server for AI agents.
0.5.0 #
Added #
to_numberpipeline op. Parses a string as a number; pass-through for existing numbers. Matches CSV and TSV cells, which are strings by default:. | map(.price | to_number) | sum. ThrowsQueryErroron strings that do not parse.typepipeline op. Returns the runtime type of the input as a string:"null","boolean","number","string","array", or"object". Example:. | filter((. | type) == "number").query()andeval()normalize input data. Maps and lists with non-canonical static types (e.g.Map<dynamic, dynamic>from some third-party decoders, or typed literals like<int>[1, 2, 3]) are recursively rebuilt asMap<String, Object?>andList<Object?>before evaluation. Previously these caused cryptic type-cast errors inside the evaluator.queryStringskips this step sinceparseInputalready produces canonical trees. Maps with non-string keys throwQueryErrorwith a clear message.
Performance #
- REPL tab completion is now independent of dataset size. The completer
reduces the data to a shape representative (one sample per list, all map
keys preserved) before walking the partial AST, so operations like
sort_by,group_by, anduniqueno longer execute against the full data. Median completion latency at 1M records drops from ~380ms–1.2s (depending on pipeline ops) to ~1–2ms. Peak resident set during a completion drops from hundreds of MB to the cost of the shape tree. Completion semantics are unchanged: the candidate lists are identical. Benchmark harness undertool/bench/.
Fixed #
unique,unique_by, andgroup_bynow use structural equality on collection-valued keys. Previously these operations relied on Dart's native==forListandMap, which is reference equality, so[{"a":1}, {"a":1}] | uniquereturned both entries instead of one. The evaluator now canonicalizes keys via JSON with sorted map keys before insertion into the hash set/map. Scalar keys (num,bool,String,null) still deduplicate by value as before. Key order in maps no longer affects equality:{"a":1, "b":2}and{"b":2, "a":1}are treated as equal.EvalExceptionfromrumil_expressionsis now wrapped asQueryErrorat the public API boundary (query()andeval()). Previously, type errors in the evaluator (e.g.,.x > 5where.xis a string, ornull + 1) would leak the underlyingEvalExceptionwith a full Dart stack trace, crashingbin/lam.dartwith exit code 255 instead of reporting a clean error with exit code 1. The REPL was not affected because it already had a catch-all handler. The docstring forquery()already advertisedQueryErroras the evaluation error type; this brings the implementation in line with the contract.- REPL banner now uses the actual
lambeVersionfrom_version.dartinstead of a hardcodedv0.1.0string.
Docs #
- Tagline in the library doc comment and MCP server instructions changed from "universal" to "multi-format" — accurate given the specific format set (JSON, YAML, TOML, HCL, CSV, TSV, Markdown).
AGENTS.mdno longer references the unimplemented..(recursive descent) operator in Markdown query examples. The 0.4.0 changelog noted this was removed fromAI.mdbutAGENTS.mdwas missed.AI.mdandAGENTS.mdpipeline operation lists now includeto_numberandtype.
Release infrastructure #
- Release matrix now builds Linux ARM64 and macOS ARM64 (Apple Silicon) in addition to x64 and Windows. The MCP registry manifest covers all five platforms.
- GitHub Actions bumped:
upload-artifactv4→v7,download-artifactv4→v8,action-gh-releasev2→v3.
0.4.0 #
Added #
- Pipeline ops are now valid bare expressions with implicit
.input.has("k"),length,keys,sum,filter(...),map(...)and every other pipe op can appear as standalone expressions —has("k")parses as sugar for. | has("k"). This also unblocks common shapes likemap(has("email")),filter(has("k")), andfilter(length > 0). Bare ops are only consulted after the other_atomalternatives fail, so existing forms like{length}object shorthand,.lengthfield access, and"\(length)"string interpolation keep their prior meaning.
Breaking #
- XML input/output support removed.
Format.xml,OutputFormat.xml, and XML extension detection (.xml,.pom,.csproj,.svg) are gone. The XML→native projection was lossy in ways that silently produced wrong query results (repeated sibling elements collapsed under last-wins map semantics; attributes were dropped entirely). Rather than ship a footgun, XML is dropped for now. The underlying XML parser inrumil_parsersis unchanged and remains spec-compliant; a future lambe release may reintroduce XML with a proper projection (array-preserved siblings, attribute preservation) once the design is settled.
MCP surface #
output_formatparameter on thelambe_queryMCP tool. AI agents can now request yaml/toml/csv/tsv/hcl output directly, matching the CLI's--toflag. Defaults to json.- CSV and TSV exposed through the MCP surface. The library always
supported them; the MCP
formatenum was missing them. - MCP tool descriptions now document common pitfalls:
&&/||for boolean logic (notand/or), bracket syntax for hyphenated keys,has()and other pipeline ops requiring a leading|, and the[{key, values}]shape ofgroup_byoutput. - Build-time version generation.
tool/gen_version.dartreadspubspec.yamland writeslib/src/_version.dart, which the MCP server uses to report its version. Run after bumping the pubspec; the release workflow also runs it automatically. test/doc_examples_test.dart— AI-doc and MCP-instruction examples are now test-gated. Everylam '...'in AI.md and every embedded query in the MCP server's tool descriptions/instructions is parsed and evaluated against a fixture. Prevents future phantom-feature drift (e.g., LLM-drafted examples that advertise syntax the parser doesn't implement).
Fixed #
- MCP server now reports its actual version.
bin/mcp_server.darthad hardcoded0.1.0since that release and was never bumped. - Removed phantom
..(recursive descent) references from docs. The operator was advertised inAI.mdand the MCP server instructions as a Markdown query pattern but was never implemented. Callers who saw it would have hit parse errors. - Fixed broken example in
AI.md:filter(has("resources") == false)→filter((. | has("resources")) == false).hasis a pipeline op and cannot appear as a bare expression.
0.3.0 #
Added #
- Markdown support. CommonMark Markdown (.md, .markdown) is now a queryable input format. Parsed into a typed AST with node types like heading, paragraph, link, code_block, list, image, emphasis, etc.
mdToNativepublic API for convertingMdDocumentto queryable Dart types- Markdown query examples in MCP server instructions, AI.md, and AGENTS.md
Changed #
- Bumped rumil, rumil_parsers, rumil_expressions to ^0.5.0
- Rewrote
tool/manpage.dartto useparseMarkdown+parseYamlfrom rumil_parsers instead of handrolled parser - 491 tests (was 465)
0.2.0 #
Breaking #
|is expression composition.PipeOpsealed class removed. Pipeline operations are nowLamExprsubtypes. Any expression can appear after|:.users[0] | {name, age},. | if .active then "yes" else "no".
Improved #
- Parser error messages show position pointers and contextual descriptions
- "Did you mean?" suggestions for misspelled pipeline operations
- MCP tool descriptions expanded with syntax reference and common patterns
- Expanded recipes: object projection, string interpolation, chaining patterns
Added #
doc/jq-to-lambe.mdmigration guidetest/syntax_examples_test.dartbacking every example indoc/syntax.md- 465 tests (was 369)
0.1.1 #
- Added
.mcp.jsonfor automatic MCP server discovery in AI coding assistants - Documented MCP server setup in README
- Added query syntax guide, REPL guide, recipes, and man page to
doc/
0.1.0 #
Core #
- Query AST: sealed
LamExprhierarchy (16 subtypes) + sealedPipeOp(24 subtypes) - Left-recursive parser via Rumil's
rule()+ Warth seed-growth - Operator precedence via layered
chainl1calls - Null propagation: navigation propagates null, computation throws on type errors
- Tolerant parsing via
.recover()for REPL completion and multi-line detection
Query Language #
- Property access chains:
.users[0].address.city - Negative indexing:
.items[-1] - String key indexing:
.data["key"] - Slicing:
.[1:3],.[:3],.[2:],.[:-1] - Arithmetic:
+,-,*,/,% - Comparison:
<,<=,>,>=,==,!= - Boolean logic:
&&,||,! - Object construction with shorthand:
{name, total: .price * .qty} - Conditionals:
if .age > 65 then "senior" else "active" - String interpolation:
"\(.name) is \(.age) years old"
Pipeline Operations (24) #
- Filter and transform:
filter,map - Ordering:
sort,sort_by,reverse - Grouping:
group_by(returns{key, values}structure) - Deduplication:
unique,unique_by - Structure:
flatten,keys,values,length,first,last - Aggregation:
sum,avg,min,max - Map operations:
filter_values,map_values,filter_keys - Existence:
has - Entry conversion:
to_entries,from_entries
Multi-format I/O #
- Input: JSON, YAML, TOML, HCL, XML, CSV, TSV with auto-detection
- Output:
--to json/yaml/toml/xml/csvfor format conversion --schemafor data structure inference--assertfor CI/CD validation (exit 0 if true, 1 if false)
Interactive REPL (lam -i) #
- Parser-driven tab completion on field names, pipeline operations, and inner fields
- Syntax highlighting and colorized JSON output
- Persistent history (
~/.lambe_history) with Ctrl+R reverse search - Multi-line input with
\continuation and parser-driven bracket detection - Ctrl+Left/Right word movement, Ctrl+A/E/K/U editing shortcuts
- REPL commands:
:schema,:to,:raw,:pretty,:load,:history,:help,:quit
API #
- Library:
query(),queryJson(),queryString(),parse(),eval() - Output:
formatOutput(),inferSchema() - CLI:
lam '<expression>' [file]with all flags - MCP server:
lambe_query,lambe_schema,lambe_asserttools
Ecosystem #
lambe_testpackage with matchers:lamWhere,lamEquals,lamMatches,lamHas- MCP server installable via
dart pub global activate lambe→lam-mcp