Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Section titled “[Unreleased]”[1.21.0] - 2026-10-04
Section titled “[1.21.0] - 2026-10-04”- BAML grammar (
baml, from BoundaryML/baml-treesitter, Apache-2.0), bringing the pack to 372 languages (#200). Upstream ships an ABI 15parser.c; it is regenerated at ABI 14 like the rest of the pack. Known limitation: the grammar rejects the documented backtickpromptform until BoundaryML/baml#5012 is fixed upstream. - Parser bundles now carry the pinned licence and NOTICE text for every grammar they contain, so redistributors can retain the applicable upstream notices (#191). Coverage is fail-closed: a grammar with no resolvable licence text fails the build.
- A root-owned, non-writable parser cache directory is accepted, so a grammar cache baked into a
read-only container image works without a
chown(#202). Group- or other-writable directories are still rejected. - Python
DocstringFormat,StructureKindandSymbolKindgain afrom_other()factory, and the unit enums are hashable. WASM classes gaincopyForTransfer().
Changed
Section titled “Changed”- Refresh 48 grammar pins to their latest upstream revisions. No grammar changed ABI or lost a query;
F#, F# Signature and Protocol Buffers gained
locals/tagsqueries.cudastays held: its upstreamparser.cis past the regeneration size threshold and would lose its injections and tags queries. - BREAKING (Java, C#):
DocstringFormat,StructureKindandSymbolKindare sealed types, not enums. Java uses sealed interfaces with record variants and C# uses abstract records with sealed record variants, soStructureKind.FUNCTION/StructureKind.Functionbecomesnew StructureKind.Function().Othernow carries its string payload, and Java’svalues(),getValue()and case-insensitivefromValue()are gone, so match with pattern matching orinstanceof/is. The JSON wire format is unchanged. JavaErrorExceptionnow extendsTreeSitterLanguagePackRsException. - BREAKING (Python):
cache_dir,clean_cache,configure,download,download_all,download_group,init,manifest_groups,manifest_languages,prefetchandprocessraise the typedtree_sitter_language_pack.Errorsubclasses (DownloadError,LanguageNotFoundError, …) instead ofRuntimeError, matchingget_language.except RuntimeError:no longer catches them. The.pyistubs also drop the never-presenttypeattribute and TypedDict variants on the kind types and tighten constructor parameter types. - BREAKING (WASM): the
dataandbodySpansetters no longer acceptnull/undefined. Use the newclearData()andclearBodySpan()instead. - BREAKING (PHP, Ruby type signatures): the PHP stubs describe the kind types as classes with a
type_tagproperty, anotherproperty and static factories, not as string constants. The Ruby RBS enum classes becomeenum_*type aliases. Kotlin Android unit enums now return the wire string fromtoString()("Line", not"LINE"). - Regenerate all bindings, fixtures, documentation and workflows with Alef 0.103.14 (was 0.87.1 at
1.20.0), which is the source of the changes above. The PHP extension now builds against ext-php-rs
0.16 and the Elixir package requires
rustler_precompiled ~> 0.10. Ruby builds against magnus 0.9 and the MCP server against rmcp 3.5.libcis held at 0.2.189 because 0.2.190 breaks iOS builds (rust-lang/libc#5601).
download([...])fetches languages the manifest knows but the cache does not hold. It previously returned the requested count without downloading anything, leavingdownloaded_languages()empty on a fresh cache (#201).- Python
get_language()releases the GIL while it downloads a missing grammar, so a cache miss no longer freezes every other thread and the asyncio loop for up to the HTTP timeout (#203). - rustls is past RUSTSEC-2026-0285 in every lock, including the Elixir NIF, Ruby extension and Rust e2e locks that the earlier bump missed (#198).
[1.20.0] - 2026-09-14
Section titled “[1.20.0] - 2026-09-14”Changed
Section titled “Changed”- BREAKING (Java): enum constants are now
UPPER_SNAKE_CASE.StructureKind.FunctionbecomesStructureKind.FUNCTION, and likewise acrossCommentKind,DataNodeKind,DiagnosticSeverity,DocstringFormat,ExportKindandSymbolKind. The serialized wire values are unchanged, so no data migration is needed and persisted JSON stays readable; only Java source naming the old identifiers has to be updated. - BREAKING (Swift):
process()returns typed value types instead of opaque Rust handles.ProcessResult,StructureItem,DataNodeand fifteen other types are nowCodablestructs with stored properties rather thantypealiases to swift-bridge handles. Field access moves from method to property syntax (result.structure, notresult.structure()), collections are real Swift arrays, andStructureKind/SymbolKind/DocstringFormatare Swift enums with associated values rather than strings. Values now cross the bridge as JSON and are decoded in Swift, soprocess()isthrowson malformed input rather than returning a handle that fails on first access. - Regenerate all bindings, fixtures, documentation and release workflows with Alef 0.87.1 (was
0.85.15), lifting the hold recorded in 1.19.1. 0.86.1 promoted the Swift binding to value types but
left the Swift e2e suite it generates calling the old method syntax; 0.87.0 fixed that but emitted
enums whose
Codableconformance could not read serde’s wire format. Both are resolved in 0.87.1.
- Swift structure-extraction test coverage. Every test in the Swift package suite previously read only
scalar fields off
process(), leaving the structure path unexercised — all seventeen passed against an Alef 0.87.0 build whoseprocess()threw on any source containing structure. The new test asserts the decodedStructureKindcases and item names, which is what exercises the element decoder; nim cannot cover this (its grammar uses none of the node kind namesstructure_kind_at()matches) so it goes through mojo, already in the package’s statically compiled language set.
- Swift enums with associated values decode serde’s externally tagged wire format. Unit variants
serialize as a bare string (
"Function") and payload variants as a single-keyed object ({"Other": "macro"}); the generated conformance previously fell through to Swift’s synthesizedCodable, which expects{"function": {}}and matched neither form. (Alef 0.87.1) - Node and WebAssembly e2e suites read internally tagged
FormatMetadataas the flattened shape serde actually emits, with the sibling-field form asserted directly so a regression to the nested form fails loudly instead of silently degrading to the variant name. (Alef 0.87.1) - The Go binding pairs native error messages with their sentinels, so callers can match with
errors.Iswhile still reading the detail the native layer produced. (Alef 0.87.1)
[1.19.1] - 2026-09-13
Section titled “[1.19.1] - 2026-09-13”Changed
Section titled “Changed”- Alef stays pinned at 0.85.15. 0.86.1 rewrites the Swift binding to real value types –
DataNode,ProcessResultandStructureItembecomeCodablestructs with typed properties instead oftypealiases to opaque Rust handles – but does not update the Swift e2e suite it generates alongside them, which still callsresult.language()andresult.structure()on what are now properties. The two halves of its own output do not compile together, ande2e/is generated with a CI freshness gate, so the mismatch cannot be patched downstream. Reported upstream; the upgrade lands once the e2e emitter follows the binding emitter. - Removed the eight superseded
release/swift/<version>branches.publish.yamlmoves the release tag onto the same checksummed commit it pushes the branch to, so each branch named a commit already reachable throughv<version>; a consumer pinned to one can switch toexact: "<version>"with no other change.release/swift/1.19.0is retained.
- Patched an indent-stack underflow in the vendored
agdaexternal scanner.VEC_POPis a barelen--andVEC_BACKreadsdata[len - 1], so the dedent loop could drain the stack, wraplentoUINT32_MAXand dereference the buffer plus 16 GiB. 1023 or more repeated'characters – or NUL bytes – crash the parser withSIGBUS. The same scanner also seeded its column-0 sentinel only whendeserializewas called with a NULL buffer, while tree-sitter resets a scanner with a non-NULL inline buffer of length 0, leaving the following scan to readdata[-1]. A survey of all 186 vendored scanners found both defects only inagda. - Dynamically loaded grammars are ABI-checked at load time.
load_from_dircalledLanguage::from_rawon a downloaded parser with only a null check, so an incompatible grammar was accepted and failed later atts_parser_set_language– or, just outside the compatible range, parsed wrongly. The ABI version is now compared against the linked runtime’sMIN_COMPATIBLE_LANGUAGE_VERSION..=LANGUAGE_VERSIONand rejected with both versions named. The bounds are read from the runtime, so a runtime upgrade moves them rather than leaving a stale literal behind. scripts/sync_zig_zon_hashes.pynow maintainsalef.toml’s[crates.e2e.registry.packages.zig.platform_hashes]alongsidetest_apps/zig/build.zig.zon. Nothing had ever populated that table, so it carried fiveSTALE_HASH_REGENERATEplaceholders; Alef responds to a placeholder by omitting the.hashline entirely, which is the only reason the manifest had to be declareduser_ownedin the first place. The five real digests are in place and the gate now fails on drift in either file.- The nightly sanitizer sweep finishes inside its job. Six consecutive nightlies were killed at the 120-minute limit, every one inside the parse sweep after all twelve preceding steps had passed, so the job reported neither its findings nor how far it got. The sweep now runs against a 90-minute budget checked between languages, states how many languages it swept, names any it did not reach, and bounds each parse with a timeout – one pathological input had been running over ten minutes. The day’s language order rotates by day-of-year so a truncated sweep stops dropping the same alphabetical tail every night.
- That sweep also reports a finding it cannot attribute to a sanitizer exit code. UBSan raises
SIGABRTwhereabort_on_errordefaults to 1 and printsERROR: UndefinedBehaviorSanitizer, neither of which the previous detector matched, so theagdacrash above was exactly the shape it let through; any parse killed by a signal now counts. Its sanitizer environment is scoped to thets-packinvocation as well – as a step-levelenv:it also reached the shell,python3,find,grepandtee, each running uninstrumented with the runtimeLD_PRELOADed into it andexitcode=42in force, so a diagnosis raised in a helper would have been reported as a scanner finding.
[1.19.0] - 2026-09-12
Section titled “[1.19.0] - 2026-09-12”Changed
Section titled “Changed”- Upgraded the tree-sitter runtime to 0.27, moving
tree-sitter-languageto 0.1.8 in the same step. The two pins are not independent: 0.27 requirestree-sitter-language^0.1.8, and 0.1.8 relocates the 0.26 WASM shims underwasm/unsupported/so they no longer compile against the older runtime. The grammar ABI is unchanged – both 0.26 and 0.27 areTREE_SITTER_LANGUAGE_VERSION15 with a minimum compatible version of 13 – so every bundled grammar loads exactly as before and no regeneration is implied. - Consumers that also depend on
tree-sitterdirectly must move to 0.27 in the same step. Thetree-sittercrate declareslinks = "tree-sitter", and Cargo permits only one package perlinksvalue in a dependency graph, so pairing this release with a 0.26 pin fails to resolve rather than failing to compile. Nothing this crate exports changed: the requirement is a dependency floor, not a break in its own API. - Grammar scanners now compile with
NDEBUGonwasm32. 0.27’s WASM libc is a documented subset that excludesassert, so a scanner keeping its assertions emitted an unresolved__assert_fail. That became anenvmodule import rather than a link error, and the package then failed to load at runtime withCannot find module 'env'. Native builds keep their assertions. - Dropped this crate’s own WASM libc shims (
memchr,strcmp,iswalnumand friends) now that 0.27 defines them. Keeping both made the two definitions collide at link time, which broke the WASM package under the split-codegen-unit settings the release build uses. - Replaced the 0.26 WASM integration, which used a separate non-thread-safe allocator and an incomplete libc. 0.27 supplies its own WASM libc and forwards C allocation to the Rust application’s global allocator.
- Swift binaries again link a single tree-sitter C runtime. SwiftPM builds tree-sitter 0.25.10 for
SwiftTreeSitter as loose object files, while this package’s Rust staticlib carries the 0.27
runtime in one archive member. Archive members load only to resolve an undefined symbol, and
0.27’s new
ts_language_is_parseablewas the first symbol SwiftPM’s copy could not supply – pulling the member in and colliding on 253 other runtime symbols at link time. The C bridge now provides a weak definition, so the member stays unreferenced and yields to any future SwiftPM runtime that exports the symbol itself (#189). wasm32builds now find their headers under either wasi-sysroot include layout. wasi-libc renamedinclude/wasm32-wasitoinclude/wasm32-wasip1, and probing only the former meant a sysroot that existed but used the newer name contributed no include flag at all, failing every grammar with'stdlib.h' file not found. Toolchains whose clang supplies its own sysroot were unaffected, which is why this surfaced only in local builds.Node::child_countnow returns tree-sitter’su32widened to theusizethis crate has always declared, rather than failing to compile against the new runtime.
[1.18.0] - 2026-09-11
Section titled “[1.18.0] - 2026-09-11”Changed
Section titled “Changed”- Refreshed 22 grammar pins to their upstream latest:
al,blade,cfml,dotenv,gitcommit,javadoc,kcl,kdl,lean,magik,mlir,nix,ocaml,ocaml_interface,perl,proto,sas,sql,swift,sysmlandt32.ablis no longer held back: upstream now defines theouter_joinnode itshighlights.scmreferences, so it compiles for the first time since the hold was taken.cudais now the one deliberate hold – upstream’s committedparser.cgrew past the regeneration threshold, so a bump would ship it as-is at ABI 15 and drop the injections and tags queries the grammar only receives while being regenerated. - Re-derived the
ocaml_interfacehighlights overlay from upstream’s reworked query. tree-sitter-ocaml declares one sharedqueries/highlights.scmfor all three of its grammars, but that query matches(shebang), which only the implementation grammar defines; the overlay is upstream’s file with that alternative removed, and it had drifted behind their block-access rework. - Regenerated bindings, fixtures, documentation and release workflows with Alef 0.85.15.
C#
containsassertions now compare a JSON-serialized value instead ofConvert.ToString, which reported a type name rather than content for tagged unions such asStructureKind. The PHP e2e runner exports the verified extension path so processes it spawns under-nload the same build. - Upgraded
rmcpto 3.3. - Retained tree-sitter 0.26 and the
tree-sitter-language0.1.7 pin. tree-sitter 0.27 requirestree-sitter-language0.1.8 and removed itsPackage.swift, so SwiftTreeSitter cannot follow it; the upgrade also changes this crate’s public API, sinceLanguageis re-exported. (#189)
- Publish every native NuGet runtime package required by the managed package’s runtime graph.
- Upload Dart native archives and checksums required by the generated package downloader.
- Publish Go installer archive aliases and their required SHA256 sidecars.
- Recorded the node e2e harness’s
tree-sitterfloor inalef.tomland realignedtest_apps/node’s lockfile with its regenerated manifest, which disagreed and brokepnpm install --frozen-lockfile. Realigned the generated Go e2e module pin, which the version sync leaves behind.
[1.17.0] - 2026-09-09
Section titled “[1.17.0] - 2026-09-09”Changed
Section titled “Changed”- Retain tree-sitter 0.26 and the current grammar revisions. The attempted 0.27 upgrade
is deferred because SwiftTreeSitter 0.25 requires the 0.25 C runtime, which conflicts
with the newer runtime in our published static Swift library. Pin the WASM shim
dependency
tree-sitter-languageto 0.1.7 because 0.1.8 rejects the retained runtime. (#189) - Regenerate bindings, fixtures, documentation, and release workflows with Alef 0.85.11;
upgrade Rust dependencies, including
dirs7 andzstd0.14.
- Restore the Rust
extension_ambiguity()API and its serde-gated JSON companion to expose the default language and alternatives for ambiguous file extensions. (#185)
- Propagate C# fallible-void native failures as typed exceptions while preserving their native error codes and messages; narrow Java boolean returns to the C ABI width.
- Preserve Swift declaration kinds, protocol requirements, aliases and constants; retain TypeScript namespace ownership and named arrow functions without duplicate constants. (#186)
- Preserve YAML flow collections and scalar keys, TOML dotted keys and commented containers, JSON property spans, and Terraform/HCL paths. Reject concatenated JSON documents and handle deeply nested TOML keys without overflowing the stack. (#186)
- Stage the matching ktreesitter host native library for Kotlin Android JVM tests and synchronize dependency lockfiles used by documentation and generated test suites.
[1.16.2] - 2026-09-05
Section titled “[1.16.2] - 2026-09-05”process()returns aProcessResultobject again in Python, so attribute access works. In 1.16.1 the generator emittedoptions.ProcessResultasclass ProcessResult(TypedDict, total=False), which is a type annotation and not a runtime class –process()therefore handed back a plaindict, and every documented access (result.language,result.chunks,result.metrics.total_lines) raisedAttributeError. Only Python was affected; the TypedDict path existed in no other binding, so the same call in Node, Go, Ruby or Java kept returning a real object throughout.ProcessResultis a@dataclass(frozen=True, slots=True)again, as it was before 1.16.1 and as every sibling binding already models it. (#183)- The generated e2e suites assert against the shape their binding actually returns. Two
backends were broken by one generator-level disagreement, in which the e2e emitter and the
binding emitter held different ideas about the shape of a value they share. In Python, 72 of the
523 tests failed because the emitter wrote attribute assertions (
result.language == "python") against a value the binding emitter was generating as a dict. In TypeScript, 18 of the 37processtests failed because the emitter wroteString(e.kind).includes("Function")againstkind, which is an object –String({ type: "Function" })is"[object Object]", so every structure-kind assertion evaluated false and reported it as the uninformativeexpected false to be true. Both emitters now read the value the way the binding presents it – the dataclass attribute in Python,e.kind?.["type"] === "Function"in TypeScript – and both suites are 523 passed / 0 failed. (#182)
Changed
Section titled “Changed”- Regenerated all bindings, stubs, scaffolding, READMEs, test apps and e2e suites on alef 0.84.1 (from 0.82.2). 0.82.1 deleted the TypedDict path from the Python binding emitter and 0.75.0 fixed the e2e emitter, so this is the first release carrying both. 0.84.1 also makes generation reproducible across runs, which clears the 459 spurious TypeScript snippet-compile failures the 0.84.0 line reported.
Note on the npm package
Section titled “Note on the npm package”@xberg-io/tree-sitter-language-pack skipped both 1.16.0 and 1.16.1 – npm’s latest is still
1.15.8 – for two different reasons. 1.16.0 never got past Validate version consistency, the
unrun task version:sync recorded in the 1.16.1 notes below. 1.16.1 cleared that gate and then
failed E2E gate — Node on the 18 TypeScript failures above; that job blocks the npm publish, so
the package never reached the registry. The wasm package is gated separately and did ship 1.16.1.
1.16.2 is the first release in which both gates pass.
- 1.16.2 is a fix-only release. The two defects above landed together in 1.16.1 and are the whole content of this one; no grammar pins moved.
[1.16.1] - 2026-09-01
Section titled “[1.16.1] - 2026-09-01”- The test-app pin check could no longer match the two alef-generated installers, so the release
gate was permanently blocked rather than merely stale. The 0.79.5 regen moved
test_apps/php‘s pin out of theVERSION="${1:-...}"default into its ownPINNED_VERSION=assignment and switched both installers’ shell literals from double to single quotes; the checker’s patterns matched nothing, and--fixtherefore could not repair them either. The patterns are now quote-agnostic and line-anchored, so a future template change still surfaces loudly as a no-match instead of silently passing. task version:syncwas never run for 1.16.0, leaving the dart, elixir, kotlin_android and swift_e2e test-app manifests plus the plugin’s own version source pinned at 1.15.12.- The plugin publish jobs no longer report success having published nothing. Both jobs probe the registry for the release version while uploading an artifact carrying the plugin’s own version, and neither depended on version validation. In v1.16.0 the npm job took a 403 for republishing 1.15.12 and still concluded success, and the PyPI job skipped an existing 1.15.12. Both now assert the bundle matches the release before uploading.
- Completed the alef regeneration interrupted in the 1.16.0 cycle, which had left 18 generated files stale.
- v1.16.0 reached no registry; 1.16.1 is the first complete 1.16 release.
[1.16.0] - 2026-08-31
Section titled “[1.16.0] - 2026-08-31”- Scoop is now a live release channel alongside Homebrew: a release publishes a Scoop manifest for
the CLI, and the install instructions cover it. The channel was inert until now because
scoopwas held out of the workflow’savailable-targets– alef only gained the target after v0.79.2, and naming one it does not recognise hard-errors the whole publish pipeline. The pinned alef is now 0.79.5, which carries it.
Changed
Section titled “Changed”- Refreshed 10 grammar pins to their upstream latest:
c3,cedar,cedarschema,cfml,dotenv,foam,mlir,nginx,sysmlandtwig. Parsing behaviour for those languages follows upstream.ablis deliberately held back – upstream’s newerhighlights.scmreferences nodes its ownparser.cdoes not define, and the grammar exceeds the regeneration threshold, so a bump fails query compilation.
- The grammar table generator no longer emits a plausible-looking wrong answer when parsers are
missing.
_grammar_abifell back to the default ABI for any parser it could not read, so running the generator against an incompleteparsers/tree – what aTSLP_LANGUAGES-scoped clone leaves behind, since it prunes rather than filters – silently rewrote the published ABI column for every language. It now fails, naming the count and the first ten missing parsers.
[1.15.11] - 2026-08-28
Section titled “[1.15.11] - 2026-08-28”Changed
Section titled “Changed”-
Refreshed 26 grammar pins to upstream latest.
apex,avro,cfml,dotenv,elisp,fish,gitcommit,json,magik,matlab,mlir,pkl,racket,rego,robot,roc,scala,sflog,soql,sosl,sourcepawn,spicedb,swift,sysml,tmux, andtwig.sflog/soql/sosl/apexshare one upstream monorepo and therefore one revision. The remaining 344 grammars were already current. -
Refreshed dependencies.
cargo upgrade --incompatiblereported all 58 workspace packages already at their latest versions, so onlyCargo.lockande2e/rust/Cargo.lockmoved. Python (gitpython, idna, packaging, pygments, pyrefly), Node (pnpm 11.24.0 plus 35 workspace packages), PHP (phpunit 13.x line), and Ruby (activesupport, csv, json, rubocop, rubocop-performance, sorbet-runtime) were taken to latest. C#, Elixir, and Swift re-resolved to identical lockfiles – they were already current.
Note on the abl pin
Section titled “Note on the abl pin”abl had an upstream update available and it was deliberately not taken. At
e5a40fd0dd, upstream’s own queries/highlights.scm matches (outer_join), a node type that
its committed src/parser.c and src/node-types.json at that same revision do not define.
abl is one of two grammars whose committed parser.c we ship as-is rather than regenerate
(99 MB, above what standard runners can regenerate), so we cannot resolve the mismatch by
regenerating from grammar.js the way we can elsewhere. Taking the bump made
query_compilation fail with Invalid node type "outer_join", so the pin stays at
5844930892. This is a hold at a known-good revision, not a downgrade.
Note on the Dart freezed pin
Section titled “Note on the Dart freezed pin”dart pub upgrade --major-versions proposes freezed ^4.0.0-dev.3. That is a prerelease, and
the 4.0.0 stable release requires Dart SDK >= 3.13.0 while this package declares
sdk: '>=3.11.0 <4.0.0'. Raising the SDK floor is a breaking change and does not belong in a
patch release, so freezed stays on the ^3.2.5 stable line and pubspec.lock is unchanged.
[1.15.10] - 2026-08-27
Section titled “[1.15.10] - 2026-08-27”task alef:syncnow runs the full version sync chain. It ranalef sync-versionsalone, which reaches the binding manifests but not the registry test-app pins or the plugin version pin. It is also the only sync tasktask --list-allsurfaces, becauseversion:synclives in an internal included taskfile – so the discoverable entry point was the incomplete one, and releases propagated through it stranded the plugin pin a version behind.
Note on 1.15.9
Section titled “Note on 1.15.9”1.15.9 was tagged but never published to any registry. Its publish run aborted on the
sync_plugin_version.py --expect gate, because at that tag Cargo.toml was 1.15.9 while
plugin/.ai-rulez/config.toml was still 1.15.8 – the drift the fix above prevents. 1.15.10
supersedes it; no artifact for 1.15.9 exists on crates.io, PyPI or npm.
[1.15.9] - 2026-08-25
Section titled “[1.15.9] - 2026-08-25”Changed
Section titled “Changed”- Regenerated all language bindings on alef 0.68.0.
-
The plugin version gate now runs on the commits that cause drift, and blocks the release when it finds any.
scripts/sync_plugin_version.py --checkwas correct andCI Pluginran it, butci-plugin.yaml’spaths:filter did not listCargo.toml— and a release commit bumpsCargo.tomland usually nothing underplugin/. The 1.15.7 and 1.15.6 release commits match zero paths in the old filter, so the gate was skipped on exactly the commits that strandplugin/a release behind. Being in sync at 1.15.8 was luck: that one commit happened to touchplugin/too.Cargo.tomland.task/version.ymlare now in the filter, so any core bump re-runs the gate. Andsync_plugin_version.pygains--expect <version>, asserting core and plugin both equal the version being released;publish.yaml’svalidate-versionsjob runs it against the tag, so a drifted plugin fails the release instead of shipping a bundle that lags the version it claims to be.
[1.15.8] - 2026-08-23
Section titled “[1.15.8] - 2026-08-23”-
The plain-Java artifact reaches Maven Central again. It was the only registry still stuck at 1.14.3: every 1.15.x publish skipped
Publish Maven package, because theE2E gate — Javait sits behind failed first on 8 assertions of the shapeexpected: <null> but was: <[]>.The cause was a disagreement inside each generated Java record. A component backed by a Rust
Veccarrying#[serde(default, skip_serializing_if = "Vec::is_empty")]was emitted as@Nullable, and the canonical constructor stored whatever it was handed —nullincluded — while the Jackson builder defaulted the same component toList.of(). A record built through the builder, or round-tripped through JSON, therefore never compared equal to the same record built through the constructor. Regenerating against alef 0.67.2 drops@Nullableon those components and adds a compact constructor normalizingnullto an empty collection, so both construction paths agree. Affected records:DataNode,DocstringInfo,ImportInfo,ProcessResult,StructureItem. -
The hand-written Java unit tests asserted the old, wrong contract. They required such a component to arrive as
null; the Rust fields behind them are plainVec, neverOption<Vec>, so empty is the truthful representation. They now assertassertEquals(List.of(), ...), which fails onnullas well as on a non-empty list — the assertions were tightened, not relaxed to accept either shape.mvn testinpackages/javareports 150 tests, 0 failures, 0 errors.
-
Prerelease mode for the registry-mode test apps.
task test-apps:prerelease:run(and:verify,:status,:clean) stages a throwaway copy of eachtest_apps/app under.prerelease/and redirects its dependency resolution at the in-repo package source, so the suite is runnable between a version bump and the publish — the window in which the pinned version exists on no registry andalef test-apps runcannot resolve anything. Covers rust, go, python, ruby, dart, elixir and swift;statusnames the remaining targets and why each needs a built artifact rather than a source path. -
scripts/check_test_app_pins.py— a gate that fails when a test app is pinned to a release other than the one being built. It re-derives all 18 pins across everytest_apps/app plus thealef.tomlregistry pins fromCargo.toml, deliberately independent of alef, so a change in whatalef sync-versionsis willing to write surfaces as a failure instead of as silent drift. A pattern that matches nothing is an error, not a pass — that was the Dart failure mode.--fixrepins everything;--release Xchecks against the version being published and also catches aCargo.tomlthat disagrees with the tag.Wired in three places:
task version:syncrepins afteralef sync-versions, theCheck version syncCI step gates every push, and the publish workflow’svalidate-versionsjob gates the release itself against the tag. Also exposed astask test-apps:check-pins/:fix-pins. -
scripts/sync_zig_zon_hashes.py— regenerates and verifies thetest_apps/zig/build.zig.zonpackage hashes from the tarballs their URLs name. A Zig package hash is a content digest, so it cannot be derived from a version string the way every other test-app pin can; the only reproducible source iszig fetch <url>, which prints the exact hash Zig will demand.--fixrewrites them, the default verifies. Assets that 404 are reported as not-yet-published rather than as failures, which is the normal state between a version bump and its publish, so the check is safe to run at any point in the release cycle. Exposed astask test-apps:check-zig-hashes/:fix-zig-hashesand wired into theci-zigjob — the only job with a Zig toolchain. Because the digest only exists once the release has published its Zig assets,--fixruns after a publish, never during release prep. -
The publish workflow now refreshes the Zig package hashes itself. A new
refresh-zig-package-hashesjob runs afterpublish-zig— the earliest point at which the tarballs the digests are taken over exist — recomputes every hash and commitstest_apps/zig/build.zig.zonback tomain, modelled on the Swiftupdate-swift-package-manifestjob. Catching the drift only made the manual--fixstep forgettable again; this removes the step. The job cannot break a release that would otherwise succeed: nothing depends on it, it iscontinue-on-error, and it commits one file or none. It skips whenmainhas already moved to a version this release did not publish, and reports every outcome — refreshed, already current, skipped, failed — in the run summary, sincecontinue-on-errorwould otherwise let a failure pass unnoticed.--require-publishedis new on the script for this caller: after a successful publish a 404 is a missing upload, not the pre-publish window, and is a failure rather than a skip.
-
test_apps/zigcould not build at all — its five package hashes had been stale since 1.14.3.alef sync-versionsrepoints each.urlat the release being cut, but a Zig package hash is a content digest over the fetched tarball, so nothing recomputed it and all five sat at the 1.14.3 values while the URLs advanced to 1.15.7.zig buildfailed outright:error: hash mismatch: manifest declares tree_sitter_language_pack-1.14.3-jCz0Y85sQQ...but the fetched package has tree_sitter_language_pack-1.15.7-jCz0Y86TSQ...This is not the cosmetic staleness it looks like — the declared hashes are byte-identical to the real 1.14.3 tarball hashes, so every release from 1.15.0 to 1.15.7 shipped an app that resolves nothing. Nothing reported it because no workflow builds
test_apps/zig:ci-zigbuildspackages/zigfrom the working tree andci-e2erunse2e/zig, so the one app that resolves these URLs was never exercised. All five are regenerated viazig fetch, andci-zignow verifies them. -
Five registry-mode test apps were validating an already-published release, not the one being built.
test_apps/{elixir,swift_e2e,php}were pinned at 1.15.1 andtest_apps/{dart,kotlin_android}at 1.15.2, against a 1.15.7 tree. Each gate passed — it proved the old release still installs, which is not what a registry-mode gate is for. All five are now repinned to the release being built.The cause is not the matched-nothing regex fixed for Dart below. Every one of these five sync rules does match its file.
alef sync-versionsreaches these paths through its catch-all branch, which refuses to rewrite a file that carries no alef provenance marker on the grounds that it reads as hand-written; it emits aWARNnaming each skipped path and exits 0. So the rules looked applied and never were.alef.toml’s own[crates.e2e.registry.packages.*]pins are skipped for the same reason.Compounding it, the
Check version syncCI step diffs onlypackages/andcrates/, sotest_apps/drift was outside the gate’s scope even when sync did rewrite it. -
The
test_apps/dart/pubspec.yamlversion-sync rule required a caret the generated pubspec does not contain, so it matched nothing. Fixing the rule was necessary but not sufficient: the file is one of the five skipped by the catch-all branch above, so it stayed at 1.15.2 afterwards.
[1.15.7] - 2026-08-23
Section titled “[1.15.7] - 2026-08-23”- Regenerated against alef 0.66.0 (from 0.64.0, via 0.65.0). This clears the Swift E2E gate
failure that blocked the v1.15.6 release:
is_emptyon a non-optional array reached through an optional parent emitted aBool?intoXCTAssertTrue, which does not typecheck, so the gate could never pass and every publish job downstream of it was skipped. The generated assertion now coalesces (?? true).alef verify --exit-codereturns 0 against the regenerated tree. - Generated documentation snippets omit the
level:front-matter key instead of emittinglevel: null, and every backend now orders IR items before emission — both are deliberate changes in alef 0.65.0/0.66.0 and account for the bulk of the regeneration diff.
-
A failed publish can no longer announce itself as a successful release.
announce-discordgated on!contains(needs.*.result, 'failure'), which is satisfied when every dependency is skipped – the exact shape of the v1.15.5 run, where all 13 publish jobs skipped and the release reached zero registries. It now additionally requiresrelease-finalize(the job that asserts every enabled target actually published) to have succeeded. -
publish-wasmnow genuinely waits onpublish-node.publish-nodewas listed in itsneeds:but never restated in itsif:; because the job opens withalways(), theneeds:entry alone gated nothing and WASM could publish to npm after the node publish had failed. The gate is deliberately negative-form (!= 'failure') so a legitimately skippedpublish-node– npm already at this version, or node not among the release targets – does not cascade into skipping WASM. -
68 of the 85 files
alef verifyreported frozen are now recorded as alef-owned. Every one of them is a create-once seed, so plainalef adoptrefuses them and the report’s own remedy is unreachable without--clobber-create-once-seeds. Only the files that already match generated output byte-for-byte were adopted, so no content changed: 66 are recorded in.alef-ownership.toml(formats that cannot carry a marker), andpackages/go/go.modpluspackages/zig/build.zig.zongained the marker header. The 17 left frozen all differ from generated output and need a content decision – notablypackages/java/checkstyle.xml, whose generated form reverts the${config_loc}+optional="false"SuppressionFilter fix, andcheckstyle-suppressions.xml, whose generated form drops the NativeLibLineLengthsuppression.alef verifystays red on those 17. -
task docs:snippets:validate:langruns again. It invokedalef snippets validate, a subcommand that does not exist (alef snippetshaslist,check,parse,audit,gaps), so the target could only ever fail withunrecognized subcommand. It now callsalef snippets check --strict --cache off --lang {{.LANGUAGES}}, matchingdocs:snippets:validatewith the per-language filter added. The--snippetspath is dropped becausecheckreads[workspace.docs.snippets], andLEVELis dropped becausecheckhas no level flag – each validator picks the deepest level its toolchain supports. -
alef verifyno longer reports 8,415 stale files.alef allran at ada0fac4e and the 1.15.6 version bump landed after it at e9816afda;alef.tomlis a hashed generation input, so every generated file was stamped against pre-bump inputs and nothing re-stamped them. The tree is regenerated after the bump, which rewrites thealef:hash:line in ~8,380 files. The remaining diffs are real drift the earlier regen never covered: all oftest_apps/*(alef alldoes not run the test-apps stage –alef test-apps generateis a separate command), plus thepackages/*/README.mdversion strings. Regeneration order is now bump-then-generate; runningalef allbeforealef sync-versionsre-opens this. -
Validate (Lint & Format)no longer fails on every release commit. The job passedpython-extra-projects: packages/pythonto the shared validate workflow, which runsuv pip install -e packages/python– a maturin build ofcrates/ts-pack-core-py, and so a run ofcrates/ts-pack-core/build.rs. That job never downloads thelanguage-parsersartifact, soparsers/is empty and build.rs falls through to theparser-sources-<version>.tar.zstrelease asset for the version inCargo.toml– which does not exist yet on a release commit, because the version is always bumped before its release is published. Run 32562369389 (v1.15.6) died on that 404 at step 9 of 32, soFormat checkandLintnever ran at all. The input is dropped: its stated purpose (letting pyrefly resolvetree_sitter) was already satisfied by the root dev group inpyproject.toml, and._nativeresolves from the checked-in_native.pyistub, sopyrefly check packages/pythonreports 0 errors without the wheel being built. -
Rust snippet validation no longer downloads parser sources. The
rustsnippet session now setsTSLP_OFFLINE=1. Itscargo checkbuilds ts-pack-core through a path dependency and so runs the same build.rs against the same emptyparsers/tree – the identical 404 on a release commit, one step later in the same job.TSLP_LANGUAGESis unset there, so no grammar is compiled into that check either way; skipping the sources changes nothing it validates. -
Quick Start’s kotlin-android
processandquickstarttabs now import the generated corpus (generated/kotlin-android/process/config_all_python.mdandgenerated/kotlin-android/parsing/parsing_python_function.md) instead of two hand-written stand-ins. Those stand-ins were kept back in 064e3c325 because the generated twins failed withunresolved reference 'io'; that classpath defect was fixed today (651ddbac1), so the stand-ins were deleted. Not re-verified with a session-basedalef snippets checkrun – watch the next CI run for kotlin_android snippet failures.
Changed
Section titled “Changed”alef.tomlnow documents next to[workspace.docs.snippets] dirswhydocs-site/src/snippets/ cli/*.mdstay outside the validated snippet tree: the CLI is not an alef binding target, so no generated/cli directory can exist for them to migrate into. This was previously only implied by their absence fromdirs– nothing recorded that the gap was deliberate.
[1.15.6] - 2026-08-22
Section titled “[1.15.6] - 2026-08-22”-
Quick Start docs import kotlin-android’s
download/prefetch_languages.mdsnippet instead ofdownload_single_language.md. The latter no longer exists: excluding the download family from the kotlin_android target (it sits behind ts-pack-core’s opt-indownloadfeature, which the JNI crate does not enable) removed it, breaking the Astro build with UNRESOLVED_IMPORT. This mirrors what wasm — excluded for the same reason — already did. -
The Docker image builds again (cc140342d, previously unreleased).
Publish Docker Imagefor v1.15.5 failed on both architectures atapk add ... python3=3.12.13-r0: Alpine’s repository only ever carries the current revision of a package, so an exact-rNpin stops resolving the moment a security bump lands and the old revision is deleted. The pins are now fuzzy (python3=~3.12), which keeps the major.minor the base image already fixes while letting the revision float. Note that pinning the base image by digest is not a substitute:apk --no-cachefetches the package index over the network at build time, so a digest-identicalrust:1.95-alpine3.22still fails on the old exact pins today. -
Go snippets are validated against a library that exists. All 521 of them failed in the validate job with
ld: cannot find -lts_pack_core_ffi. The-Lpath was right; nothing had ever put a library behind it.binding.golinks${SRCDIR}/.lib/<platform>,.lib/is gitignored, and the validate job builds no binding packages – sogo build ./..., which is whatcompile-level validation runs, could not link. The go snippet session now has abeforehook (scripts/stage_go_native.sh) that buildsts-pack-core-ffiand stages it there, the same artifactci-e2e.yaml’s Go job restores by hand.timeout_secswent from 120 to 900 because the same budget boundsbeforehooks, and a cold cargo build does not fit in two minutes. -
Java snippets are compiled against a built package. All 521 were
Unavailablein the same run, which understrictfails the run exactly as aFaildoes –has_incomplete_coveragecountsSkip,UnavailableandDowngraded. The Java validator builds its classpath from<pom dir>/target/{classes,test-classes,dependency/*.jar}and nothing had built any of it, so the session now runsmvn compileandmvn dependency:copy-dependenciesfirst. That is a different artifact and a different build from the Go fix above, not one change covering both. 513 of the 521 now compile; the remaining 8 are an upstream alef defect, not an environment gap – see Known issues. -
wasm/typescript snippets are type-checked against a package that has type declarations. All 511 were
Unavailablewith “Cannot find module ‘@xberg-io/tree-sitter-language-pack-wasm’” – the TypeScript validator resolves that module through the manifest’stypesfield (pkg/nodejs/ts_pack_core_wasm.d.ts), which only exists after awasm-pack build, andpkg/is gitignored. The wasm snippet session now runsscripts/stage_wasm_types.sh, which builds withTSLP_LANGUAGES=mojo,nim,norg– all three are wasm32-unbuildable and already skipped bybuild.rs, so the build needs no wasi-sdk and finishes in well under a minute, and the generated public API surface does not vary with which grammars are statically linked in. -
kotlin_android snippets are compiled against a built package. All 521 were
Unavailablewithunresolved reference 'io'– the Kotlin validator resolves its classpath through Gradle’s own task model, which reportscompileDebugKotlin’sdestinationDirectorywhether or not anything has been compiled into it, and nothing had. The session now runsscripts/stage_kotlin_android_sdk.sh, which writeslocal.propertiesitself (alef’s snippet runner does not pass ANDROID_HOME/ANDROID_SDK_ROOT through to session commands) and then compiles.[crates.kotlin_android]also gained the sameexclude_functionslist[crates.wasm]already has: the 10download-family snippets fail for the identical reason wasm’s do (the feature isn’t enabled for this target), so they are excluded from generation entirely rather than validated against a package that can’t have them. 511 of the 521 now compile against a built package locally; the remaining 10 need a fixture regenerate to pick up the exclusion, which is out of scope for this change. -
zig snippets are validated with a zig toolchain, then linked against a built FFI library. All 521 were
Unavailablewith “zig toolchain not found” – the validate job never installed one;ci.yamlnow passessetup-zig: truetoreusable-validate.yml@v1, which gained the input upstream. That alone was not enough: compile-level zig validation links a real executable (the generated per-snippetbuild.zigcallsb.addExecutable+b.default_step.dependOn), so it also needsts-pack-core-ffibuilt atpackages/zig/build.zig’s default-Dffi_path(target/release). The session now runsscripts/stage_zig_ffi.shfirst, mirroringstage_go_native.sh’s FFI build. All 521 now pass.
Known issues
Section titled “Known issues”-
8 generated Java snippets reference an exception class the binding never declares.
download_invalid_language,error_empty_language_name,error_handling_get_language_empty_string,error_handling_unknown_language,parse_empty_language,process_unknown_language,get_language_unknownandget_parser_unknownallcatch (TreeSitterLanguagePackException ...), which does not exist inpackages/javaand is not referenced anywhere else in this repo. Alef derives that name twice and disagrees with itself: the binding emitter usesbackends::java::naming::exception_class_name, which suffixes the raw FFI class and yieldsTreeSitterLanguagePackRsException, whilee2e/codegen/java/snippet.rsre-derives it asformat!("{simple_class_name}Exception")from the facade class, whoseRssuffix has been stripped. No build step can close this, and hand-editing the snippets is pointless because the same job regenerates them and gates on the diff. Until alef is fixed the Java session keeps 8Unavailableresults and the validate job stays red. -
CI Sanitizeno longer instruments third-party C it cannot link. The job injected the sanitizer flags throughCFLAGS_x86_64_unknown_linux_gnu, on the stated assumption that a target-scoped variable leaves host build scripts alone. It does not: cc-rs keys that variable on the triple alone, and on a Linux x86_64 runner the host triple isx86_64-unknown-linux-gnu, so the flags also instrumented the C inzstd-sysandring– both build-dependencies ofts-pack-core, whose objects are linked into its build-script binary. Cargo withholds RUSTFLAGS from host units when--targetis passed, so that link never received the-lasan/-lubsanthe job adds and died with undefined__asan_*/__ubsan_*symbols while linkingbuild_script_build, before a single grammar was compiled.build.rsnow readsTSLP_GRAMMAR_CFLAGSand applies it to the vendored grammar C/C++ on the static-link path only, which is the one place the host/target split is expressible on stable. -
The e2e drift gate no longer depends on which formatters a machine happens to have.
alef e2e generateranrufffor python andpnpm dlx oxfmtfor node and wasm. The validate job has neither – it sets up python, java, go, ruby, dart and elixir, and the reusable workflow has nosetup-nodeinput – so alef loggedskipped -- executable not found, emitted unformatted suites, andgit diff --exit-codefailed on 45 files that were correct on every developer machine. All three now go throughpoly, which the job installs before the generate step and which bundles the same engines. -
The Zig
get_languagetest no longer pins the transport of an unknown-language failure. With the compile error fixed the suite ran for the first time and the last test failed:get_languageon an unknown name returnederror.Download, noterror.LanguageNotFound. Both are documented outcomes – the manifest lookup falls back to a fetch, and CI cannot reach the release artifacts for the version under test – so the test now asserts the invariant, that no language is handed back. -
Five CI gates that had never actually executed. Every one was masked by an earlier job failing or being cancelled, so all five failures surfaced at once the first time the gates ran.
- The Zig package tests called
has_parseron the result ofnew_language_registry()withouttry; that function returnserror{UnknownFfiError}!LanguageRegistry, so both Zig jobs failed to compile. packages/kotlin-android/.../Language.ktwas a pre-alef-0.61 handle wrapper.Languageis akotlin_androidcapsule type, so alef stopped emitting the wrapper, but it does not delete files it no longer generates – the stale class stayed on disk with aclose()callingTreeSitterLanguagePackBridge.nativeFreeLanguage, a symbol neither the Bridge object nor the JNI shim declares. Both AAR jobs failed on the unresolved reference.quickstart.mdxstill importedwasm/download/download_single_language.md, one of the ten wasm download snippets dropped in c5da3347a because the wasm package never exported those symbols. The docs build failed withUNRESOLVED_IMPORT; the tab now uses the wasmprefetch_languagessnippet.- The
validatejob pinned alef to 0.62.8 whilealef.tomlmoved to 0.62.10, soalef e2e generate && git diff --exit-codegraded the committed tree against a different generator and reported 45 files as drift. task swift:e2e:testbuilt onlytree-sitter-language-pack-swift.Package.swiftlinkslibts_pack_core_ffi.aby absolute path and silently falls back totarget/debugwhen the release archive is missing, so the Swift e2e link failed on a debug archive that was never built. The four Swift task builds now include-p ts-pack-core-ffi, matchingci-swift.yaml.
- The Zig package tests called
-
The grammar tables record AL at ABI 15. AL’s committed
parser.cis now over the 24 MB regeneration exemption, so it ships at ABI 15 rather than the pack default of 14. The languages page, the README partial and README claimed 14, and the bundled-query columns for Fsharp Signature, Leo, Sflog, Soql and Sosl were stale. -
The typst scanner’s
deserializebounds its reads, andserializeno longer truncates.vec_u32_deserializenever took thelengthtree-sitter passes totree_sitter_typst_external_scanner_deserialize. It read an 8-byte element count out of the buffer and thenmemcpy’dcount * 4bytes with no bound against the serialized length, so any buffer that was shorter or differently framed than the oneserializewrote was read past its end — the count itself came from the buffer and was used directly as a copy length. Given a 1-byte buffer the scanner restored a full 40-byte state, i.e. it read 39 bytes it was never given.deserializenow takes the length, bounds the count before it is multiplied (which also stopscount * 4andsizeof(uint32_t) * capfrom wrapping into an undersized allocation), and fails closed to the base state rather than applying a half-restored one.The previous patch bounded only the
serializeside, and did it by truncating. That was itself the mechanism that made a misframed buffer reachable from ordinary input: whenindentationconsumed the budget,containerswas silently written as nothing at all, the four trailing scalars were appended in its place, and the reader then took those scalars plus four bytes of whatever followed the buffer ascontainers’ 8-byte length prefix. Truncation is undetectable to the reader; absence is not.serializeis now all-or-nothing and returns 0 when the state does not fit, which tree-sitter reads as “no state” and whichdeserializealready handles exactly.This is a distinct defect from the prefix-width mismatch described below, and it survived that fix: the widths now agree, but nothing bounded the reads. It is the crash behind
process("#let x = 1", {"language": "typst"})(#161, GitHub #180), which took down the Ruby, Python, PHP, Elixir, Java, Dart and Go E2E gates along with the C# test host and a Node vitest worker. -
vec_u32_popin the typst scanner reads the last element instead of the one past it. Upstream returnsself->vec[self->len--]. The post-decrement indexesvec[len]— one past the last element — and only then shortens the vector, so every pop returned four bytes of whatever followed the elements and read them from outside the allocation wheneverlenhad reachedcap(whichdeserializeguarantees, since it sizescapto exactlylen). It is nowvec[--self->len], and the empty-vector guard is written as real control flow because theassert()macro in that file expands towhile (false);and enforces nothing.Nothing in the scanner consumed the value — all four call sites discard it — and the length bookkeeping was already correct, which is why the defect outlived the serialization fix it was found next to.
patches/typst/vec-u32-pop-off-by-one.patchcarries it; the existingserialize-buffer-overflow.patchkeeps its own scope, and its first hunk no longer takes thevec_u32_popline as context so the two patches apply and reverse-apply in either order. -
processfixtures name the field they actually assert on. Twenty-one assertions saidcontainson the barestructureorimportscollection and meant “some item has this kind” (or, for imports, “this source”). Up to alef 0.60.0 that generated a debug-string substring match over the whole collection, which happened to be true. Alef then narrowed a collectioncontainsto compare only each item’sname, and sinceStructureItem.nameis the identifier (main) rather than the kind (Function), every one of those assertions began failing — 19 of the Rust E2E suite’s 37processtests. The fixtures now spell the path out asstructure[].kindandimports[].source, which restores the intended check explicitly.process_testgoes from 18 passed / 19 failed to 37 passed / 0 failed.Only the Rust suite is regenerated here; the other language E2E suites need the same regeneration before their gates clear.
-
Parser binaries are rebuilt for every release instead of being copied out of a stale cache. The v1.15.5 release run shipped a stale
linux-x86_64parser set: every one of its 371 parser shared libraries was byte-identical to v1.15.0’s, nothing having been recompiled since 2026-08-13. (Measured per platform:linux-aarch64,macos-arm64,macos-x86_64and both Windows targets were genuinely rebuilt — onlylinux-x86_64was stale, which is exactly the platform GitHub #180 was reported on.) The Rust cache restorestarget/with mtimes newer than the fresh checkout, so cargo judged the build script up to date and skipped it, and the packaging step (find target/release/build -path '*/out/libs/*' -exec cp) then shipped whateverout/libs/the cache happened to carry. The build-script output directory is now dropped before the parser build in bothpublish.yamlandci-e2e.yaml, sobuild.rsreruns and no stale artifact survives to be packaged.This is why the typst segfault (#161, GitHub #180) outlived its own fix.
process("#let x = 1", {"language": "typst"})crashed because the bounded-serialize patch added in v1.15.0 narrowed the scanner’s element-count prefix touint32_twhilevec_u32_deserializestill read it back assizeof self->len— 4 bytes written, 8 bytes read, so the garbage upper half became a huge element count that memmoved off the end of tree-sitter’s 1 KiB serialization buffer. The source was corrected on 2026-08-14 in 61e331426 and has been present since v1.15.1, but the corrected scanner was never compiled: the shippedlibtree_sitter_typst.sostill stores the prefix with a 4-bytemovl. That crash took down the Ruby, Python, PHP, C FFI, Dart, Elixir and Go E2E gates.
-
pnpm installsucceeds in the WASM e2e suite again, so the WASM package publishes. The last dependency upgrade movedvitestto ^4.1.10 ine2e/wasm/package.jsonwithout regeneratinge2e/wasm/pnpm-lock.yaml, and CI hasfrozen-lockfileon by default:specifiers in the lockfile don't match specifiers in package.json. That failed theE2E gate — WASMjob, andpublish-wasmrequires it to succeed — so v1.15.4 would have shipped every other ecosystem while silently leaving npm one version behind. The lockfile is regenerated. -
Nested
Cargo.lockfiles track the workspace version. Carried forward from v1.15.4: theValidate versionsgate failed on stale locks undere2e/rustandpackages/ruby/ext/ts_pack_core_rb/native, which skipped the crates.io publish job and cascaded into every language build withfailed to select a version for the requirement.
Neither v1.15.3 nor v1.15.4 published artifacts to any registry — use this version.
[1.15.4] - 2026-08-21
Section titled “[1.15.4] - 2026-08-21”- The release actually publishes. v1.15.3 was tagged and released but published nothing: the
Validate versionsgate failed on staleCargo.lockfiles undere2e/rustandpackages/ruby/ext/ts_pack_core_rb/native, which skipped the crates.io publish job. Every language-package build downstream of it then failed withfailed to select a version for the requirement ^1.15.3, becausealef publish preparewas retrying against a registry version that had never been pushed. The lockfiles are refreshed and the gate passes. v1.15.3 carries no artifacts on any registry — use this version instead.
[1.15.3] - 2026-08-21
Section titled “[1.15.3] - 2026-08-21”-
The generated Node e2e suite is valid TypeScript again. A fixture whose call returns void and whose only assertion is
not_errorrendersawait expect(...).resolves.not.toThrow(), but theit(...)callback it lives in was only markedasyncwhen the call was async. For a synchronous call —cleanCache,configure,init,prefetch— that producedawaitinside a plain arrow function, a hard syntax error. Becausealef allformats every language in a single phase, the formatter rejecting one file aborted formatting for all of them, so the whole tree was left unformatted and unstamped. Fixed in alef 0.62.8. -
The Zig e2e suite no longer panics on a serde-omitted field.
data_extraction_json_empty_objectassertsis_emptyon aVec<DataNode>that#[serde(skip_serializing_if)]omits entirely when empty, so the generated JSON accessor’s.object.get("data").?hit a missing key and panicked with “attempt to use null value”. alef now tracksskip_serializing_ifas a fact distinct fromOption<T>-optionality and guards the lookup withorelse .null. -
Generated e2e tests no longer skip themselves where a
not_nullassertion was declared. Fixtures whose only assertion was “returns a valid X” rendered asassumeTrue(false, "alef rendered no runnable expectation")— a test that reported green while checking nothing. They now emit a realassertNotNull(result, "expected non-null result"). 844 assertions across the JVM targets were affected. Regenerated on alef 0.62.8. -
Enum-valued fields are compared against their wire value instead of an enum’s
toString().result.data.kindis aDataNodeKind, and every backend compared it directly to the string"KeyValue". Each target now goes through its own wire-text conversion —toWire()in Kotlin,JsonSerializer.Serialize(...).Trim('"')in C#,_alefE2eTextin Dart,_alef_e2e_textin Python,to_stringin Elixir,format!("{:?}", ...)in Rust — so the assertion tests the serialised value rather than an accidentalDebug/toStringmatch. -
The C examples free the enum handle they allocate.
ts_pack_data_node_kindreturns aTS_PACKAlefHandle, not achar *; the generated C now takes the handle, asserts it is non-zero, converts withts_pack_data_node_kind_to_string, and callsts_pack_data_node_kind_free. The previous form assigned a handle tochar *. -
The JNI bridge formats errors with
to_string()rather thanformat!("{e}"), dropping an allocation per thrown exception across 25 call sites. -
The Java package’s javadoc and compiler source sets no longer fight each other. alef now emits both the
maven-javadoc-plugin<sourceFileIncludes>(restricting javadoc to publishable API sources, so test-scoped imports cannot failattach-javadocs) and themaven-compiler-plugin<excludes>, plus a checkstyle<excludes>**/.alef/**</excludes>.packages/java/pom.xmlis now alef-owned and carries a provenance marker; the hand-applied javadoc fix it previously held is reproduced by the generator. Checkstyle moves to 13.11.0 and surefire/failsafe to 2.22.2.
Removed
Section titled “Removed”DownloadTestis gone from the Android e2e suites.crates.kotlin_android.featuresdoes not enabledownload, so alef now emits anExcludedBindingsTestof@Disabledcases naming the gating feature instead of tests that could never link.
Changed
Section titled “Changed”- alef pinned to 0.62.8 in
alef.tomland in the CIalef-versioninput. - All Rust dependencies taken to their latest versions (
cargo upgrade --incompatiblefollowed bycargo update): 70 packages upgraded, one dropped, no downgrades.
[1.15.2] - 2026-08-19
Section titled “[1.15.2] - 2026-08-19”Use this release instead of 1.15.1. 1.15.1 was tagged but never published — its release run failed every e2e gate (Ruby, Elixir, Python, Go, Rust, C#, WASM, Java, Node) plus the WASM build, so no artifact from it reached any registry. crates.io still tops out at 1.15.0. Everything listed under 1.15.1 below ships here.
-
Generated bindings no longer assert a required field is optional. The generator derived field optionality from a hand-maintained config list rather than from the extracted IR, so a crate that declared none — as this one does — had every optional wrapper resolved wrongly across 13 language backends. Go’s
assert.True(result.Data)against a*DataNodeis the shape that failed the release. Regenerated on alef 0.62.0, which derives optionality from the IR. -
Generated C examples no longer call a constructor the FFI never exports. A
Vec<String>argument resolved its element type to the std typeString, so the generator emittedts_pack_string_from_json("[]")and a matchingts_pack_string_free(...)— symbols nothing declares, failing every affected snippet with “call to undeclared function”. The C ABI takes the argument as a plainconst char *JSON string, so it is now passed directly:ts_pack_prefetch("[]"). Fixed in alef 0.62.2. -
StructureItem.kindis no longer typed as optional. The Rust core declarespub kind: StructureKindwith#[default] Function; bindings emitted the equivalent ofkind: StructureKind | str | None = None, making a required field nullable and defaulting it to the wrong value. It now carries the real default.
Changed
Section titled “Changed”- CI runs fixture snippet validation again (
check-fixture-snippets), pinned to the alef version that generated this tree. It had been disabled on the premise that the shared workflow’sv1tag does not declare the input;v1does declare it. This is the gate that catches generated examples which no longer compile against the API they document.
[1.15.1] - 2026-08-18
Section titled “[1.15.1] - 2026-08-18”Parsernow implementsDebug, reporting the configured language name and parse limits. It could not be derived — the wrappedtree_sitter::Parseris aNonNullnewtype with noDebugof its own — so the pointer is elided. Debug-printing aParser, or aResult<Parser, Error>returned byget_parser, previously failed to compile with E0277.
Use this release instead of 1.15.0. The 1.15.0 GitHub release is incomplete — it carries the parser bundles and the Rust CLI, but none of the Go, C FFI, Elixir, PHP, Swift or Zig archives — and most bindings were never published at all: crates.io and Packagist got 1.15.0, but PyPI, npm, the npm WASM package, RubyGems, both Maven Central artifacts, NuGet, Hex.pm, pub.dev and the Homebrew tap did not. 1.15.0 also shipped a crash in the typst grammar, described below.
-
The language bindings are generated again. Two independent generator-configuration faults had to be cleared, and because generation is all-or-nothing across backends, either one alone stopped every binding from being produced. First, three keys in
alef.tomlwere not in the generator’s schema; they had previously been discarded in silence, and a move todeny_unknown_fieldsturned them into a hard parse error that failed the run before any backend was reached. Second, six backends — C#, Java, Kotlin/Android, Go, Swift and Zig — now have to declare an ownership contract for the types they hand to a host tree-sitter library, and none of them did. -
Typst crashed the process on any input, including
#let x = 1. Introduced in 1.15.0 and affecting no earlier release. The external scanner’s serialization patch wrote the vector length prefix as a 4-byteuint32_tbut read it back as an 8-bytesize_t, so deserialization took four bytes of adjacent buffer as the high half of the element count andmemmoved that many elements off the end of tree-sitter’s fixed 1 KiB serialization buffer. The result was aSIGBUSat a different address on each run. The prefix is nowsize_ton both sides. If you parse typst, 1.15.0 is unusable; there is no workaround short of upgrading. -
Brightscript had no syntax highlighting at all. Upstream added the
mgrammar rule and its(m) @keywordhighlight in one commit without re-runningtree-sitter generate, so the committed parser had nomnode and the entire highlights query was rejected — not merely one keyword. Our pin sat on that merge. Upstream has since regenerated, so the pin moves forward one commit. -
The Go module could not link out of the box on four of its five platforms. FFI libraries were staged under Go’s
GOARCHspellings (linux-amd64,linux-arm64) whilebinding.go’s cgoLDFLAGSandcmd/setupboth look for alef’s labels (linux-x86_64,linux-aarch64) verbatim. Onlymacos-arm64happened to line up; every other platform required runningcmd/setupfirst. -
The Java JAR threw
UnsatisfiedLinkErroron every ARM64 Linux and Windows host. The loader resolves natives underlinux-aarch64/windows-aarch64, but the build matrix packaged them aslinux-arm64/windows-arm64, so the JAR contained no directory the loader would look in and it fell through toSystem.loadLibrary. macOS ARM was unaffected because the loader special-cases it. -
The Swift package pointed at the previous release’s binary artifact.
Package.swift’sbinaryTargetURL is rewritten to the current version on each release, but it was left onv1.15.0, while the publish workflow injects the checksum of the bundle built for the release actually being cut. SwiftPM verifies the downloaded bundle against that checksum, so the two disagreeing would have failed resolution outright rather than silently fetching a stale binary. The test app’s Rust dependency had drifted the same way and was two releases behind.
Changed
Section titled “Changed”- The
Languagehanded to a host tree-sitter library is borrowed, and that is now stated and enforced. Every binding that exposes a grammar to a third-party tree-sitter runtime now declares that the pointer is borrowed for the process lifetime, that the host must not free it, and that it is ABI-compatible with the host’s own language type. This is a declaration of what was always true — the grammars are'staticand were never owned by the caller — so no runtime behaviour changes; the contract is simply checked at generation time now instead of being assumed. - Publishing to crates.io is now ordered after the GitHub release is complete. In 1.15.0 the
crate went out roughly seven hours before its release assets did, leaving a window where the
version resolved but the artifacts
build.rsdownloads returned 404 (#177). The draft-publish step also gated on nothing, because the job opened withif: always(), which is how a release missing most of its assets still went public. Asset uploads are now checked individually before the release is published, and the crates.io publish depends on that.
[1.15.0] - 2026-08-13
Section titled “[1.15.0] - 2026-08-13”This release ships the Rust crates only. The language bindings are held back pending generator fixes and will follow; they skip the crates once regenerated.
Changed
Section titled “Changed”-
PackConfig.cache_diris a base directory, not the final library path. The crate now appendstree-sitter-language-pack/v{version}/libsto it, exactly as it does to the platform default, socache_dir = "/tmp/parsers"resolves to/tmp/parsers/tree-sitter-language-pack/v{version}/libs/. Earlier releases used the configured path verbatim, which put the manifest, bundles and lock file in that directory’s parent and let a cache built by one crate version be reused by another. If you setcache_dir, your parsers will be re-downloaded once into the new location. -
Query results change for roughly fifteen languages, because they were being served another grammar’s queries.
_score_query_candidatehad an unreachable preference branch, so for grammars vendored from a monorepo the winner was decided by filesystem traversal order:cfmltook all five of its query files from sibling grammars,psvandtsvtook CSV’s, anddtdtook XML’s.leotook itslocalsfrom m68k and itsindentsfrom ocaml. Affected: apex, bsl, cedarschema, cfml, dtd, fsharp_signature, leo, markdown, ocaml_interface, postgres, psv, sflog, soql, sosl, tsv, xml, ziggy_schema. If you hold golden files for any of these, regenerate rather than trust them — several previously returned plausible-but-wrong captures rather than an error. -
sflog,soqlandsoslnow ship fewer query files. Upstream never wrote the missing kinds for them; what they had belonged to a sibling grammar.Known and not yet fixed in this release:
templstill receives Go’shighlightsandtags, which reach it through tree-sitter-templ’s own npm dependency on tree-sitter-go. They compile, because templ embeds Go, so no gate catches them. A fix is written but is being held until its effect on every grammar with npm dependencies has been measured. -
All 46 out-of-date grammars refreshed to upstream HEAD. Two carry parse-tree shape changes for previously-valid source rather than merely new syntax:
al(3.2.1 → 4.0.1: dangling-else binding, operator precedence, statement-terminator restructuring) andscala(operator precedence and associativity per SLS 6.12.3). Node types are unchanged in both, so queries still compile.
PackConfig::try_discover(), which distinguishes “no config file” (Ok(None)) from “config file found but unreadable or malformed” (Err, naming the path).discover()keeps its infallible signature and now warns instead of silently returningNone.- Windows cache-directory hardening: the cache is verified to sit under
%LOCALAPPDATA%, which Windows already restricts to the owning user, and warns when it does not. Advisory only — it never refuses. - Always-on
tracingspans and events across the parse and intelligence pipelines. - Documentation snippets are generated from the complete E2E fixture corpus and checked for fixture-by-language coverage parity in local tasks and CI.
-
get_queryreturned an error for dart and phptags: both hand-written overlays named node types their grammars do not have, and nothing ever compiled them.ocaml_interfacehighlights likewise, where upstream ships one shared query set written for the implementation grammar. -
Intelligence extractors no longer drop a module docstring that follows a shebang or licence header, fabricate docstrings, or miscount comment rows.
SymbolInfo.docis populated. -
Shebang detection works on files that begin with a UTF-8 BOM.
-
A cache-registration race could leave the process wedged; a panic inside a critical section no longer poisons a lock permanently.
-
DownloadManagerresolves language aliases, and configuration errors are reported rather than discarded. -
Only grammars whose external scanner actually keeps global state are serialised during parsing — measured at 1 of 199 — instead of a single global parse lock.
-
Dynamic-loading builds are relocatable.
-
Dart: the native loader downloads and caches the library again on a cold cache. It only read the versioned cache and then threw a
StateError, even thoughnativeDownloadAndCacheLibrary()was defined and exported for exactly that case. The loader also now searches for the_dart-suffixed cdylib that is actually built, opens every candidate by absolute path (a hardened runtime rejects a relativedlopen), and names the real environment variable in its error message instead of printing the identifier$nativeLibDirEnvliterally. Fixed upstream in alef 0.55.6.Behavior change: an unresolvable native now throws a descriptive
StateErrornaming the asset URL and the download command, where it previously returnednulland let flutter_rust_bridge attempt its own relative-pathdlopen.
[1.14.3] - 2026-08-05
Section titled “[1.14.3] - 2026-08-05”- Windows parser binaries build. The
UTF8PROC_STATICfix in 1.14.2 covered the two static-link paths but not the third one, which builds each grammar’s shared library from a raw compiler invocation and compilesutf8proc.cstraight into it. MSVC therefore still rejected all 183 scanner grammars witherror C2491, and the 1.14.2 release produced nowindows-x86_64orwindows-aarch64parser binaries (#174).
- CI builds the parser binaries on
windows-x86_64andwindows-aarch64on every push, mirroring the publish job. Windows previously ran on no CI runner, so the dynamic-link build was first attempted at release time; the build log is also written to a file and uploaded, because the failing step exceeded GitHub’s per-step log archive cap and left no recoverable diagnostic. - The validate job installs the Go, Ruby, Dart and Elixir toolchains, so
poly’s whole-project lint phase — golangci-lint, rubocop, steep, dart-analyze and credo — runs in CI rather than only in the git hooks.
[1.14.2] - 2026-08-05
Section titled “[1.14.2] - 2026-08-05”- Windows artifacts build again.
utf8proc.cis compiled into a static archive, butUTF8PROC_STATICwas never defined, so on MSVCutf8proc.hdeclared everyutf8proc_*symbol__declspec(dllimport)and the same translation unit then defined it —error C2491. Every Windows job in the 1.14.1 release (parser binaries, C FFI, CLI, C# native) failed, which is why nowindows-x86_64entry ever reached the pre-built parser manifest (#174). - C++ grammar scanners build under musl. The deterministic wide-ctype shim redirects the libc
classifiers through function-like macros. Preprocessing is token-based, so a scanner calling
std::iswspace(c)expanded to astd::ts_pack_iswspacethat does not exist, and heavy libstdc++ headers re-declaring those classifiers after the macros were in scope failed to compile on musl. The redirected names are now visible innamespace std, andnorg— whose scanner pulls in<cwctype>,<locale>and<regex>— is excluded from the shim entirely, so it falls back to libc consistently. This unblocks the all-grammar Docker image and the Docker publish. - The WASM build no longer emits an unresolved
envimport. Most tree-sitter scanners include<wchar.h>themselves, and wasi-libc’s copy unconditionally redefinesiswdigitand friends as trampoline macros, silently clobbering the shim’s redirects and leaving a reference to a symbol that does not exist onwasm32. It surfaced asCannot find module 'env'from the wasm-bindgen glue. The shim now burns that include guard up front on__wasi__. - PHP e2e autoloads the relocated sources. The generated
composer.jsonstill pointed at the emptiedpackages/php/src, so every PHP test failed with a missing-class error. It now resolves tocrates/ts-pack-core-php. - Restored formatting of a generated Go source file and
docs-site/pnpm-workspace.yaml, and scopedpoly lintto skip whole-project linters whose toolchains CI does not install.
[1.14.1] - 2026-08-04
Section titled “[1.14.1] - 2026-08-04”- Ruby binding no longer exports generated types into the global
Objectnamespace (issue #173, theParsercollision with theparsergem); generated types stay namespaced underTreeSitterLanguagePack.
Changed
Section titled “Changed”- Vendored grammar scanners now classify characters through a deterministic utf8proc-backed
wide-ctype shim instead of libc
<wctype.h>, making parse trees (and downstream formatting) identical across macOS and glibc. Pinnedtree-sitter-clito0.26.11in CI.
[1.14.0] - 2026-08-01
Section titled “[1.14.0] - 2026-08-01”- Add 65 tree-sitter grammars, raising coverage from 306 to 371. New languages include Slint,
MoonBit, Aiken, Koka, Koto, Unison, Leo, Motoko, Eiffel, Picat, Cython, Ballerina, SAS, D2,
Vala, and Fluent, alongside DSL/schema/query grammars (YANG, FlatBuffers, Avro, Cypher, SpiceDB,
Kusto, PostgreSQL, T-SQL, WDL, SysML, and the Salesforce SOQL/SOSL/debug-log family), templating
grammars (HAML, Slim, Vento, rsHTML), and systems/config grammars (m68k, scfg, kitty,
jjdescription, bpftrace, strace, SystemTap, TRACE32). Seven permissively-licensed but unmaintained
grammars are vendored in-repo (ABNF, PlantUML, Promela, Reason, Wolfram, XQuery, Yul), and
justis re-sourced to the canonicalcasey/tree-sitter-just. - Detect kitty (
kitty.conf), rsHTML (.rs.html), and dotenv (.env) files by name. - The core library emits
tracingspans and events always-on across parsing, grammar loading, and download operations, making observability a first-class product surface.tracingis now a non-optional dependency (near-zero cost without a subscriber). Levels follow the shared contract: INFO for coarse state changes (download/init/prefetch/clean, network bundle fetch), DEBUG for per-call flow (process,get_language, grammar shared-library loads). Span names and field keys are part of the public API.
Changed
Section titled “Changed”- The
ts-packCLI installs atracingsubscriber for every command (previously only the MCP server did), soRUST_LOGsurfaces library diagnostics on stderr for all subcommands; machine- readable result output on stdout is unchanged.tracing/tracing-subscriberare no longer gated behind themcpfeature. - Raw
println!/eprintln!/print!/eprint!/dbg!are denied in production code across the workspace (clippyprint_stdout/print_stderr/dbg_macro);tracingis the sole diagnostic surface. Thets-packCLI’s command results and prompts remain its stdout/stderr output contract (opted in crate-wide), while the MCP command’s diagnostics continue throughtracing. - Regenerate all language bindings on alef 0.49.0.
[1.13.5] - 2026-07-27
Section titled “[1.13.5] - 2026-07-27”Changed
Section titled “Changed”- Regenerate all language bindings on alef 0.48.4, which fixes the Java publish (lowered the Maven
enforcer floor) and the C# publish (generates
runtime.json.templatesoruntime.jsoncan be rendered before the NuGet pack). - Update grammar revisions to latest upstream (gnuplot, javadoc, scala, tmux).
- Upgrade Rust dependencies to their latest incompatible versions (base64 0.22 -> 0.23).
- ci: render
runtime.jsonfrom its template before packing the NuGet package so C# runtime metadata is published correctly.
[1.13.4] - 2026-07-26
Section titled “[1.13.4] - 2026-07-26”Changed
Section titled “Changed”- Regenerate all language bindings on alef 0.48.2.
- Update dependencies to their latest compatible versions.
Removed
Section titled “Removed”- Remove unused Java PMD ruleset and stale linter configuration.
[1.13.3] - 2026-07-21
Section titled “[1.13.3] - 2026-07-21”- ruby: platform-specific native gems now package one native extension per supported Ruby ABI and
load the extension matching the current runtime. This keeps precompiled gems usable on Ruby 3.2,
3.3, 3.4, 3.5, and Ruby 4 while avoiding
Parser#parseself-conversionTypeErrors from loading an extension compiled against a different Ruby ABI. Fixes #171.
Changed
Section titled “Changed”- grammars: refresh 22 grammar revisions to newer upstream commits (no languages added or removed).
- deps: refresh lock files with non-breaking updates within existing constraints (
Cargo.lock,pnpm-lock.yaml,uv.lock, RubyGemfile.lock). - Regenerated all bindings with alef 0.42.0.
[1.13.2] - 2026-07-20
Section titled “[1.13.2] - 2026-07-20”- ruby: the Magnus binding now exposes the
Parserinstance methods (set_language/parse/parse_bytes/reset). alef previously skipped&mut selfmethods on opaque types, soParserwas generated with no callable methods;parse_bytesalso now accepts a RubyString. This fixes the root cause behind #168 at the generator level (alef 0.38.4), superseding the hand-edit stopgap in #169.
Changed
Section titled “Changed”- build: regenerate all bindings against alef 0.38.4. Swift and Elixir generated output pick up
the 0.38.4 codegen fixes (Swift
CodingKeyshonoring serde renames,Optional<Vec<struct>>accessors no longer double-encoding, and the Elixir e2e streaming entry-point suffix). - ci: the Docker CI and publish workflows tolerate a full GitHub Actions cache — a cache-export failure no longer fails the build.
[1.13.1] - 2026-07-19
Section titled “[1.13.1] - 2026-07-19”- ruby: the published 1.13.0 gem still shipped
required_ruby_version = [">= 3.2.0", "< 4.0"]despite #162, because alef regeneration reverted the hand-edit. Fixed at the generator level (alef 0.38.0 defaults to">= 3.2.0"with no upper bound and makes the constraint configurable), so the gem now installs on Ruby 4.x. - elixir: positional JSON-encoded NIF args now forward
niland pre-encoded JSON strings as-is instead of double-encoding, matching the keyword-arg path (alef 0.38.0).
Changed
Section titled “Changed”- build: regenerate all bindings against alef 0.38.0. Includes an improved Dart native-library loader (env-var override, versioned user cache, explicit errors).
[1.13.0] - 2026-07-19
Section titled “[1.13.0] - 2026-07-19”- ruby: expose the parser API methods (
Parser#parse, tree/node accessors) on the Ruby binding so consumers can drive parsing directly instead of only fetching languages. Fixes #166. - ruby: support installing on Ruby 4. Removed the
< 4.0RubyGems constraint; the native extension builds and the generated Ruby e2e suite passes on Ruby 4.0.6. Fixes #162. - ci: run the Ruby package and Ruby e2e tests on Ruby 4.0 in addition to Ruby 3.4.
- dart: pin
freezedto the stable^3.2.5constraint instead of a^4.0.0-devprerelease. The prerelease was rejected byflutter_rust_bridge_codegen’s dependency validator, which broke the Dart binding build. - node: build and publish the
linux-x64-muslandlinux-arm64-muslplatform sub-packages. CI now cross-compiles both musl targets (via zig /cargo-zigbuild), and the main package’soptionalDependenciesreference the real version instead of the0.0.1placeholder, so the native binaries resolve on Alpine/musl systems. - elixir: generate typed e2e configs. Fixes #167.
- ci: the sdist smoke test uses the
tree_sitterproperty API (root_node/type).
Changed
Section titled “Changed”- grammars: refresh 39 grammar revisions to newer upstream commits (no languages added or removed).
- deps: bump
rmcpto 2.2,go-tree-sitterto v0.25.0,@napi-rs/clito ^3.7.3,@types/nodeto ^26.0.0, andvitestto ^4.1.10. - Regenerated all bindings with alef 0.37.0.
- docs: migrate the documentation site to Astro Starlight on the shared
@xberg-io/docs-theme, and rewrite the landing page and README to a value-first voice.
[1.12.5] - 2026-07-07
Section titled “[1.12.5] - 2026-07-07”- build:
build.rsno longer forces spurious rebuilds when the crate is consumed from a registry (crates.io) rather than a git checkout. It skipscargo:rerun-if-changedfor thequery-overlaysdirectory when it does not exist, skips rerun triggers for parser directories that live underOUT_DIR, and falls back toCARGO_MANIFEST_DIRwhen the overlay-directory search walks past the filesystem root. Previously these emitted rerun triggers on nonexistent or generated paths, so cargo rebuilt the crate on every invocation. Fixes #159.
[1.12.3] - 2026-07-02
Section titled “[1.12.3] - 2026-07-02”- python:
get_parser()now returns the installedtree_sitter.Parserinstead of a vendored parser class. On Python 3.14 the vendored parser produced unusable trees — nodes exposed notype/childrenandparse(bytes)raisedTypeError— while nothing failed at construction, so the breakage was silent.get_parser(name)now buildstree_sitter.Parser(get_language(name)), mirroringget_language()and working across Python versions. Fixes #157.
Changed
Section titled “Changed”- deps: upgrade dependencies to their latest versions across every language binding (Rust, Python,
Node, Ruby, PHP, Go, Java, C#, Elixir, WASM, Dart, Swift, Zig, Kotlin-Android) and refresh grammar
revisions and GitHub Actions pins. On
wasm32,getrandommoves to 0.4 with thewasm_jsfeature. - Regenerated all bindings with alef 0.30.10, which corrects the generated Python
get_parserreturn annotation (previously an invalid_rust.Parserthat broke import on Python < 3.14).
- wasm: skip the
tmuxgrammar onwasm32. Its generatedparser.cis ~30MB — underTSLP_WASM_MAX_PARSER_BYTES(40MB) so the size gate doesn’t catch it, but it still overruns the clang wasm backend during codegen. Added toDEFAULT_WASM_SKIP_GRAMMARSso the wasm build is clean; it degrades gracefully (absent fromSTATIC_LANGUAGES). - wasm: enable the
getrandomjsfeature forwasm32-unknown-unknown. Build-dependencies (ureq) transitively depend ongetrandom, which requires thejsfeature on that target; without it wasm32 builds failed with “the wasm*-unknown-unknown targets are not supported by default”. - build:
build.rsnow reportsfailed_languages.txtwrite errors separately (viaeprintln) and always surfaces the underlying grammar compilation error even when the write fails.
[1.12.0] - 2026-06-29
Section titled “[1.12.0] - 2026-06-29”- prefetch:
prefetch(&[&str])downloads (if needed) and loads every requested grammar in one pass, so a subsequent parallel workload only parses. It probes real on-disk loadability rather thanhas_language, fixing a short-circuit where known-but-not-downloaded grammars were skipped. - query cache (Rust):
get_query(language, QueryKind) -> Result<Option<Arc<Query>>>compiles a bundled.scmquery once and caches theArc<Query>process-wide (negative results cached too). Rust-only — bindings continue to expose the raw query-string accessors. - indents & folds queries:
get_indents_query/get_folds_querynow expose the bundledindents.scm/folds.scm(harvested from the grammar bundle alongside the existing kinds);QueryKindgainsIndentsandFolds.
Changed
Section titled “Changed”- performance:
get_languagenow takes a lock-free fast path for statically-compiled and already-loaded dynamic grammars; the global load mutex guards only the not-yet-loaded dynamic library path. Removes per-call mutex contention on the hot parse path.
[1.11.1] - 2026-06-29
Section titled “[1.11.1] - 2026-06-29”- python:
get_language()(and other download failures) now surface the public exception type in tracebacks —tree_sitter_language_pack.DownloadErrorinstead of_native.DownloadError(#147). Regenerated against alef 0.30.1. - java: generate
ByteArraySerializer.javaso the generatedObjectMappercompiles for every package (alef 0.30.1). - docs: correct stale “300+” language counts to “306”.
[1.11.0] - 2026-06-27
Section titled “[1.11.0] - 2026-06-27”Stable release promoting 1.11.0-rc.2 (fully published). Version synced across all manifests.
- CI resource exhaustion: reduce parser generation concurrency. The “Clone vendors” step was
invoking
tree-sitter generatewith default concurrency of 3, causing each of the 306 grammars to generate in parallel (~1 GB RSS per instance), exhausting the 7 GB RAM limit on GitHub-hosted runners and triggering SIGTERM (exit 143) at ~13 minutes. Reduced defaults to 8 clone concurrency (from 16) and 2 generate concurrency (from 3) to stay within resource budgets. Fixes CI, CLI, Docker, E2E, Swift, Rust, and Validate workflow failures.
[1.11.0-rc.2] - 2026-06-27
Section titled “[1.11.0-rc.2] - 2026-06-27”[1.10.9] - 2026-06-24
Section titled “[1.10.9] - 2026-06-24”- Kotlin/JVM:
Tree.walk()(and other handle-returning calls) no longer crash the JVM. Opaque handle types (Tree,Node,TreeCursor) crossed the JNI boundary asString/JSON while the Rust shim returned a rawjlong, so the JVM dereferenced a primitive as an object reference and faulted withEXCEPTION_ACCESS_VIOLATION. Regenerated with alef 0.27.1, the kotlin-android bridge now returns primitiveLonghandles (required and optional, via a0Lsentinel) and constructs the wrapper directly. Fixes #146. - Python: exported exception classes are now catchable.
get_language("unknown")raised_native.DownloadError, a different class object than theDownloadErrorexported from the package, soexcept DownloadError:never caught it. Regenerated with alef 0.27.1, the native variants derive from the native baseErrorand the package re-exports the native classes (with matching type stubs), soexcept DownloadError:/except Error:work. Fixes #147.
[1.10.8] - 2026-06-24
Section titled “[1.10.8] - 2026-06-24”-
wasm32 builds no longer OOM on pathologically large grammars. Compiling the bundled grammars to
wasm32previously included everyparser.c, but a few are huge generated sources (e.g.ablat ~130 MB) that need 18-25 GB+ of clang RAM at any optimization level — a single one OOMs standard ≤16 GB CI runners (serialization viaCARGO_BUILD_JOBS=1cannot help when one file alone exceeds the budget).build.rsnow skips any grammar whoseparser.cexceeds a size limit on wasm32 (default 40 MB, configurable viaTSLP_WASM_MAX_PARSER_BYTES;0disables the gate), emitting acargo:warningper skipped grammar plus a summary. Skipped grammars are absent fromSTATIC_LANGUAGES(no dangling FFI symbol) and degrade gracefully at runtime. The 40 MB default keeps every common language (including the ~40 MBsqlgrammar) and excludes only the handful of unbuildable outliers (abl,systemverilog,razor,fsharp,verilog,gnuplot,latex). (crates/ts-pack-core/build.rs) -
Swift publish now creates the
release/swift/<version>branch carrying the substituted XCFramework checksum. The alef-generated Swift e2e/test-app pins.package(url: …, branch: "release/swift/<version>")(the non-destructive layout shared with the other polyglot repos), but the publish workflow only force-moved thev<version>tag and never created that branch — so SwiftPM could not resolve the package and the Swift test-app failed with an emptyTreeSitterLanguagePacktarget. The checksum commit is now also pushed torefs/heads/release/swift/<version>. (.github/workflows/publish.yaml)
[1.10.4] - 2026-06-22
Section titled “[1.10.4] - 2026-06-22”ts-pack mcpserver now exposes MCP resources, prompts, and argument completions in addition to its tools. Resources serve the language catalog (ts-pack://languages,ts-pack://languages/downloaded) and a per-language template (ts-pack://language/{name}); a ready-madeanalyze-codeprompt drives a structure/imports/symbols analysis workflow; and language-name arguments autocomplete against the available-language catalog.
Changed
Section titled “Changed”ts-pack mcptools are now fully aligned with the CLI and carry accurate rmcp annotations. Thedownloadtool takesgroups(multiple) andfreshlikets-pack download;processgains theallflag; the combinedcachetool is split intocache_dir(read-only) andclean_cache(destructive), mirroring the CLI. Every tool now declares correctopen_world_hint,read_only_hint,destructive_hint, andidempotent_hintvalues.- The CLI ships the MCP server by default.
mcpis now a default feature ofts-pack-cli, sots-pack mcpis present in every distribution —cargo install ts-pack-cli, the prebuilt release binary, Homebrew, and the@kreuzberg/ts-pack-cli/ts-pack-clinpx/uvx proxies. Previously the feature was opt-in and absent from shipped binaries, which also broke the marketplace plugin launcher that invokests-pack mcp.
- Host-native
get_language()passthrough now works for Swift, Kotlin-Android, and Java. The capsule passthrough (#143) returns the ecosystem’s nativeLanguage, but the bindings did not wire the host-runtime dependency: Swift generated an uncompilable forwarder (wrong return type, missingimport SwiftTreeSitter); Kotlin-Android declaredktreesitterasimplementation, hiding theLanguagetype from callers’ compile classpath; and Java’sjtreesitter(Panama FFM) dlopens the standalonelibtree-sitterruntime, which CI and the test harness did not provision. Fixed via alef 0.26.1 (Swift/Kotlin codegen) pluslibtree-sitterprovisioning in CI and the Java test harness. Zig already wired itstree_sittermodule correctly. .app.srcfiles now map to Erlang. The application-resource template is Erlang term syntax, but single-extension lookup only sawsrc. A compound-extension table now resolves*.app.srcto the Erlang grammar.
[1.10.3] - 2026-06-22
Section titled “[1.10.3] - 2026-06-22”Changed
Section titled “Changed”- Regenerated all bindings with alef 0.26.0. Picks up alef’s sync-versions byte-stability fix:
sync-versionsno longer rewrites externally-formatted scaffold/manifest files (this repo formats via external tools with[workspace.format] enabled = false), and it preserves external SwiftPM dependency pins instead of clobbering them with the workspace version. This clears the CI version-sync freshness gate. - Updated dependencies within their current major versions (Rust crates, PHP dev tooling, pnpm toolchain pin).
[1.10.2] - 2026-06-22
Section titled “[1.10.2] - 2026-06-22”- Generated binding doc comments no longer emit Rust intra-doc link syntax. alef copied core
rustdoc comments verbatim into the per-language binding crates, carrying
[Type]/[fn](crate::fn)intra-doc links that resolve in the core crate but breakcargo docin the binding crates withrustdoc::broken-intra-doc-links. The references are now de-linked to plain code spans (`Type`) during emission, preserving genuine URL/anchor Markdown links. Picked up from the alef 0.25.60 regen.
[1.10.1] - 2026-06-20
Section titled “[1.10.1] - 2026-06-20”- Java: fixed a JVM crash (
EXCEPTION_ACCESS_VIOLATION) when traversing a parsed tree via opaque handles (#146).Parser.parse,Tree.walk,Tree.rootNode,Node.parent/child, andTreeCursor.nodefreed the returned native handle in afinallyblock immediately after wrapping it, so the returnedTree/Node/TreeCursorreferenced already-freed memory and the next native call dereferenced it and crashed. The wrapper now owns the handle and frees it once onclose(). Fixed in the alef Java backend and picked up by the 0.25.55 regen; value/DTO returns (byteRange/startPosition/process) still correctly free the FFI temporary after reading it.
Changed
Section titled “Changed”- chore(precommit,alef): standardize kotlin-android formatting on ktfmt –kotlinlang-style. Drop the conflicting prek ktlint hook, scope detekt/ktfmt to
packages/kotlin-android, add--kotlinlang-styleto ktfmt, switchalef.tomlkotlin format/check from gradle-ktlintFormat to ktfmt so alef and prek agree, and exclude the vendored Gradle wrapper from shellcheck. detekt remains for static analysis. (.pre-commit-config.yaml,alef.toml)
- Host-native
Languagepassthrough across the C-ABI binding family (#143).get_languagenow returns each ecosystem’s native tree-sitterLanguageinstead of an opaque alef handle, so the result drops straight into the host runtime’s parser: Go (*tree_sitter.Languageviago-tree-sitter), Zig (?*const tree_sitter.Languageviazig-tree-sitter), Java (jtreesitter.Language), C# (TreeSitter.Language), Kotlin Android (ktreesitter.Language), and Swift (SwiftTreeSitter.Language) — joining the existing Python and Node passthrough. Each binding gained a dependency on its host tree-sitter runtime, injected into the generated manifest. Configured via[crates.*.capsule_types.Language]inalef.toml; regenerated against alef 0.25.55.
Changed
Section titled “Changed”- Regenerated all bindings against alef 0.25.55. The C FFI crate now takes a direct
tree-sitterdependency so the capsule shim can nametree_sitter::ffi::TSLanguage(the pointee it castsvalue.into_raw()to), and the zigbuild.zig.zoncarries the resolvedzig-tree-sittercontent hash.
[1.9.1] - 2026-06-18
Section titled “[1.9.1] - 2026-06-18”- Swift: restored the public
getLanguage(name:)function in theTreeSitterLanguagePackmodule. An alef 0.25.38 codegen regression added opaque types to the Swift forwarder exclusion set, droppingget_language(the only free function returning the opaqueLanguagetype) from the generated public API in v1.9.0. Regenerated against alef 0.25.43.
[1.9.0] - 2026-06-18
Section titled “[1.9.0] - 2026-06-18”Changed
Section titled “Changed”- Bumped
alefpin 0.25.28 → 0.25.38 and regenerated all bindings. Picks up alef 0.25.29–0.25.38: enum associated (static factory) methods surfaced across backends, the swift opaque no-op shim so$_freeis synthesised for handle types with no visible methods (e.g.Language), swift streaming-owneralready_declaredre-declaration, javamarshal_optional_bytestemplate registration, and java@Nullabletype-use placement on qualified types. - Upgraded all dependencies to their latest versions (cross-major). Ran
task upgradeacross every language workspace; lock files regenerated and committed. - Repo hygiene. Ignore the machine-local
packages/kotlin-android/.gradle/cache and the.basemind/index (untracking the accidentally-committed Gradle cache files), and exclude the deterministic.ai-rulez/.generated-manifest.jsonfrom theoxfmtpre-commit hook so it no longer fights theai-rulez-generatehook.
- Java: dropped throwing
UnsupportedOperationExceptionstubs forSelf-returning DTO/enum methods. There is no JNI/FFM symbol for DTO methods yet, so the throwing stubs compiled but misled callers and broke any path that reached them. The Java backend now skips these methods until marshaling lands. - Java: restored the
truedefault for boxed@Nullable Boolean#[serde(default)]record fields. A non-optional#[serde(default)] bool = truefield is boxed to@Nullable Boolean, so JSON that omitted it deserialised tonulland the accessor returnednullinstead oftrue.
[1.9.0-rc.55] - 2026-06-17
Section titled “[1.9.0-rc.55] - 2026-06-17”Changed
Section titled “Changed”- Bumped
alefpin 0.25.24 → 0.25.28. Regenerated all bindings viatask alef:generate. Picks up alef 0.25.25–0.25.28: scaffoldexcluded_default_featuresfor dart/swift wrappers, publish/vendor retry on crates.io registry-index propagation lag, e2e/codegen wasm[crates.e2e.env]block, e2e/codegen php PIE invocation syntax for v1.4.5, backends/swiftRustBridgeCimport + Vec skip on already-declared types, docs heading demotion, e2e/codegen typescriptSsrfPolicy.denyPrivate=falsefor WASM e2e, backends/ffi shared extractor → FFI same-name fn dedup, and backends/dart FRB primitive bridge return-value type cast restoration (.map(|v| v as i64)regression that blocked rc.55 regen).
- Elixir Hex install OTP 27.2 TLS
key_usage_mismatchagainstbuilds.hex.pm. Switched test-elixir jobs inci.yamlandci-e2e.yamltoxberg-io/actions/setup-elixir@v1wrapper which routes throughcdn.hex.pmto bypass OTP 27.2 TLS cert-chain rejection againstbuilds.hex.pm. ci.yamltest- jobs 404 race onparsers.json.* Mirroredci-e2e.yaml’sbuild-e2e-bundlesjob intoci.yamland addedTREE_SITTER_LANGUAGE_PACK_MANIFEST_URLmanifest wiring to all test-* jobs (test-python, test-node, test-wasm, test-go, test-java, test-csharp, test-ruby, test-php, test-elixir, test-c-ffi). Pre-publish, the workspace version has no GitHub Release yet, so the runtime’s network fetch ofparsers.jsonwould 404; bundling parsers locally and exporting the manifest URL avoids the race.
[1.9.0-rc.54] - 2026-06-17
Section titled “[1.9.0-rc.54] - 2026-06-17”Changed
Section titled “Changed”- Bumped
alefpin 0.25.20 → 0.25.24. Regenerated all bindings viatask alef:generate. Picks up alef 0.25.21–0.25.24: dart e2e setEnv robustness, swiftalready_declaredopaque-handle class triples, java PMD/palantir-java-format compliance, C FFI e2e download_ffi.sh derives FFI_PKG_NAME fromlib_name(was hardcoded), kotlin-android per-file ktfmt invocation, plus the rc.53 → rc.54 structural fixes below.
- Java loader RID alignment (rc.53 regression).
NativeLib.resolveNativesRid()previously emittedosx-aarch64/linux-aarch64(a JNA/LWJGL-style convention) while the published JAR’snatives/<rid>/…directory is named viago_java_platform()(macos-arm64,linux-aarch64,windows-x86_64). Result: every macOS-arm64 client failed withUnsatisfiedLinkErrorbecausenatives/osx-aarch64/libts_pack_core_ffi.dylibdoes not exist (it’s atnatives/macos-arm64/). Loader template now matchesgo_java_platform()naming. - Elixir download NIFs unregistered in precompiled binary (rc.53 regression). The rustler NIF crate
Cargo.tomlhad no[features]table — only a[lints.rust] check-cfgreference todownload. Default precompiled CI builds therefore stripped the download/cache/init/configure NIFs from the cdylib, producing:nif_not_loadederrors on everyTreeSitterLanguagePack.Native.download/*,cache_dir/0,clean_cache/0,init/0,configure/1,downloaded_languages/0call (10/450 errors at rc.53). Template now emits canonical[features] default = ["config", "download", "serde"]block forwarding to the core crate, mirroring the magnus fix from alef 0.25.19. - Node vitest first-load timeouts.
smoke_devicetreeandsmoke_ocamllexexceeded the default 30 s test timeout on first load. RaisedtestTimeoutto 60 s,hookTimeoutto 120 s. - C FFI E2E 404 race in
ci-e2e.yaml.e2e/c/download_ffi.shpinned the FFI tarball URL to the current workspace version; on main pushes before the matching tag was created, the curl 404’d because the GitHub Release for that version didn’t exist yet. Script now honoursALEF_FFI_LOCAL_DIRenv override to skip the network fetch and consume pre-staged headers/libs.ci-e2e.yaml/test-c-ffiis nowneeds: build-ffiand stages the locally-built artifact via the override.
[1.9.0-rc.53] - 2026-06-16
Section titled “[1.9.0-rc.53] - 2026-06-16”Changed
Section titled “Changed”- Bumped
alefpin 0.25.18 → 0.25.20. Regenerated all bindings viatask alef:generate. Picks up:- 0.25.19 — magnus binding
Cargo.toml[features]block (fixes rc.52 Ruby gem build under-D warnings), elixir NIFCargo.toml[lints.rust]ordering (fixes CICheck version sync), ruby Rakefile yard-coverage hook, FFI opaque-pointer call-site.clone()for service-API codegen,binding_excludedfield fallthrough that preserves bespoke coreDefault::default()semantics, csharp e2e csproj arm64 RID branching. - 0.25.20 — dart loader absolutize defensive improvement, zig opaque method error decoding (
_first_error→_error_with_message),language_pages.rsmodularization under 1000-LOC cap.
- 0.25.19 — magnus binding
- Ruby gem publish (rc.52 regression). All four
Build Ruby gemmatrix jobs failed at rc.52 — the magnus binding’sCargo.tomllacked the[features]table forwardingdownloadto the core crate, so 18×#[cfg(feature = "download")]arms triggerederror: unexpected cfg condition value: downloadunder-D warnings. The skippedPublish Ruby gemsstep meant rc.52 never reachedrubygems.org(test-apps:ruby failed withCould not find gem 'tree_sitter_language_pack ~> 1.9.0.pre.rc.52'). Picked up via alef 0.25.19. - CI
Check version syncred onmain. The elixir NIFCargo.tomlemitted[lints.rust]before[dependencies]; consumers’prek run --all-filesruns cargo-sort which reorders the block to the file end, producing a perpetual diff. The CI version-sync step does NOT run cargo-sort, so it reported “Versions are out of sync” on every release tag. Picked up via alef 0.25.19. - Dart publish pipeline native staging (rc.52 regression). The
assemble-dart-packagejob in.github/workflows/publish.yamluseddownload-artifact@v8withmerge-multiple: true, flattening everydart-native-<rid>artifact’s contents directly underdart-natives/. The subsequent RID inference (basename "$(dirname "$f")") then resolved to the literal stringdart-nativesfor every file, causing all four native libraries to be skipped withWarning: unrecognized rid 'dart-natives'. The published rc.52 pub.dev tarball contained nolib/src/native/<rid>/directory; the FRB loader fell through to the default relative-path dlopen which macOS hardened-runtime rejected with “relative path not allowed in hardened program”. Fix: dropmerge-multiple: trueso each artifact extracts to its owndart-natives/dart-native-<rid>/directory, and derive the RID by stripping thedart-native-prefix from the artifact directory name.
[1.9.0-rc.52] - 2026-06-16
Section titled “[1.9.0-rc.52] - 2026-06-16”Changed
Section titled “Changed”- Bumped
alefpin 0.25.15 → 0.25.18. Regenerated all bindings viatask alef:generate+task alef:sync. Picks up:- 0.25.16 — drop cfg propagation on enum
From-impl match arms, API reference docs improvements. - 0.25.17 — dart
unreachable_patternsallow at crate root, zig test sequencing to avoidclean_cacherace, node smoke timeout forvb. - 0.25.18 — swift cfg-gated extern blocks for
DownloadManager(#[cfg(feature = "download")]), dart absolutize env +Platform.scriptpaths in hardened runtime loader, node slow-grammar list extended (earthfile,perl), pyo3 + napi emit binding-side wrapper structs for[workspace.opaque_types]entries withoutcapsule_typesoverride, napi bindingCargo.toml[features]block (default = ["download"]), cleanup detection tightened from looseby alefsubstring to specificauto-generated by alef, cbindgenautogen_warningupdated so generated C headers are correctly identified as cbindgen-owned.
- 0.25.16 — drop cfg propagation on enum
- Python
get_language/get_parserAPI consistency (#141). Both helpers now return the binding’s own native types (Language,Parser) instead of the standalonetree_sitterpackage’sLanguage, matching every other binding (Java, Go, Swift, Ruby, C#, etc.). Droppedpip_dependencies = ["tree-sitter>=0.23"]from the Python package. Breaking change for callers that relied ontree_sitter.Parser(get_language(name))— useget_parser(name)directly, or importtree_sitteras a separate dependency. - Node
getLanguage/getParserAPI consistency. Same shape change as Python —getLanguagenow returns the nativeLanguageclass from@kreuzberg/tree-sitter-language-packrather than passing through to the upstreamtree-sitternpm package. Dropped thetree-sitterdevDependency from the e2e harness. - Swift
DownloadManagerbuild error. swift-bridge proc-macro previously failed withno type named 'DownloadManager' in module 'RustBridge'(and 9 related missing-member errors) because cfg-gated extern blocks were emitted unconditionally. Now wrapped in#[cfg(feature = "download")]so disabled-feature compile correctly elides the bridge surface. - Dart hardened-runtime dlopen failures. All 9 dart e2e tests previously failed at
setUpAllwith “relative path not allowed in hardened program”. Loader now absolutizes env-var andPlatform.script-derived paths before constructing search roots. - Node smoke timeouts on
earthfileandperl. Tree-sitter grammars with heavy scanner.c logic now receive the 90000ms slow-grammar timeout (previously onlyvbwas covered).
Removed
Section titled “Removed”- Python: standalone
tree-sitterpackage dependency. No longer required bytree-sitter-language-pack. Install it separately if you need the upstream API. - Node:
e2e/node/tests/capsule_passthrough.test.tsandtree-sitterdevDependency. Obsolete now that nodegetLanguagereturns the native type.
[1.9.0-rc.51] - 2026-06-15
Section titled “[1.9.0-rc.51] - 2026-06-15”Changed
Section titled “Changed”- Bumped
alefpin 0.25.14 → 0.25.15. Regenerated all bindings viatask alef:sync+task alef:generate. Picks up the swift cfg-postprocess revert:- 0.25.15 — revert(swift): drop cfg-union postprocessing passes (c89926d5e, 4313b6e1d). The wrapper-type and function cfg-union propagation passes introduced in 0.25.12/0.25.13 caused downstream binding regressions; reverted in favour of the 0.25.14 default-features approach in the swift binding Cargo.toml.
- Bumped tslp
1.9.0-rc.50→1.9.0-rc.51propagated viatask alef:sync.
[1.9.0-rc.50] - 2026-06-15
Section titled “[1.9.0-rc.50] - 2026-06-15”Changed
Section titled “Changed”- Bumped
alefpin 0.25.11 → 0.25.14. Regenerated all bindings viatask alef:sync+task alef:generate. Picks up the accumulated fixes:- 0.25.12 — (java): bind
${classifier}property to maven-jar-plugin config so native JARs are emitted with the correct classifier. Resolves rc.49 Maven Central regression wheretree-sitter-language-pack-java-1.9.0-rc.49.jarwas published without classifier (missing/natives/{rid}/libts_pack_core_ffi.dylib), causingUnsatisfiedLinkErrorat JNI init. - 0.25.12 — (dart): copy
.frameworkdirectories recursively in publish workflow assemble step. Thefindpredicate matched only*.so/*.dylib/*.dllfiles and skipped macOS.framework/bundles; pub.dev rc.49 was missingtree_sitter_language_pack_dart.framework/. - 0.25.12 — (php): always stage PIE extension as
.soon Unix. PIE 1.4.5 probes for.soon all Unix platforms including macOS; the previous OS-branching produced rc.49 prebuilt archives missing the.so(PIE extracted source but found no binary). - 0.25.12 — Swift wrapper-type cfg union postprocess. Wrapper structs whose fields reference cfg-gated upstream types now inherit the union cfg gate, fixing swift-bridge-build
Type must be declared with 'type X'panics. - 0.25.13 — Swift function cfg union postprocess. Free helper functions taking cfg-gated wrapper struct references now inherit the union cfg gate, complementing the wrapper-type fix.
- 0.25.14 — Swift binding Cargo.toml lists every forwarded cfg-feature in
default = [...]. Preventserror[E0425]: cannot find type 'DownloadManager' in this scopeon regen consumers when the wrapper struct is cfg-gated but free helper functions referencing it are not — the binding’s default profile now matches what its core dep already pulls in viafeatures = [..., "download"].
- 0.25.12 — (java): bind
- Bumped tslp
1.9.0-rc.49→1.9.0-rc.50propagated viatask alef:sync.
[1.9.0-rc.49] - 2026-06-15
Section titled “[1.9.0-rc.49] - 2026-06-15”Changed
Section titled “Changed”- Bumped
alefpin 0.25.9 → 0.25.11. Regenerated all bindings viatask alef:sync+task alef:generate. Picks up the accumulated 0.25.10 and 0.25.11 fix series:- 0.25.10 —
publish preparecanonicalize bug.publish preparewas canonicalizing onlymanifest_dir.join("Cargo.toml"), notmanifest_diritself. Withcurrent_dir(manifest_dir)set on the cargo subprocess, the--manifest-path ./packages/elixir/.../Cargo.tomlargument resolved relative to the new cwd — effectively doubling the path — and cargo bailed withmanifest path '...' does not existfollowed by the misleading “publish core first” hint. Every source-build binding in rc.48 hit this (Python sdist, Ruby gem, Elixir NIF, PHP PIE). 0.25.10 canonicalizesmanifest_diritself at the top of the regenerate branch andmanifest_absinrewrite_binding_path_deps. - 0.25.10 — kotlin-android
copyHostJnialways reads workspace target. Drop the configuration-timeif (workspaceTarget.exists())selector that evaluated beforecargo buildfinished, eliminating theUnsatisfiedLinkErrorcascade at static-init time on every JNI-loading test class. - 0.25.10 — R extendr enum path resolution.
gen_from_binding_to_core/gen_from_core_to_bindingnow useresolve_type_pathagainst abuild_type_path_lookup(api)map instead ofcore_enum_path_remapped, fixing E0433cannot find ImageOutputFormat in crate 'kreuzberg'for enums defined outside the crate root. - 0.25.10 — Swift
From<core>arms cfg-gate variants.emit_enum_wrappernow prepends#[cfg(...)]before each rendered arm so the match remains valid when the binding crate’s feature set drops upstream variants (iOS / Android cross-targets). - 0.25.10 — C# e2e csproj emits
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>. Closes the CS0579 duplicate-attribute path for consumer e2e directories that carry a hand-checked-inProperties/AssemblyInfo.cs. - 0.25.10 — Visitor result routes bare strings to
Customwhen multiple string-payload variants exist. Fixes silent fallback to the default variant when an enum has bothCustom(String)andError(String). - 0.25.10 — FFI visitor context emits enum-typed fields as
i32discriminant. Closes theArrayIndexOutOfBoundsExceptioncascade inVisitorBridge.decodeContextcaused by reading the low 4 bytes oftag_namepointer when the C struct omitted the enum field. - 0.25.10 — Dart cfg-extraction whitespace bug and check-cfg allow-list.
extract_feature_names_from_cfgnow normalizes whitespace + handles theany(test, feature = "X")sibling form; check-cfg allow-list populates from everyEnumDef.variants[*].cfginstead of falling back to the single-entrycfg(frb_expand)form. - 0.25.10 — Release task:
task set-versionhandles prerelease versions when updatingALEF_REV+ Ruby Rakefile template documentsGEMSPECconstant for YARD coverage. - 0.25.11 — README generation supports named non-language targets.
[crates.readme.targets.<name>]renders additional template-backed README outputs alongside per-language READMEs. - 0.25.11 — Option B cfg forwarding for Dart and Swift binding crates. Each cfg feature name referenced by any IR type/field/variant/function is now emitted as
{name} = ["{core_dep_key}/{name}"]in[features], making the feature resolvable at the binding level and eliminatingunexpected_cfgswithout an allow-list. Shared collection logic extracted tosrc/codegen/cfg.rs. WASM backend now delegates to it. Swiftemit_enum_wrapperemits a_ => unreachable!()catch-all whenever any variant in the primary list carries a#[cfg(...)]gate. - 0.25.11 — Generated Homebrew test apps trust third-party taps before installing formulae.
run_tests.shnow callsbrew trust "$TAP" || truebeforebrew bundle install. - 0.25.11 — Dart test_app run no longer invokes
download_libs. Natives ship inside the pub.dev package; the FRB loader resolves them fromlib/src/native/<rid>/. Drops the structural HTTP 404 againstreleases/download/v.../tree-sitter-language-pack-dart-...that masked publish-pipeline failures. - 0.25.11 — C e2e Makefile: always re-download
ts_pack.h. Drops theHEADER_PATH/LIB_PATHshort-circuit that elided the header dependency whenever a stale prior-rc header was on disk. Per-version markerffi/.alef-ffi-versionkeeps unchanged trees from paying network cost. Resolves the rc.48 C test_appunknown type name 'TS_PACKDataNode'cascade. - 0.25.11 — C# scaffold: SDK-generated AssemblyInfo with explicit version stamps and full RID list. Enables
<GenerateAssemblyInfo>(dropsfalsesuppression), stamps<AssemblyVersion>/<FileVersion>as 4-component numeric (MAJOR.MINOR.PATCH.0) via newto_dotnet_assembly_versionhelper, preserves full SemVer on<InformationalVersion>, replaces conditional singular<RuntimeIdentifier>with a plural list of all six published RIDs, and pins<PlatformTarget>AnyCPU</PlatformTarget>. Resolves the rc.48 C#Version=0.0.0.0+targets a different processorcascade. - 0.25.11 — Zig
_error_with_messagedispatches to per-error-set message-prefix matchers. Replaces the unconditional_first_error(E)fallback that masked the real cause of every typed FFI failure; emits aif (E == ErrName) return _from_ffi_msg_ErrName(msg_opt);chain per declared error-set. - 0.25.11 — Rustler codegen clippy violations (type complexity, collapsible if, struct update, useless conversions) + Rustler trait-bridge parameter cloning skips no-op clones on reference types + JNI clippy lints + Swift cargo.rs api param + pyo3 async lifetime/result-handling cleanup.
- 0.25.10 —
- Bumped tslp
1.9.0-rc.48→1.9.0-rc.49propagated viatask alef:sync.
[1.9.0-rc.48] - 2026-06-15
Section titled “[1.9.0-rc.48] - 2026-06-15”Changed
Section titled “Changed”- Bumped
alefpin 0.25.6 → 0.25.9. Regenerated all bindings viaalef sync-versions --skip-swift-checksum+task alef:generate. Picks up the accumulated 0.25.6/0.25.7/0.25.8/0.25.9 fixes:- 0.25.6 — Java codegen.
NativeLibdowncall fallback chain now emits in the single-line shape that palantir-java-format produces, so the regeneratedNativeLib.javano longer triggers a post-regen formatter rewrite (the diff that broke rc.47’sValidate Lint & Formatjob). - 0.25.6 — publish vendor
cargo --lockedparadox.alef publish preparewas runningcargo update -p <crate>with--lockedpassed viaCARGO_BUILD_LOCKED, which made cargo refuse to update the lockfile. The Python sdist + all 4 Ruby gem + all 4 Elixir NIF + all 18 PHP PIE jobs on rc.47 failed with “cannot update the lock file … because –locked was passed”. Fix:vendor.rsnow.env_remove("CARGO_BUILD_LOCKED")before both cargo invocations and drops the--lockedflag fromcargo metadata. Regression testscrub_lock_succeeds_for_non_workspace_binding_crate_with_incomplete_seedcovers the path. - 0.25.8 — Dart mirror enum cfg-strip.
emit_mirror_enumno longer propagatesvariant.cfginto the generated mirror enum body. The mirror is a DTO/wire type thatflutter_rust_bridge_codegenreferences unconditionally fromfrb_generated.rs— gating a variant out via#[cfg]left the unconditional reference dangling withE0599 no variant named 'Heif' found for enum 'ImageOutputFormat'when the binding crate didn’t declare the upstream feature. The catch-all_ => unreachable!()arm in theFrom<CoreType>impl (introduced earlier) handles runtime safety. - 0.25.9 — Dart check-cfg allow-list + mirror dead-code cleanup + publish current_dir. Resolves the v0.25.8 build regression where the 0.25.8 cfg-strip patch orphaned the
emit_variant_cfg_open/emit_variant_cfg_closehelpers, tripping-D warningson all 4 alef publish jobs (3× Build CLI + crates.io). Also widens the dart check-cfg allow-list and tightenspublishcurrent-dir handling. - alef-side accumulated fixes (released as 0.25.9 alongside the above). Direct-deps replacement for the no-op
[patch.crates-io]block alef 0.25.8 emitted in the Elixir NIFCargo.toml(cargo refused with “patch points to the same source”). Direct deps with=constraints + matchingpackage.metadata.cargo-machete.ignoredentries pinalloc-no-stdlib/alloc-stdlib/brotli-decompressortransitively. YARD doc-coverage hook fixed via a documentedGEMSPECconstant in the generatedpackages/ruby/Rakefileand a matching docstring in the in-tree stalepackages/ruby/ext/ts_pack_core_rb/src/Rakefile(the latter file is hand-maintained and not regenerated; cleanup deferred).
- 0.25.6 — Java codegen.
[1.9.0-rc.46] - 2026-06-14
Section titled “[1.9.0-rc.46] - 2026-06-14”Changed
Section titled “Changed”- Bumped
alefpin 0.25.1 → 0.25.2. Picks up two source-build publish-prepare fixes:publish preparenow strips workspace-member[[package]]entries from the seededCargo.lockbefore per-membercargo update -p. Without this strip the path-source seed entry collides with the rewritten registry-source dep andcargo metadata --lockedvalidation fails.publish preparedisambiguates the per-membercargo update -pspec by using the fullregistry+https://github.com/rust-lang/crates.io-index#NAME@VERSIONpackage id when the member version is known. Both fixes are required to unblock Ruby gem + Elixir NIF + PHP extension matrix builds on rc.46 (rc.45 failed Ruby macos-x86_64 / linux-aarch64 + Elixir linux-aarch64 / macos-x86_64 on this exact path).
[1.9.0-rc.45] - 2026-06-14
Section titled “[1.9.0-rc.45] - 2026-06-14”Changed
Section titled “Changed”- Cross-major dependency upgrade via
task upgrade. Rust + Python + Node + Java + Elixir + PHP + Ruby dep trees rebased to their latest semver-compatible heads; lockfiles re-resolved (Cargo.lock,e2e/rust/Cargo.lock,composer.lock,pnpm-lock.yaml,mix.lock,uv.lock,packages/php/composer.lock).sources/language_definitions.jsonregenerated. - Bumped
alefpin 0.25.0 → 0.25.1. Picks up theassertions.rs:227C-e2e codegen hardening (panic-on-missing-fields_c_typesrather than the silent PascalCase fallback that producedTS_PACKDatainstead ofTS_PACKDataNodein rc.43). - All cargo invocations across
.github/workflows/and.task/now pass--locked. Sweep applied in a separate commit (130627437) ahead of this regen to keep the manifest-normalisation fix isolated; this rc carries it forward. Same motivation as the actions-side v1.8.68 sweep: a broken upstream release (recentbrotli-decompressor 5.0.1) can no longer silently override the committed lockfile during CI.
[1.9.0-rc.44] - 2026-06-14
Section titled “[1.9.0-rc.44] - 2026-06-14”publish-release: normalise parser library names across platforms in theparsers.jsonmanifest generator. Linux/macOS producelibtree_sitter_<lang>.{so,dylib}, Windows producestree_sitter_<lang>.dll(nolibprefix). TheGenerate parsers.json manifeststep compared the stripped basenames as-is, so the intersection of grammar names across platforms was empty whenever the Windows archives were present — the manifest then reported every grammar as missing on Windows and refused to upload. The generator now strips thelibprefix andtree_sitter_/tree-sitter-prefix uniformly and reverses the fourc_symboloverrides (c_sharp/embedded_template/nu/vb_dotnet) so the per-platform sets agree on language identifiers.alef.toml: declareprocess_result.data → DataNodein[crates.e2e.fields_c_types]. alef’s C e2e generator falls back to<field>.to_pascal_case()when a field path is not declared, which producedTS_PACKDatainstead of the actual cbindgen-emittedTS_PACKDataNode. Adding the explicit mapping makes the regeneratede2e/c/test_data_extraction.creferenceTS_PACKDataNode*and thets_pack_data_node_*accessor family consistently. A deeper alef-side hardening of the fallback (loud error or IR-driven type lookup) is accumulated locally in../alefand pending an alef release.build.rs: allowclippy::type_complexityon the MSVC patches table. The&[(&str, &str, &[(&str, &str)])]shape introduced for crystal/sml MSVC compat trippedclippy -D warningsin CI; the table reads cleanly as a literal and isn’t worth a type alias.rust-max-linespre-commit cap: excludecrates/ts-pack-core/src/intel/data_extraction.rs. New 1322-line module added for hierarchical data extraction; remediation backlog entry.
[1.9.0-rc.43] - 2026-06-14
Section titled “[1.9.0-rc.43] - 2026-06-14”publish-release: read.tar.zstparser archives viazstandard. Python’starfile.open(path, 'r:*')only supports.gz,.bz2,.xz, and uncompressed.tar— not.tar.zst. TheGenerate parsers.json manifeststep was failing withtarfile.ReadError: not a gzip file / not a bzip2 file / not an lzma file / invalid header, blocking parsers.json + parsers-*.tar.zst upload to the release and breaking every downstream consumer at runtime withFailed to fetch manifest from .../parsers.json: http status: 404. The step nowpip install --user zstandardthen opens each archive viaZstdDecompressor().stream_reader()andtarfile.open(fileobj=..., mode='r|').
[1.9.0-rc.42] - 2026-06-14
Section titled “[1.9.0-rc.42] - 2026-06-14”Changed
Section titled “Changed”- Regenerated against released alef 0.25.0. Picks up the new
Extensiontrait surface (per-extension TOML config +transform_emitted_fileshook), Swift target-specific core dependency overrides, the zig_first_error→ contextual error fix, and the Dart hardened-runtime framework load fix. Restorescrates/ts-pack-core-ffi/{src/lib.rs,build.rs,cbindgen.toml}which a transient pre-release regen against an in-progress alef had erroneously dropped.
[1.9.0-rc.41] - 2026-06-13
Section titled “[1.9.0-rc.41] - 2026-06-13”- JNI codegen:
&[&str]core params no longer fail E0308. The JNI function/method shims emitted&namesforVec<String>slots, which coerces to&[String]but not&[&str]. Core fns declared as&[&str](e.g.download(&[&str])) failed to compile withexpected reference &[&str], found reference &Vec<String>. Alef now consults the IRvec_inner_is_refflag to materialise aVec<&str>and borrow it (&names.iter().map(|s| s.as_str()).collect::<Vec<_>>()) when the core function expects&[&str], matching the existing Dart codegen behaviour. Folded into the alef 0.24.17 release.
[1.9.0-rc.40] - 2026-06-13
Section titled “[1.9.0-rc.40] - 2026-06-13”- Removed stray
test_apps/kotlin_android/file:/tmp/directory that broke every Windows publish job. A prior regen wrote a runtime download cache (.download.lock,manifest.json) into a literal directory namedfile:because acache_dirvalue of the formfile:/tmp/…was interpreted as a relative path rather than a URI. The:is illegal in Windows paths, so everyactions/checkoutstep on Windows failed withinvalid path 'test_apps/kotlin_android/file:/tmp/.download.lock'— collapsing 21 Windows builds in the rc.39 publish run and leaving npm / PyPI / NuGet / Maven stuck at rc.38. The bad files are removed and.gitignorenow blocks the pattern (test_apps/*/file:/) alongside the existingtest_documents/file:/guard.
[1.9.0-rc.39] - 2026-06-12
Section titled “[1.9.0-rc.39] - 2026-06-12”-
Hierarchical data extraction for 17 data-format languages (#136). Set
data_extraction = trueonProcessConfigto extract a nestedDataNodetree preserving the original document’s hierarchy. Covers JSON, HJSON, JSON5, TOML, properties, Cue, HCL, HOCON, KDL, YAML, INI, EditorConfig, PO, Nginx, Caddy (key-value pairs); XML and DTD (element shape); and CSV/PSV (sequence shape). See docs/guides/intelligence.md#data-extraction. -
JNI is a first-class test-apps target for Kotlin Android host-JVM. The kotlin_android test app’s host-JVM gradle tests now satisfy
Language::Jniinalef test-apps run, enabling CI/CD verification without Android emulator. Requires alef 0.24.14+.
Changed
Section titled “Changed”- Alef pin bumped 0.24.10 → 0.24.14. Pulls in the JNI run-default split (host-JVM gradle runner replaces the
Ffi | Jnino-op), JNI return marshalling for rawString/Option<String>returns (no more JSON-encoded"\"python\""surfacing in Kotlin), Kotlin test emitterloadLibraryrespecting[crates.ffi] prefix(resolvests_pack_jniinstead of the literal crate name), and the Kotlin assertion emitter switching list-containschecks to a case-insensitivetoString().lowercase().contains(...)shape that mirrors the Java emitter.
[1.9.0-rc.32] - 2026-06-11
Section titled “[1.9.0-rc.32] - 2026-06-11”release-finalizejob guardsFinalize releaseonpreparesuccess. The job ran withif: always()and unconditionally invokedfinalize-release@v1, which errors withINPUT_TAG is requiredwheneverprepare’stagoutput is empty (cancelled or failedprepare). Result: a cancelled rc.31 surfaced as a confusingFinalize release: failureon top of the actual upstream cancellation. Nowif: needs.prepare.result == 'success'. rc.31 publish run 27214336783.- PHP
test_apps/install.shverifies extension load viaextension_loaded()rather than parsingphp -moutput. When the PIE-installed extension was already loaded through the globalphp.ini, an explicitphp -d extension=...invocation caused PHP to emitModule already loadedto stderr; the harness’s combined-output capture treated the warning as fatal and the install step exited non-zero before the actual smoke test ran. Switched tophp -r 'exit(extension_loaded("...") ? 0 : 1);'so the check is decoupled from PHP’s logging and tolerant of double-loading. ts-pack-core-ffiregen emits zero rustdoc warnings. Previously the regen produced 26 broken-intra-doc-link warnings on every build because emitted///comments contained bare and backtick-wrapped intra-doc-link forms ([download()],[`Error::LanguageNotFound`], etc.) referencing core-crate items not in scope from the FFI wrapper. Pulled in via the alef 0.24.2 bump.ts-pack-core-noderegen emits zero rustdoc warnings. The previous regen leftVec<u8>andArray<number>bare inJsBytesdoc comments, which rustdoc parsed as unclosed HTML tags. Pulled in via the alef 0.24.2 bump.test_apps/zig/build.zig.zonURLs now match publish-zig asset naming. Previous releases emitted URLs with Go-style platform labels (linux-aarch64,macos-arm64, …) while published assets used Rust target triples (aarch64-unknown-linux-gnu,aarch64-apple-darwin, …), sozig fetch404’d. The alef 0.24.2 bump switches both sides to Rust triples; tslp’salef.toml[crates.e2e.registry.packages.zig.platform_hashes]keys updated to match. Reverts the simple-arch direction taken in rc.31.
Changed
Section titled “Changed”- Alef pin bumped 0.23.68 → 0.24.2. Pulls in the FFI/NAPI rustdoc-warning fixes and Zig URL alignment above, plus a Kotlin Android host JNI artifact for JVM test_apps (
buildHostJni/copyHostJniGradle tasks guarded byalef.skipHostJni), Go scaffoldmodule_majorparameterization that lets non-kreuzberg consumers configure theirpackages/go/v{N}layout, and a broad sweep of trait-bridge adapter fixes across Kotlin Android, C#, Java, Node, R, Swift, Dart, Elixir, and Go. crates/ts-pack-core/build.rsadded to therust-max-linesexclude list (1081 LOC > 1000-line ceiling). Joins the existing remediation backlog of large files awaiting split.
[1.9.0-rc.31] - 2026-06-09
Section titled “[1.9.0-rc.31] - 2026-06-09”- Alef pin bumped 0.23.58 → 0.23.65. Pulls in two test_apps-driven fixes from alef 0.23.65:
- kotlin-android: Foojay toolchain resolver plugin bumped v0.7.0 → v0.10.0 in both
settings.gradle.ktsemitters. v0.7.0 referencedJvmVendorSpec.IBM_SEMERU, which Gradle 9.0+ removed (renamed toIBM); Gradle 9.5.1 hosts failed at project-evaluation withClass org.gradle.jvm.toolchain.JvmVendorSpec does not have member field 'IBM_SEMERU'. v0.10.0 is Gradle 9.x-safe. - zig: published tarballs now use simple-arch platform labels (
linux-x86_64,linux-aarch64,macos-arm64,macos-x86_64,windows-x86_64) matchingbuild.zig.zonURL templates. PreviouslyRustTarget::platform_for(Language::Zig)returned the rust triple, soalef publish package --lang zig --target …emitted…-aarch64-apple-darwin.tar.gzbut the e2e codegen’s URL templates and per-platform[crates.e2e.registry.packages.zig.platform_hashes]user config used the simple-arch convention. Consumers’zig fetchthen 404’d.
- kotlin-android: Foojay toolchain resolver plugin bumped v0.7.0 → v0.10.0 in both
[1.9.0-rc.30] - 2026-06-09
Section titled “[1.9.0-rc.30] - 2026-06-09”Stage Go FFI librariesstep usesgit add -f. Root.gitignoreglobally ignores*.so/*.dylib/*.dll/*.lib, so the plaingit addsilently refused to stage the downloaded FFI artifacts underpackages/go/.lib/.xargspropagated the (silent) failure as exit 123, failing the step before thepackages/go/v<version>subtree tag could be pushed. Added-fso the published Go module deliberately ships pre-built FFI artifacts past the global ignore. Fixes rc.29 publish run 27192809836 Stage Go FFI failure.upload-release-assets@v1receives the publisher-app token as an action input on all 4 cross-repo-write call sites. The shared action setsGH_TOKENinside its own composite step frominputs.token(defaultgithub.token), so a step-levelenv: GH_TOKEN: …on the calling job had no effect — uploads ran with the read-only defaultGITHUB_TOKENand hitHTTP 403: Resource not accessible by integration. Now passestoken: ${{ steps.app-token.outputs.token }}on the Go FFI, Elixir NIF, Swift bundle, and Zig upload sites. The PHP PIE upload site (line 2473) keeps the default token because its job declarespermissions: contents: write. Fixes rc.29 publish run 27192809836 Upload Go FFI 403.- Pulls in
xberg-io/actionsv1.8.49 retry-on-SSL upload fix.publish-github-release/scripts/upload_artifacts.pynow retries 5× with exponential backoff onURLError/ssl.SSLError/ConnectionError/TimeoutError/ HTTP 5xx. rc.29 parser-sources bundle upload hit a transientssl.SSLEOFErrormid-upload on a 30 MB asset and cascaded to ~15 dependent failures (skippingpublish-crates, which broke every PHP/Ruby/Elixir/Python-sdistcargo generate-lockfileagainst the unpublished workspace member); the retry absorbs the SSL race.
[1.9.0-rc.29] - 2026-06-09
Section titled “[1.9.0-rc.29] - 2026-06-09”Changed
Section titled “Changed”-
Alef pin bumped 0.23.48 → 0.23.58. Pulls in the PHP MINIT module-startup mutex (0.23.50 —
crates/ts-pack-core-php/src/lib.rsnow wires__ext_php_rs_module_startupinto the extension builder so class registrations actually reach PHP), the NAPI TS overload/optional-param signature cleanup (0.23.55), the NAPI arrow-type return type strip fix (0.23.54), the napi service-wrapper lowerCamelCase fix (0.23.57), per-item version annotations in the IR + docs generator (0.23.58), the NAPI enum variant JSDoc*/escape (0.23.58 —crates/ts-pack-core-node/index.d.tsno longer prematurely closes the JSDoc block aroundDocstringFormat::JSDoc/::JavaDocvariant docs), and a sweep of Java/Kotlin/Zig/Dart formatting normalization across all generated binding files. -
Release automation migrated to the
kreuzberg-dev-publisherGitHub App. All 15 release-write jobs in.github/workflows/publish.yaml(parser-sources upload, parser-binaries upload, Go FFI upload, C FFI upload, Elixir NIF upload + draft create, Hex checksums fetch, pubdev workflow dispatch, Swift manifest commit + tag force-push, Zig upload, CLI upload, homebrew formula render + tap push, homebrew bottle build + DSL merge + tap push, Go subtree commit + tag push, finalize-release) now mint a short-lived installation token viaactions/create-github-app-token@v2keyed off the org secretsBOT_APP_ID/BOT_APP_PRIVATE_KEY. Bot identity:kreuzberg-dev-publisher[bot](user id 291994444). Eliminates theHOMEBREW_TOKENPAT for cross-repo tap pushes and lets tag pushes trigger downstream workflows (GITHUB_TOKEN-driven pushes don’t). Branch protection onmainrequireskreuzberg-dev-publisher[bot]in the bypass list.
Stage Go FFI librariesstep in.github/workflows/publish.yamlnow resolves the artifact path correctly. The stepcds intopackages/go/before walking the downloaded artifact tree, so thefindinvocation needs../../tmp/go-ffi-all(two levels up to the repo root). Commit1de6c8dcaintroduced../../../tmp/go-ffi-all(three levels up), pointing one directory above the workspace root →find: '…/tmp/go-ffi-all': No such file or directory→ exit 1 →packages/go/v1.9.0-rc.28subtree tag never pushed. Manually staged + tagged rc.28; the next publish run picks up the fix.
[1.9.0-rc.28] - 2026-06-08
Section titled “[1.9.0-rc.28] - 2026-06-08”-
Homebrew
libts-packbottle now ships with all 306 grammars statically compiled. Thebuild-c-ffistep in.github/workflows/publish.yamlwas invokingalef publish build --lang ffiwithoutTSLP_LANGUAGES, socrates/ts-pack-core/build.rsdefaulted to zero statically-compiled grammars and the resulting FFI tarball (downloaded verbatim by the libts-pack formula) had an empty language registry. The bottle’sts_pack_available_languages()returned an empty string, breakingtest_apps/homebrew/ffi_smoke.c. Step now setsTSLP_LANGUAGESto the full language list (via the samepython3 -c "import json"extraction used by the CLI build) andTSLP_LINK_MODE=static. -
C# NuGet
TreeSitterLanguagePackpackage now bundles an FFI dylib with all 306 grammars statically compiled. Same root cause as the libts-pack fix above — thebuild-csharp-nativestep invokedbuild-csharp-natives@v1without settingTSLP_LANGUAGES. The composite action’s native cargo build inherits the calling step’s env, so addingTSLP_LANGUAGES/TSLP_LINK_MODE=static/PROJECT_ROOTon the step propagates into cargo. Fixes theLanguage 'comment' not foundfailure surfaced bytest_apps/csharpagainst rc.27. -
CommentKind::BlockandCommentKind::Docrustdoc no longer contains literal*/inside backticks. The*/sequence inside`/* ... */`code spans was landing verbatim in NAPI-RS-emitted JSDoc, prematurely closing the/** ... */block and triggering oxlintTS(1164): Computed property names are not allowed in enums. Reworded the rustdoc to avoid the*/terminator. (Alef 0.23.47 added anescape_jsdoc_block_closesanitization helper but it does not reach the napi enum variant doc path — tracked as alef 0.23.48+ follow-up.)
Changed
Section titled “Changed”- Alef pin bumped 0.23.34 → 0.23.48. Pulls in the Zig null-check primitive-return fix (0.23.47), PHP module entry explicit-name fix (0.23.47), JSDoc
*/sanitization helper (0.23.47), kotlin-android foojay-resolver plugin emission (0.23.47), Zig publish package name using Zig platform mapping (0.23.48), Zig null-guard returning canonicalerror.Serialization(0.23.47), FFI Finalize owner-pointer preservation (0.23.46), and the c download_ffi.sh asset name + zig cache clear + php pie always-install fixes (0.23.43–45).
[1.9.0-rc.27] - 2026-06-08
Section titled “[1.9.0-rc.27] - 2026-06-08”-
Publish smoke install: scope
--no-binarytotree-sitter-language-pack.pip install --no-binary :all: --no-build-isolationforced source builds for transitive deps too and pip then failedBackendUnavailable: Cannot import 'hatchling.build'because the smoke venv only pre-installed maturin + setuptools + wheel. The smoke step now scopes--no-binaryto just our package; transitives use their published wheels. -
Exclude
php8.5 / macos-arm64from the PIE matrix.shivammathur/setup-php@2.37.1cannot install PHP 8.5 on macOS arm64 — the brew arm64 bottle is not yet published, and the macOS arm64 runner images ship no pre-installed PHP. All other PHP 8.5 variants build cleanly. Re-enable when upstream catches up. -
Retry transient HTTP errors when downloading parser sources in
crates/ts-pack-core/build.rs.fetch_bytesnow retries up to 6 times with exponential backoff (2s → 64s) on any ureq error, covering both network blips and the GitHub release CDN’s intermittent 504s. Without retries, a single 504 mid-cargo publishverify-build would blow up the publish workflow (as happened on rc.26’sPublish Rust cratesjob during the 2026-06-08 GH CDN incident). -
Local-clone fallback in
crates/ts-pack-core/build.rs. When the workspaceparsers/tree is empty (gitignored on a fresh clone) and the GH release tarball forparser-sources-{version}.tar.zstisn’t published yet (rc builds during the publish workflow window), the build no longer panics on a 404. The new resolution order is: workspace populated → OUT_DIR cache →scripts/clone_vendors.py(if present, dev workspace) → GH release tarball. The local-clone path triesuv run --no-sync,uv run,python3, andpythonin turn so it works across dev environments. ExistingTSLP_OFFLINEandTSLP_SOURCE_BUNDLE_URLoverrides are unchanged.
-
get_tags_query(language: &str) -> Option<&'static str>— new public accessor incrates/ts-pack-coremirroringget_highlights_query/get_injections_query/get_locals_query. ReturnsSomefor the 15 languages with vendoredtags.scm(rust, kotlin, csharp, swift, gleam, gap, al, enforce, gdshader, roc, cfml, ql, tact, sourcepawn, mojo) andNoneotherwise. Propagated to the PyO3, NAPI, and FFI bindings via the alef codegen cascade. -
gherkingrammar. Pre-compiledtree-sitter-gherkinparser for.featurefiles. Source:SamyAB/tree-sitter-gherkinpinned at43873ee8de16476635b48d52c46f5b6407cb5c09.
- Bump alef pin
0.23.30 → 0.23.34and regen all bindings, e2e, test_apps. Pulls in 7 rolling alef fixes triaged from rc.25 test_app failures: php#[php_class]constants escape PHP-reserved variant names (CLASS_/INTERFACE_/…) avoidingFatal error: A class constant must not be called 'class'; java javadocescape_javadoc_linerewrites nested*/inside{@code …}to*/so the surrounding/** … */block isn’t closed prematurely (was breakingmvn compileonCommentKind.java+DocstringFormat.java); swiftZSwiftPluginHelpers.swiftimportsRustBridge(notRustBridgeC) soRustStringresolves; zig test_apps_run sed patterns/}, */}\n/gcorrectly splitsbuild.zig.zondep blocks sozig fetch --savepopulates.hashfields; dart flutter_rust_bridge external library loader usesAbi.current()instead ofPlatform.versionstring parsing for reliable arch detection; java e2e pom.xml antrun copy-native-lib step falls back fromffi/lib/(pre-built FFI tarball) totarget/release/(local Cargo build); php install.sh appendsextension=<name>to the loaded php.ini after PIE 1.4.5+ install (PIE’s--skip-enable-extensiondefault no longer auto-enables). (alef.toml, 482-file regen acrosspackages/,e2e/,crates/,test_apps/)
-
Omit field-level javadoc in multiline Java record declarations for PMD compliance. PMD 7.x does not recognize javadoc preceding annotations as belonging to record components (DanglingJavadoc rule). Field-level documentation is omitted from multiline record declarations since records are self-documenting value types and class-level record javadoc provides sufficient context. (Alef upstream:
src/backends/java/gen_bindings/types.rs) -
Suppress
missing_docslint in generated swift-bridge bindings. The swift-bridge crate (packages/swift/rust/src/lib.rs) is entirely generated code with 1:1 wrapper mirrors oftree_sitter_language_packtypes. Rustdoc coverage on these wrappers is not meaningful — the file now emits#![allow(missing_docs)]at the crate root, matching the pyo3 and wasm backends. (Alef upstream:src/backends/swift/gen_rust_crate/mod.rs) -
Bump alef pin
0.20.10 → 0.20.12and regen all bindings, e2e, docs, test_apps, README. Pulls in upstream alef fixes since the rc.16 regen:v0.20.11rubyDir.chdir(ext/<name>/native)wrap onRbSys::ExtensionTask.newsoCargo::Metadatalookup finds the workspace-excluded crate; goembed-import-named-not-blank + extra-blank-line cleanup; R extendr unit-enum constructor wrappers.v0.20.12R extendr numeric-double handling + fixture-extracted backend name; PHP e2e static-method teardown; ruby restoreconfig.ext_dir = "native"in extconf.rb so build-time mkmf path matches the newExtensionTaskresolution. Subsequent main fixes consumed via local hand-edit pending alef 0.20.13: rustlerRustlerPrecompiledbase_url:template pre-wrap somix formatis idempotent (packages/elixir/lib/tree_sitter_language_pack/native.ex— wrapped manually until the alef300d0b85brustler-template fix ships). (alef.toml, 435+ regen files acrosspackages/,e2e/,crates/ts-pack-core-*,docs/reference/,test_apps/,README.md) -
Drop
alef fmtfromCheck version syncstep in both CI workflows. The rc.16 hotfix wiredalef fmtbetweenalef sync-versionsand the diff check to absorbindex.jsoxfmt drift. Butalef fmtinvokes every post-gen formatter at once (clang-format, ktlint, php-cs-fixer, dotnet format, mvn spotless, swift-format, mix format, …) — CI doesn’t install most of those (PHPvendor/bin/php-cs-fixermissing, clang-format pipeline empties to stdin), so the step fails with[ffi] error: cannot use -i when reading from stdinand[php] Could not open input file. Revert: onlyalef sync-versionsruns;alef 0.20.12no longer driftsindex.jspost-sync so the original-w --ignore-blank-linesdiff check passes idempotently. (.github/workflows/{ci,ci-validate}.yaml) -
CI Validate: unblock rc.16 by pinning
pyproject-fmt==2.5.0, applyingalef fmtafteralef sync-versions, and refreshing two version-pin manifests the rc.15→rc.16 regen missed. Three independent CI failures on HEAD447a5f78: (1) thepyproject-fmtprek hook crashes in argparse (add_argument("--table-format", help="...")) under newer pyproject-fmt releases — pinned to2.5.0viaadditional_dependenciesso prek installs the last working version without forkingxberg-io/pre-commit-hooks; (2) theCheck version syncstep in.github/workflows/{ci,ci-validate}.yamlranalef sync-versionsand then failedgit diff -woncrates/ts-pack-core-node/index.jswhere alef emits 2-space wrapped output that diverges from the oxfmt-formatted committed file — addedalef fmtbetween sync and diff so the post-gen formatters bring output back to the committed shape; (3)e2e/go/go.modstill declaredv0.0.0andtest_apps/swift/Package.swiftstill declaredfrom: "1.8.1", whichalef sync-versionson CI 0.20.10 would rewrite — hand-bumped both to1.9.0-rc.16so the diff check passes idempotently. (.pre-commit-config.yaml,.github/workflows/ci-validate.yaml,.github/workflows/ci.yaml,e2e/go/go.mod,test_apps/swift/Package.swift) -
CI Validate: refresh pnpm
minimumReleaseAgeExcludeallowlist for rc.16 platform packages and pick up newlinux-*-muslvariants.CI Validate / Lint & FormatandCI / Validate (Lint & Format)were failing with[ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION]against the six@kreuzberg/tree-sitter-language-pack-*@1.9.0-rc.16platform packages (published 2026-05-29T05:51Z, within the 24h supply-chain age cutoff). Bumped the six existing allowlist entries fromrc.11 → rc.16and added two new entries forlinux-x64-musl/linux-arm64-musl, which the alef 0.20.10 regen now declares asoptionalDependenciesofcrates/ts-pack-core-node/package.json. Regeneratedpnpm-lock.yaml(pnpm install --lockfile-only) so the eight platform entries match the manifest andpnpm install --frozen-lockfilesucceeds in both the node and wasm workspaces. (pnpm-workspace.yaml,pnpm-lock.yaml) -
Restore typed
DownloadErrorfor Python (and equivalent typed exceptions across other bindings). Issue #133:DownloadErrorwas dropped during the alef polyglot migration (commit8557c150) because theDownload,ChecksumMismatch, andCacheLockvariants oncrates/ts-pack-core/src/error.rs::Errorwere#[cfg(feature = "download")]-gated, and the alef variant extractor skipped cfg-gated variants when generating the public exception taxonomy.get_parser("not_a_real_language")consequently raised a bareRuntimeErrorcarrying the message"Download error: ..."instead of a catchable typed exception. The three variants are pure string carriers with no extra dependencies, so the cfg gates were unnecessary — they now live unconditionally on theErrorenum. The next alef regen extracts them asDownloadError,ChecksumMismatchError, andCacheLockError(Python) and equivalents in every other binding, restoring the documentedexcept DownloadErrorcatch path. TheJsonandTomlvariants remain feature-gated because they carry external dependency types. (crates/ts-pack-core/src/error.rs)
Changed
Section titled “Changed”- Docs and READMEs bumped to 306 grammars after the gherkin addition. Updated hand-written count references in
crates/ts-pack-core/{README.md,src/lib.rs},docs/reference/api-*.md(15 files),skills/tree-sitter-language-pack/{SKILL.md,references/*.md},.ai-rulez/domains/parser-compilation/context/tree-sitter-overview.md, and the OOM-mitigation comments in.github/workflows/publish.yaml. Remaining305mentions in alef-generated package metadata (packages/*/Cargo.toml,composer.json,pyproject.toml, etc.) refresh on nexttask alef:generate; thealef.tomlsource-of-truth is already at 306. - repo: Add
.gitattributesmarking all alef-generated output directories (packages/**,crates/*-{py,php,ffi,node,wasm}/**,e2e/**) aslinguist-generated=trueso generated files collapse in GitHub PR diffs. - Bump alef to 0.18.0 and regen all bindings, e2e, docs. Major upstream restructure: workspace renamed
alef-cli→alef(single distributable crate; 28 internalalef-*member crates yanked), Node/WASM crate directories renamed (ts-pack-core-node,ts-pack-core-wasm), and zig/c FFI search paths reorganised. Configuration follow-ups in this repo:[crates.{node,wasm}.crate_dir]overrides pin the napi/wasm-pack build to the renamed crate dirs;napi build --platform --releaseproduces per-platform.nodeartifacts (fixes “Cannot find module ‘./ts-pack-core-node.darwin-arm64.node’” on Node e2e); zig defaults inpackages/zig/build.zigswitched to../../target/release+../../crates/ts-pack-core-ffi/include, with.task/zig.ymland the[crates.test.zig]alef e2e step both passing-Dffi_path=../../target/release; C e2e command corrected from./test_runner→./run_testsand.task/c.ymlswitched from--lang ffi→--lang c; new[crates.e2e].result_fieldsarray +[crates.e2e.fields_c_types]map drive alef’s namespace-aware field navigation for the Cprocess_result.metrics → FileMetrics → uintptr_taccessors. Upstream alef fix in 0.18.0:namespace_stripped_pathno longer strips path segments whenresult_fieldsis empty, so legacy bindings (noresult_fieldsconfigured) keep dotted-field paths intact. All 14 language e2e suites pass after regen. - Source-gem publish now uses the shared
rewrite-native-deps@v1action. Thepublish-rubygemsjob’s source-gem fallback rewrites the native ext’s workspace path-dependency (packages/ruby/ext/ts_pack_core_rb/native/Cargo.toml→crates/ts-pack-core) to a registry version-dependency so the shipped manifest resolves on user install. Replaced the dead “Set up Python (for vendor script)” + “Vendor core for source gem” steps withxberg-io/actions/rewrite-native-deps@v1(lang: ruby) beforegem build, matching the precompiledbuild-ruby-gemjob. (.github/workflows/publish.yaml)
Removed
Section titled “Removed”wolframgrammar dropped from the language pack.tree-sitter-wolframproduces glibc heap corruption (free(): invalid next size) when parsing trivial input under serial test execution on Linux; macOS allocator silently tolerated the corruption. The entire upstream ecosystem is unmaintained (canonicalbostick/tree-sitter-wolframlast touched 2021-11-11 with 3 stars; every known fork —LumaKernel,LoganAMorrison,JuanG970,jakassebaum— ships the sameLANGUAGE_VERSION 13parser tables and is inactive). Rather than fork-and-maintain a Wolfram grammar in-house for marginal demand, the entry is removed fromlanguage_definitions.json, all CITSLP_LANGUAGESlists, the smoke fixture, the e2e harness, the docs, and the README ecosystem listings. Total supported grammar count drops from 306 to 305, which matches the long-standing “305 languages” marketing copy (previously off-by-one due to the broken wolfram entry).- Dead workspace-vendor scripts superseded by shared GitHub Actions. Deleted
scripts/ci/php/vendor-core.py(rewrote theexcludedcrates/ts-pack-phpcrate; publish uses thecrates/ts-pack-core-phpcrate viabuild-php-extension@v1) andscripts/ci/ruby/vendor-core.py(targeted the nonexistentcrates/ts-pack-rubycrate; no-op). Dropped the now-danglingvendortasks from.task/php.ymland.task/ruby.yml; the local PHPbuild/build:devtasks now build thets-pack-core-phpcrate directly, mirroring CI. (scripts/ci/php/vendor-core.py,scripts/ci/ruby/vendor-core.py,.task/php.yml,.task/ruby.yml)
1.9.0-rc.1 - 2026-05-22
Section titled “1.9.0-rc.1 - 2026-05-22”- Four new language bindings via alef 0.16.6, taking total binding count from 10 to 14:
- Dart / Flutter —
dart pub add tree_sitter_language_pack. Built with flutter_rust_bridge for isolate-safe Future APIs. - Kotlin (Android) —
dev.kreuzberg.tslp:tslp-androidAAR on Maven Central. JNI-based with per-ABI native libraries (arm64-v8a, armeabi-v7a, x86_64, x86). JVM Kotlin users continue to consume the canonical Java / Panama-FFM package. - Swift —
TreeSitterLanguagePackvia SwiftPM. swift-bridge for macOS, iOS, and Linux. - Zig —
zig fetch --save <tarball-url>from GitHub Releases. Direct C FFI via@cImport.
- Dart / Flutter —
- Two new Rust binding crates:
tree-sitter-language-pack-dart(FRB bridge) andtree-sitter-language-pack-swift(swift-bridge). - Hand-written
crates/ts-pack-core-jniRust crate exportingJava_...JNI symbols for the Kotlin-Android binding (excluded from the default workspace build because it cross-compiles viacargo ndk). - Per-language CI workflows:
ci-zig.yaml,ci-swift.yaml,ci-dart.yaml, plus a combinedci-mobile.yamlcovering Android cross-compile + iOS cargo check. - Publish jobs for pub.dev (
publish-pub), Swift Package Index (publish-swift), Zig (publish-zig→ GitHub Release tarball), and Maven Central kotlin-android (publish-kotlin-android).
- Download cache is now safe under concurrent multi-process access.
DOWNLOAD_CACHE_LOCKincrates/ts-pack-core/src/lib.rswas aMutex<()>— intra-process only — so multi-worker servers (gunicorn / Puma / Node cluster), fan-out build pipelines (make -j8, parallel test runners), and the zig e2e suite (zig build testspawns eight test binaries in parallel) all raced on the same~/.cache/tree-sitter-language-pack/v{version}/directory. Partialentry.unpackwrites were observable to other workers’libloading::open, producing intermittentLanguageNotFound/ segfaults on first request for an uncached language; N processes could also each redundantly pull the 50MB platform bundle. Cache writes are now atomic (write to<dest_dir>/.<name>.tmp.<pid>.<seq>thenfs::rename— readers see old, new, or nothing, never partial) and the bundle-fetch / extract / clean critical section is serialized across processes with an exclusivefd-lockon<version_cache_dir>/.download.lock. Double-checked locking preserves the lock-free hot path: steady-stateis_cachedlookups never pay the OS file-lock cost. NewError::CacheLock(String)variant surfaces lock-acquisition failures cleanly. Affects every binding (Python, Node.js, Ruby, PHP, Go, Java, C#, Elixir, WASM, Dart, Swift, Zig, Kotlin-Android) because the fix lives entirely in the sharedts-pack-coreRust crate. Newfd-lock = "4"dependency (gated under thedownloadfeature). Cross-process safety relies onflocksemantics, which are unreliable on NFS — users withXDG_CACHE_HOMEon NFS should use a local-FS cache or serialize at the application layer. (crates/ts-pack-core/src/{download.rs,error.rs},crates/ts-pack-core/Cargo.toml,Cargo.toml, newcrates/ts-pack-core/tests/concurrent_download.rs) - Zig e2e auto-omits fixtures outside the static-compiled grammar set (regen on alef
65f1a129). Declared[crates.zig].languages = [<curated 18-grammar list>]mirroring theTSLP_LANGUAGESvalue in[crates.test.zig].before. Alef’s new Zig codegen filter consults bothinput.languageandinput.config.languageand drops fixtures whose target grammar is not in the list (mirroring the WASMf9e0ff50pattern). Eliminatessmoke_bibtexand every other non-static-set test that previously failed at parser-load time. Also reverts the per-fixtureskip: { languages: ["zig"] }workaround onfixtures/smoke/actionscript.jsonsince the auto-omit subsumes it. (alef.toml,fixtures/smoke/actionscript.json) - swift e2e:
processcontainsassertions onVec<DTO>fields aggregate every stringy accessor (regen on alef857c55d1).testProcessPythonImportsDetailandtestProcessRustStructureNamepreviously failed because the codegen relied onresult_field_accessornaming a single “primary” accessor per array field (imports → source,structure → kind), which misses values surfaced on sibling fields —"os"againstImportInfo.items,"MyConfig"againstStructureItem.namerather thanStructureKind. The regenerated tests now emit acontains(where: { item in … })closure that gathers every text-bearing accessor (String, Option, Vec , serde-enum) into a [String]and substring-matches the expected value, mirroring python’s_alef_e2e_item_texts. Swift e2e: 411 tests, 0 failures. (e2e/swift_e2e/Tests/TreeSitterLanguagePackE2ETests/ProcessTests.swift) - Maven JAR native layout collapses every classifier under
natives/native/(#128). The re-stage loop inbuild-maven-packagewalked onedirnametoo far when extracting the classifier from each lib’s path, so all six platform libs landed atnatives/native/{lib}instead ofnatives/{classifier}/{lib}. The Maven Central JAR shipped in v1.8.1 contained only three files (one per.so/.dylib/.dllextension) andTreeSitterLanguagePack.getParser("…")failed withUnsatisfiedLinkError: Expected resource: /natives/windows-x86_64/ts_pack_core_ffi.dll. Fixed the path-walk depth, and hardened both build-side and deploy-side verification steps to require everylinux-x86_64 / linux-arm64 / macos-arm64 / macos-x86_64 / windows-x86_64 / windows-arm64classifier directory is present in the staged JAR so the regression cannot ship again. Additionally corrected the Windows-ARM classifier fromwindows-aarch64towindows-arm64: the Java loader (NativeLib.resolveNativesRid) normalizes every ARM architecture toarm64and resolves tonatives/windows-arm64/, so a JAR staged underwindows-aarch64would stillUnsatisfiedLinkErroron Windows ARM64 — the publish matrix and both verification steps now usewindows-arm64, consistent with thelinux-arm64/macos-arm64classifiers and the loader. (.github/workflows/publish.yaml) - WASM e2e local-feasibility + auto-skip wiring.
[crates.test.wasm].beforepreviously ranwasm-pack buildwith noTSLP_LANGUAGESset, which triggered a full 305-grammar static build — the 97MBabl/parser.calone hangs clang at -O2 for tens of minutes. Mirrored the publish-wasm CI environment locally:TSLP_LINK_MODE=static TSLP_LANGUAGES=<curated 31-grammar list> CARGO_PROFILE_RELEASE_LTO=false CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16. Also declared[crates.wasm].languages = [<same list>]so alef’s wasm e2e auto-skip path correctly elides 268 of the 302 smoke tests for grammars not in the bundle (with the matching aleff23ae5d3/f9e0ff50fixes that teach the wasm filter to look up bothinput.languageandinput.config.language). (alef.toml) - Regen on alef HEAD (csharp List
, go os import, php deterministic accessor ordering, swift codegen trifecta). Pulls in upstream alef fixes:4f6a9056csharp Listemission for mock_url_list;06caa440goosimport include guard formock_url_list;1fde7aaePHP deterministic accessor extraction order (HashMap→BTreeMap; resolves the recurring$imports/$structureflip ine2e/php/tests/ProcessTest.php);13717e24swift e2e — trailing()on scalar accessors that bridge through opaque structs, drop spurious?.map ... ?? []on non-optionalRustVecaccessors, and camelCase swift-bridge method names (e.g.asStr()notas_str()); plus the wasminput.config.languagefilter follow-up cited above. (e2e/php/**,e2e/swift_e2e/**,e2e/wasm/**,e2e/zig/**) - npm darwin-x64 NAPI binary missing (#127).
crates/ts-pack-core-node/package.json#napi.targetsalready listedx86_64-apple-darwin, but thebuild-node-nativematrix in.github/workflows/publish.yamlomitted themacos-15-intelrunner — so v1.8.0 / v1.8.1 npm tarballs shipped withoutts-pack-core-node.darwin-x64.node, breakingrequire('@kreuzberg/tree-sitter-language-pack')on Intel Macs. Added amacos-15-intel/darwin-x64/x86_64-apple-darwinrow to the matrix, mirroring the parity already present in the Python/Ruby/Java/Go publish matrices. The next published version (≥1.8.2) will include the darwin-x64 binary. (.github/workflows/publish.yaml) - Regen on alef v0.17.13. Pulls in four upstream fixes since v0.17.11:
fix(alef-e2e/rust): unwrap Option<scalar> leaf fields in numeric comparison assertions(the threegreater_than/less_than/less_than_or_equaloperators no longer fail to compile when the leaf field isOption<T>),fix(alef-e2e/rust): use serde_json::from_str instead of json! macro for fixture json_object args(sidesteps the macro recursion-limit on fixtures with large JSON payloads),fix(alef-backend-php): emit Box::default() instead of Box::new(Default::default()) for boxed fallback fields(resolvesclippy::box-default-D warnings on the PHP umbrella crate), andfeat(alef-core,alef-e2e/wasm,alef-e2e/typescript): auto-skip wasm fixtures outside the static-compiled language set(foundational for tslp’s curated wasm32 builds; no-op for now since[crates.wasm].languagesis empty, but unlocks the future curated-build flow). Side effects in this regen: a few Rust e2e fixture bodies re-formatted,e2e/c/main.ccosmetic update, andpackages/swift/rust/Cargo.tomldeps re-ordered. (alef.toml,e2e/{c,php,rust}/**,packages/swift/rust/Cargo.toml) - CI E2E (.NET) lib-path block uses grouped redirect.
shellcheck SC2129flagged four consecutiveecho … >> "$GITHUB_ENV"lines in the Set library paths for .NET step; consolidated into a single grouped{ … } >> "$GITHUB_ENV"block to keep actionlint clean on the workflow. (.github/workflows/ci-e2e.yaml) - Pin alef to v0.17.10. Bumps
alef_versioninalef.tomland the alef pre-commit-hook rev. Lands the Phase-5 leakage-sanitizer chain plus follow-up codegen fixes: v0.17.4 csharp/elixir/kotlin/swift codegen-consumer unblocks; v0.17.5 NAPI/PHP/Java docstring sanitizer wiring; v0.17.7 sanitizer recognises rustdoc test-attribute fences (```no_run,```ignore,```should_panic,```compile_fail,```edition*) as Rust code (so their bodies are dropped for foreign-language targets); v0.17.8/v0.17.9 csharp U1-bool P/Invoke call-site fix; v0.17.10 Swift free-function forwarder fixes —Option<String>returns now use?.toString()and host DTO args flow through.intoRust()before the bridge call, sodetectLanguageFromExtension/Path/Content, the*Querygetters, andprocess(_:config:)compile and execute against the high-level Swift API. Downstream surface: 61 Rust-code-block leaks incrates/ts-pack-core-node/index.d.tsand 20+ incrates/ts-pack-core-php/src/lib.rscollapse to 0 after this regen. - Rust e2e
chunksundefined.e2e/rust/tests/process_test.rsfourtest_*_chunking_*cases were emittingassert!(chunks.len() >= 2 as usize, ...)wherechunkswas undeclared (E0425). Same class of bug as the PHP$chunksfix; alef’s Rust e2e codegen unconditionally fired the streaming-virtual-field assertion arm forchunks/imports/structureeven for non-streaming fixtures. Fix pulled in via alefa32ca2a0 fix(rust-gen): bind fields_array accessor before len() assertion in e2e tests— non-streaming fixtures with a collidingfields_arrayfield now emit a leadinglet {field} = &{result}.{field};binding. e2e/nodetree-sitterdev-dep restored (recurring).alef generatestripstree-sitter@^0.25.0frome2e/node/package.jsonon every regen, buttests/capsule_passthrough.test.tsimports it to verify FFI capsule type-tag pass-through between ourLanguageobject and the upstream tree-sitter Node native module. Hand-restored, alongside the correspondingpnpm-lock.yamlrows.- Subsequent regen on top of alef Swift API tightening. Pulls in alef
2eaa260a fix(swift): hide RustVec/RustString/intoRust from public API; convert at forwarder boundariesplus a handful of smaller adapter fixes (fix(alef-backend-pyo3),fix(alef-backend-napi,wasm),fix(alef-backend-ffi)clippy). Public Swift API surface no longer leaksRustVec/RustString/intoRust(); conversion happens at forwarder boundaries inside generated extensions. - CI green-up. Regenerated
pnpm-lock.yamlto drop the stalee2e/node → tree-sitter@^0.25.0devDependency that brokepnpm install --frozen-lockfileinCI Validate. Regenerated thedocs/reference/api-*.mdset so committed output matchesalef docs(compact Markdown tables) andalef verifystays green onmain. - Full alef regen on top of upstream codegen fixes. Pulls in three alef fixes:
fix(swift): enum intoRust(), Ref→owned init, Vec<RustString> elem type(Swift CI was failing onCommentKind.intoRust(),RustStringRef.toString(),RustVec<String>not conforming toVectorizable);fix(php-gen): bind fields_array accessor before count() assertion in e2e tests(PHP e2etest_*_chunking_*cases were referencing undefined$chunks);fix(alef-backend-go): null-check and box Option<String> returns instead of dereferencing(generatedpackages/go/binding.gowas returningC.GoString(ptr)where the signature expected*string, breakinggolangci-lintandgovulncheck). Side-effect: API docstrings now elide Rust-style[Type]-link syntax (e.g. PHPNode.phpdoc comments now readA single syntax node within a 'Tree'instead ofA single syntax node within a [Tree]). - WASM yuck grammar marked unsupported.
tree-sitter-yuckproducesRuntimeError: unreachablewhen parsing under wasm32 (same class of bug as zig/ziggy, which already skip on wasm).fixtures/smoke/yuck.jsonnow carriesskip: { languages: ["wasm"] };alef e2e generateremoved the corresponding test frome2e/wasm/tests/smoke.test.ts. Native bindings remain unaffected. package.jsonpnpm-field cleanup. Removed the now-ignoredpnpm.onlyBuiltDependenciesblock from the rootpackage.json. pnpm 11 reads that setting frompnpm-workspace.yaml(which already declares the same allowlist); the duplicate field made pnpm emit a warning on every install.- Downloader now honours the host OS trust store by default (#125). Manifest and bundle downloads from
github.com/xberg-io/tree-sitter-language-pack/releases/...previously used ureq 3.x’s default rustls agent, which trusts only the bundled Mozilla webpki roots and ignores the platform store. On Linux/WSL2 hosts where GitHub HTTPS traffic is presented with a chain rooted in a locally trusted (corp / private) CA — and wherecurl,pip, andgitall succeed against the same URL via the OS trust store — first-use parser downloads failed withDownloadError: ... io: invalid peer certificate: UnknownIssuer. The downloader now constructs a configuredureq::AgentwithRootCerts::PlatformVerifierby default (viarustls-platform-verifier), matching the behaviour of every other host-trust-aware HTTP client on the system. SetTREE_SITTER_LANGUAGE_PACK_TLS_ROOTS=webpkito opt back into ureq’s bundled Mozilla roots; setTREE_SITTER_LANGUAGE_PACK_TLS_ROOTS=platformto make the default explicit. Affects every binding (Python, Node.js, Ruby, PHP, Go, Java, C#, Elixir, WASM, Dart, Swift, Zig, Kotlin-Android) because the fix lives entirely in the sharedts-pack-coreRust crate. (crates/ts-pack-core/src/{download.rs,pack_config.rs}, workspaceCargo.toml)
Removed
Section titled “Removed”wolframgrammar dropped from the language pack.tree-sitter-wolframproduces glibc heap corruption (free(): invalid next size) when parsing trivial input under serial test execution on Linux; macOS allocator silently tolerated the corruption. The entire upstream ecosystem is unmaintained (canonicalbostick/tree-sitter-wolframlast touched 2021-11-11 with 3 stars; every known fork —LumaKernel,LoganAMorrison,JuanG970,jakassebaum— ships the sameLANGUAGE_VERSION 13parser tables and is inactive). Rather than fork-and-maintain a Wolfram grammar in-house for marginal demand, the entry is removed fromlanguage_definitions.json, all CITSLP_LANGUAGESlists, the smoke fixture, the e2e harness, the docs, and the README ecosystem listings. Total supported grammar count drops from 306 to 305, which matches the long-standing “305 languages” marketing copy (previously off-by-one due to the broken wolfram entry).
Changed
Section titled “Changed”- Split pub.dev publish into a dedicated
publish-pubdev.yamlworkflow triggered bypush: tags: v*. pub.dev OIDC trusted publishing rejects tokens fromreleaseevents; onlypushandworkflow_dispatchevents are accepted. The new workflow produces an accepted token. One-time setup required: configure pub.dev → tree_sitter_language_pack package → Admin → Automated publishing with workflow path.github/workflows/publish-pubdev.yaml. (.github/workflows/publish-pubdev.yaml,.github/workflows/publish.yaml) - Regenerated all alef-managed surfaces (per-binding READMEs, API reference docs, generated bindings, e2e tests) and the script-managed docs/languages.md +
_supported_languages.pyto reflect the 305-grammar count. scripts/generate_grammar_table.pydefault output path corrected fromdocs/supported-languages.mdto the canonical nav-referenceddocs/languages.md; Taskfiledocs:generate:languagesgenerates:field updated to match.
1.8.1 - 2026-05-13
Section titled “1.8.1 - 2026-05-13”- E2E fixture coverage for: language alias resolution (
shell→bash) viahas_language/get_language/get_parser(3 fixtures);downloadedge cases — empty list, multiple-language, and unknown-language error path (3 fixtures); error-handling for 120KB sources andget_language("")(2 fixtures); and TypeScript function parsing (1 fixture). Brings fixture count from 403 to 412, covering 100% of the publicdownload,get_*, andhas_languagesurface across all 10 language bindings.
-
Node:
getLanguage(name)now returns a realtree-sitterLanguagethatnew Parser().setLanguage(lang)accepts at runtime. The previous capsule shim usednapi::bindgen_prelude::External::new(rejected bynode-tree-sitter’sUnwrapLanguage), wrote the External to__parser, and did not type-tag the value. Adopts alef v0.15.49 where the napi capsule codegen emits rawnapi_create_external+napi_type_tag_objectand readsproperty_name/type_tagfrom[crates.node.capsule_types]. -
Python:
PackConfigandProcessConfigtype hints now resolve to the.optionsdataclasses, fixingmypy --stricterrors at everyinit(...)/process(...)call site (adopts alef #72). -
Python: restore
SupportedLanguageasLiteral[...]of all 306 grammars attree_sitter_language_pack.SupportedLanguage. The symbol was dropped during the alef 0.15.x codegen migration and re-importing it raisedImportErrorin 1.8.0 (#121). -
Python:
get_parser("python").parse(b"...")returns a realtree_sitter.Treeagain instead of raisingAttributeError.get_parser/get_languagenow return nativetree_sitter.Parser/tree_sitter.Languageinstances via PyO3 capsule pass-through (alef v0.15.39 wirescapsule_typesthroughgen_bindings) (#121).
Changed
Section titled “Changed”- CI pinned to Node 22 LTS across all workflows.
tree-sitter@0.25.0(thetree-sitternpm package) ships abinding.ccwritten against pre-C++20 stdlib (nostd::ranges,concept,requires) and fails to compile against Node 24/26’s V8 headers. Node 22 is the latest supported runtime until upstreamnode-tree-sitterupdates itscflags_ccor ships prebuilds. - CPD pre-commit hook and
packages/java/pom.xmlmaven-pmd-pluginminimum-tokens bumped from 100 → 250: alef’s java codegen emits ~200-tokentry/catchcleanup blocks onDownloadManager/LanguageRegistry. Refactoring the codegen to share a helper is tracked separately.
1.8.0 - 2026-05-09
Section titled “1.8.0 - 2026-05-09”- macOS x86_64 native binaries across all polyglot bindings (Python wheels, npm napi, Ruby gem, Maven JAR, NuGet, C FFI, Go FFI, libts-pack bottle) — restores Intel Mac coverage that was missing under the alef 0.11 transition
- Real Homebrew bottle protocol for both
ts-pack(CLI) andlibts-pack(FFI library) viabrew install --build-bottle+brew bottle --json, replacing the prior synthetic tarball approach. Eight bottles per release acrossarm64_sequoia,sequoia,arm64_linux,x86_64_linux.brew installnow pours instead of source-building libts-packHomebrew formula bundling tree-sitter language pack as a C library (headers + dylib/so + static archive)- Python sdist published to PyPI alongside the existing platform wheels
- E2E fixtures covering Kotlin package + class structure (
kotlin_package_class_intel.json), Java package declarations (java_package_intel.json), and a process call exercising the typedextractionsmap (process_with_extractions.json)
Changed
Section titled “Changed”- Migrated to alef 0.15.x (Jinja-based codegen) for all polyglot bindings — Python, TypeScript, Ruby, Go, Java, C#, Elixir, PHP, WASM
- WASM now ships the
--target nodejsbuild to npm so consumers no longer hit the bundler-onlyimport * from "env"failure onrequire() - WASM coverage scoped to a curated 32-language subset to fit the 16 GB GitHub runner during builds
- Intel: emit
StructureKind::Modulefor Kotlinpackage_headerand Javapackage_declarationso callers can build fully-qualified names for JVM languages (#112) - Intel: resolve structure names via a fallback chain (
namefield →type_identifier→identifier→scoped_identifier) so Kotlin classes and Java/Kotlin packages no longer surface with null names (#111) - Java: ship
natives/{rid}/entries inside the published JAR —actions/download-artifactproduces nested artifact paths, and the previous staging loop preserved them, so every platform hitUnsatisfiedLinkErroron load. Flatten viafindand add presence/jar tfguard steps so the regression cannot ship silently again (#114) - Bindings: surface
extractionsas a typedMap<String, ExtractionPattern>/Map<String, PatternResult>across Java, Python, Go, TypeScript, Ruby, PHP, C#, Elixir, FFI, and WASM (wasOptional<String>on Java, blocking pattern extractions through the high-level API). Driven by the alef 0.12.4 codegen fix forAHashMap-typed fields (#115) - C#: strip duplicate
{lines emitted by alef 0.14.33 codegen so generated.csfiles compile - Ruby: regenerated
native.rbno longer recurses into itself viadefine_singleton_method— magnus codegen now skips re-export when binding name matches the native module method - Node:
index.jsnow contains real platform-dispatch logic sorequire()resolves the correct.darwin-arm64.node/.linux-x64-gnu.node/etc. instead of failing on the un-suffixed bundle name - WASM: drop bundler-only output, removing spurious
'env'module imports that brokerequire()from Node consumers - Maven JAR previously missed
linux-x86_64natives because of stage-loop path mishandling; flatten artifact downloads and add ajar tfguard - Hex.pm
metadata.configsize limit — exclude the parser sources tarball from the package - PHP: fix broken
crates/ts-pack-php/README.mdlinks in rootREADME.md— path moved topackages/php/README.mdafter alef migration (#106) - PHP: fix
.task/php.ymlbuild,build:dev, andcleantasks pointing to removedcrates/ts-pack-php/— corrected tocrates/ts-pack-core-php/(#106) - PHP: align
packages/php/composer.jsonandpackages/php/README.mdpackage name to canonical Packagist vendor slug (kreuzberg/notxberg-io/) (#106) - PHP: document
mlocati/php-extension-installerprerequisite in install docs and correct minimum PHP version to 8.4+ (#106) - Go: regenerate stale
binding.gowith current alef generator
1.7.0 - 2026-04-22
Section titled “1.7.0 - 2026-04-22”- Migrate to alef polyglot binding generator — all language bindings (Python, TypeScript, Ruby, Go, Java, C#, Elixir, PHP, WASM) are now generated from a single
alef.tomlconfiguration Default,Hash,PartialEq,Eqderives on all public types- 18 new e2e test fixtures closing testing gaps across all binding languages
- Consolidated CI: 12 language-specific workflows merged into a single
ci.yaml - Registry-mode e2e test apps under
test_apps/(generated viaalef e2e generate --registry)
Changed
Section titled “Changed”- Public API locked down with
pub(crate)— only functions and types that were in the pre-alef Python bindings are exported; internal modules (json_utils,intelsubmodules,config,definitions) are no longer public - Workspace lints applied to all binding crates (
clippy::all = "deny",unsafe_code = "deny") test_apps/moved fromtests/test_apps/to project root
available_languages(),has_language(), andlanguage_count()now register the download cache directory before querying the registry — fixes empty results when using thedownloadfeature (#90)process()auto-downloads missing parsers instead of returningLanguageNotFound(#94)- C# task references updated from
.slnto.csproj - Maven version plugin pinned to exclude alpha/beta/RC versions
- Docker CI:
uv runchanged touv run --no-projectto avoid triggering root pyproject.toml build - Ruby CI: removed stale
working-directorythat pointed to wrong path
1.6.3 - 2026-04-20
Section titled “1.6.3 - 2026-04-20”- Go: fix FFI build defaults — add
TSLP_LINK_MODEandTSLP_LANGUAGESenv vars to Go task (#102) - Go: fix CGO
LDFLAGSpaths — point to workspacetarget/release/instead of crate-local path (#102) - Go: remove duplicate forward declarations from
ffi.go(already ints_pack.h) (#102) - Go: fix README examples — proper error handling, correct API signatures (
Init,Download) (#102) - FFI: add extra libs dir from
cache_dir()to registry on creation (#102) - Docs: fix textlint pre-commit hook — add
additional_dependenciesfor all textlint plugins (#102)
1.6.2 - 2026-04-18
Section titled “1.6.2 - 2026-04-18”- Compile bundled grammars with
-fno-strict-aliasingto prevent undefined behavior (#100)
Changed
Section titled “Changed”- Update dependencies across lockfiles
- Regenerate READMEs for 1.6.1 version bump (#101)
1.6.1 - 2026-04-17
Section titled “1.6.1 - 2026-04-17”- Go: move package root from
packages/go/v1/topackages/go/so the Go module proxy can resolvego.modat the correct path —go get github.com/xberg-io/tree-sitter-language-pack/packages/gonow works (#97) - Go: fix CGO
SRCDIR-relative include/lib paths (one fewer../after directory restructure) - Remove
features = ["all"]from e2e Rust testCargo.toml— usedownloadfeature for runtime parser fetching - Remove 305
lang-*features to unblock crates.io publish (300 feature limit) - Regenerate READMEs for v1.6.0, fix Windows query cache test flake
- Bump
rustls-webpkito patch RUSTSEC-2026-0098 and RUSTSEC-2026-0099 (#99) - Fix MIME type inference in core build by embedding
language_definitions.jsonin crate
Changed
Section titled “Changed”- Update dependencies across Python, Node.js, PHP, and Rust lockfiles
- Replace feature group docs with
download/TSLP_LANGUAGESdocumentation in READMEs
1.6.0 - 2026-04-14
Section titled “1.6.0 - 2026-04-14”- Thread-local parser cache in
parse_string()— avoids re-creating parsers on repeated calls for the same language - Two-level compiled query cache (thread-local + global) in
run_query()— avoids recompiling tree-sitter queries parse_with_language()internal API for callers that already have aLanguageobject- Pre-computed capture names in
CompiledExtraction— avoids rebuilding on every extraction call - Go
type_specdeclarations extracted as symbols with correctSymbolKind(struct, interface, type) - Dedicated “Download Parsers” section in quickstart docs covering CLI, programmatic APIs, groups, Docker/CI, and config files
- Tests for parser cache reuse, query cache sharing across threads, cursor byte-range isolation, and capture name correctness
compiled_query()now propagatesError::LockPoisonedinstead of silently ignoring poisoned RwLockQueryCursorbyte-range no longer leaks between patterns when reusing the cursor inextract_from_tree()- Replaced
std::collections::HashMapwithahash::AHashMapin parser cache for consistency - Redundant
get_language()call removed fromparse_string()hot path — only called on cache miss
Changed
Section titled “Changed”CompiledExtraction::extract()andintel::parse_source()now use the thread-local parser cacheQueryCursorreused across patterns within a singleextract_from_tree()call- Unnecessary
Stringallocation removed fromnode_types.contains()check in chunking
Removed
Section titled “Removed”- All 305
lang-*Cargo features and group features (all,web,systems,scripting,data,jvm,functional,wasm) — language selection is now viaTSLP_LANGUAGESenv var at build time; thedownloadfeature (default) fetches parsers at runtime
1.5.0 - 2026-04-08
Section titled “1.5.0 - 2026-04-08”- 57 new permissively-licensed grammars — 305 languages total
- abl, c3, cel, cfml, chuck, cst, dhall, elvish, gap, gdshader, glimmer, gnuplot, gotmpl, gowork, gpg, hjson, hocon, hoon, htmldjango, jai, javadoc, json5, kcl, mlir, nasm, norg_meta, ocamllex, openscad, phpdoc, poe_filter, prql, rasi, razor, rbs, roc, rtf, slang, smalltalk, sml, snakemake, souffle, sourcepawn, sql_bigquery, stan, superhtml, sway, systemverilog, tact, tera, typespec, typoscript, vhs, vrl, wgsl_bevy, x86asm, ziggy, ziggy_schema
- CI license validation job in
ci-validate.yaml— blocks PRs that introduce non-permissive (GPL/AGPL/LGPL/MPL) grammars
lessgrammar: regenerated parser from ABI 11 to ABI 14 (was incompatible with tree-sitter 0.26)cornsmoke fixture: replaced invalid"x"snippet with valid corn syntax
1.4.1 - 2026-03-31
Section titled “1.4.1 - 2026-03-31”- Include
language_definitions.jsonin the published crate sobuild.rscan find extension mappings, ambiguity data, and C symbol overrides when installed from crates.io
Changed
Section titled “Changed”- Updated dependencies across all language ecosystems
1.4.0 - 2026-03-29
Section titled “1.4.0 - 2026-03-29”- Expose
detect_languagein Python public API (#85) - PHP extension name corrected to
ts-pack-php(hyphens)
Changed
Section titled “Changed”- All language snippet READMEs and documentation corrected
- Removed automated grammar updates workflow
1.3.3 - 2026-03-27
Section titled “1.3.3 - 2026-03-27”C_SYMBOL_OVERRIDEStable now includes ALL languages fromlanguage_definitions.json, not just compiled ones — fixes download and loading ofcsharp,vb,embeddedtemplate,nushellfrom PyPI/npm/RubyGems packagesdownloaded_languages()returns canonical names (csharp) instead of c_symbol names (c_sharp)- Elixir NIF publish: upload both hyphen and underscore artifact names so RustlerPrecompiled can find them
- Elixir NIF 2.17 packaging: fix stale variable names from dual-name refactor
- Ruby comprehensive test: remove
JSON.parseon native Hash return fromprocess() - Go comprehensive test: access flat
ProcessResultfields directly (nometadatawrapper) - Homebrew bottle and PHP PIE packages now included in release artifacts
Changed
Section titled “Changed”- Dependency updates across all language ecosystems
rustler_precompiledupdated to 0.9.0 (Elixir)
1.3.2 - 2026-03-26
Section titled “1.3.2 - 2026-03-26”- Dynamic parser loading for languages with
c_symboloverrides (csharp,vb,embeddedtemplate,nushell) — build was naming libraries with the raw name but runtime loader expected thec_symbolname (#80) - Go E2E generator: unused
tspackimport in non-process test files - Elixir: add missing
extract/2andvalidate_extraction/1NIF declarations - PHP E2E generator: use double-quoted strings for source code so
\nis interpreted correctly - Nim grammar: switch from abandoned
paranim/tree-sitter-nim(ABI v11) toaMOPel/tree-sitter-nim(MIT, ABI v14)
- Smoke test fixtures for all
c_symboloverride languages (csharp, vb, embeddedtemplate, nushell) - Dynamic-linking CI step in
ci-all-grammars.yamlto catchc_symbolnaming mismatches
1.3.1 - 2026-03-26
Section titled “1.3.1 - 2026-03-26”- Ruby binding:
process(),extract(),validate_extraction()now return native Ruby Hash instead of raw JSON string - WASM binding: output keys now use camelCase (matching Node.js binding convention), input config accepts both camelCase and snake_case
- Go E2E generator: use typed
*ProcessResultstruct fields instead of invalidjson.Unmarshalon non-string return - Elixir CI: stage NIF with both hyphenated and underscored filenames to satisfy Rustler force-build check and
load_fromloader
1.3.0 - 2026-03-26
Section titled “1.3.0 - 2026-03-26”- Extraction query API: run user-defined tree-sitter queries and get structured results
extract_patterns()/extract()across Python, Node.js, Rust, Ruby, Elixir, PHP, WASM, C FFIvalidate_extraction()for config validation without executionCompiledExtractionfor pre-compiled query reuse (Rust)ProcessConfig.extractionsfor combining custom queries with standard analysis- Types: ExtractionConfig, ExtractionPattern, CaptureOutput, CaptureResult, MatchResult, PatternResult, ExtractionResult
- Criterion benchmarks: 9 groups, 23 benchmarks across Python, TypeScript, Rust, Go
- Extraction queries guide and documentation across all API references
- E2E generator:
process_imports_contains_sourceassertion uses contains instead of equality - WASM: language list matches actual compiled features (30 languages)
- WASM: add missing
detectLanguageFromPathanddetectLanguageFromExtensionexports - PHP generator: null array handling in
process()result assertions - Elixir: RustlerPrecompiled
cratefield resolution withload_fromoverride - Predicate evaluation: remove redundant re-evaluation (tree-sitter 0.26 handles internally)
- Documentation: stale version numbers, incomplete API references, incorrect function signatures
- Java version requirement standardized to JDK 25+
1.2.1 - 2026-03-25
Section titled “1.2.1 - 2026-03-25”- Nushell grammar
c_symboloverride — linker errorundefined symbol: tree_sitter_nushell - E2E generator calling
.as_deref()onStringtype (compile error on CI) - WASM build: gate
c_symbol_forbehinddynamic-loading/downloadfeatures (dead code warning) - Elixir publish: align RustlerPrecompiled
crate:field with Cargo[lib]name (underscores, not hyphens) - Elixir publish: add
--cfgflag patch to publish workflow for Rustler 0.37.3 compatibility - Python
without_gil(): addcatch_unwindto ensure GIL is reacquired on panic - Text splitter: prevent zero-width chunks in pathological UTF-8 edge case
- Comment kind detection: handle
//!,/*!, anddoc_commentnode types - Import detection: restrict fallback to explicitly supported languages only
- Export detection: use field-based AST matching instead of fragile
text.contains()
Changed
Section titled “Changed”- Registry:
Arc<Vec<PathBuf>>for extra lib dirs (avoids Vec clone per language lookup) - Registry:
AHashSet<&str>inavailable_languages()(avoids 248+ String allocations) NodeInfo.kindusesCow::Borrowed(zero-copy from tree-sitter’s&'static str)- Python:
with_tree()/try_with_tree()helpers replace 9 duplicate lock patterns - Python:
without_gil()helper replaces 5 duplicate GIL release patterns - Core:
extension_ambiguity_json()helper replaces duplicated JSON serialization in 4 bindings - Chunking:
MetadataCollectorstruct reduces function from 11 to 7 parameters - FFI: 25 SAFETY comments added to unsafe blocks
- Docs: rewrite all 12 API references to match actual binding source code
- Docs: add JSON-LD structured data and Open Graph metadata for crawlers
1.2.0 - 2026-03-25
Section titled “1.2.0 - 2026-03-25”- 49 new permissively-licensed grammars — 248 languages total
- angular, bass, blade, brightscript, circom, cooklang, corn, crystal, cue, cylc, desktop, djot, earthfile, ebnf, editorconfig, eds, eex, elsa, enforce, facility, faust, fidl, foam, forth, git_config, git_rebase, godot_resource, http, hurl, just, ledger, less, liquid, mojo, move, nickel, nginx, norg, nushell, promql, pug, ql, robot, teal, templ, tmux, todotxt, turtle, vimdoc, wolfram
- Grammar updater automation (
scripts/check_grammar_updates.py) with weekly CI workflow - Generated supported languages table (
docs/supported-languages.md) integrated into docs CI - Node.js NAPI exports:
detectLanguageFromExtension,detectLanguageFromPath,getHighlightsQuery,extensionAmbiguity - E2E
processtest category withprocess()API coverage across all 11 language bindings
- Download/load filename mismatch for languages with c_symbol overrides (csharp, embeddedtemplate, vb) — fixes #80
- E2E fixture system: merged stale
intel/andmetadata/directories into unifiedprocess/category - TypeScript and WASM e2e generators now use camelCase for metrics keys
- Docker CI grammar fixture updated to include all languages
- Elixir publish workflow: checksum file verification, increased retry timeout
- Missing Node.js
index.jsexports for detection and query functions
Changed
Section titled “Changed”- Renamed e2e fixture assertions from
intel_*/meta_*toprocess_* - All documentation and package descriptions updated to reflect 248 languages
1.1.4 - 2026-03-24
Section titled “1.1.4 - 2026-03-24”- New language:
al(AL / Business Central) — 198 languages total - Grammar license linter (
scripts/lint_grammar_licenses.py,task lint:licenses) verifies all grammars use permissive licenses - Permissive license policy documented in CONTRIBUTING.md, docs, and README
- Replace
nimgrammar (alaviss, MPL-2.0 copyleft) with paranim/tree-sitter-nim (MIT) - Replace
prologgrammar (codeberg foxy, AGPL-3.0 copyleft) with Rukiza/tree-sitter-prolog (ISC) - Docs: align mkdocs config with kreuzberg branding; mermaid diagrams now render (fixes #81)
1.1.3 - 2026-03-24
Section titled “1.1.3 - 2026-03-24”- Dynamic loader: resolve
c_symboloverrides for csharp, embeddedtemplate, and vb soget_language()works for dynamically loaded grammars (fixes #80) - E2E generator: enable all ProcessConfig features (structure, imports, exports, comments, docstrings, symbols, diagnostics) for intel tests so diagnostics assertions pass
- 23 new smoke test fixtures for languages missing coverage: asciidoc, awk, batch, caddy, cedar, cedarschema, csharp, devicetree, diff, dot, embeddedtemplate, idris, jinja2, jq, lean, pkl, postscript, prolog, rescript, ssh_config, textproto, tlaplus, vb, wit, zsh
- CI workflow (
ci-all-grammars.yaml) that tests all 197 grammars end-to-end, preventing regressions like #80 rust:e2e:all-grammarstask for running the full grammar suite locally
1.1.2 - 2026-03-23
Section titled “1.1.2 - 2026-03-23”- Elixir NIF: fix Rustler crate name mismatch (
ts_pack_elixir→ts-pack-elixir) causing compilation failure - Rust crate publish: embed query file contents at build time instead of using
include_str!with relative paths that break in the cargo package tarball
1.1.1 - 2026-03-23
Section titled “1.1.1 - 2026-03-23”- WASM build: ahash uses compile-time-rng instead of runtime-rng (avoids getrandom on wasm32)
- Docker/static build: add
c_symboloverride for grammars with non-standard C symbol names (csharp, vb, embeddedtemplate) - Unused imports when
dynamic-loadingfeature disabled (WASM builds) - Python sdist:
.pyiandpy.typednow included in both wheel and sdist - C# build: add missing
ExtensionAmbiguityResultmodel class - Set
generate: truefor csharp, vb, embeddedtemplate grammars
Changed
Section titled “Changed”- Switch from
std::HashMap/HashSettoahash::AHashMap/AHashSetfor faster hashing in registry
1.1.0 - 2026-03-23
Section titled “1.1.0 - 2026-03-23”- 20 new languages from arborium: asciidoc, awk, caddy, cedar, cedarschema, devicetree, dot, idris, jinja2, jq, lean, postscript, prolog, rescript, ssh_config, textproto, tlaplus, vb, wasm-interface-types, zsh (197 total)
- Centralized extension-to-language mapping:
sources/language_definitions.jsonis the single source of truth for 239 file extensions across 197 languages - Build-time code generation:
build.rsgenerates extension lookup with strict validation (panics on duplicates, non-ASCII, uppercase, dots) detect_language_from_content(content): shebang-based language detection (#!/usr/bin/env python3→ “python”)extension_ambiguity(ext): query whether a file extension is ambiguous (e.g..m→ objc with matlab alternative)- Highlight query bundling:
get_highlights_query(lang),get_injections_query(lang),get_locals_query(lang)— embed .scm queries at build time ambiguousfield inlanguage_definitions.jsonfor declaring known extension ambiguities- E2E test fixtures and generators for detect-language, ambiguity, and highlights across all 11 language targets
- New APIs exposed in all bindings: Python, Node.js, Ruby, WASM, Elixir, PHP, C FFI, Go, C#
Changed
Section titled “Changed”LanguageRegistryusesArc<RwLock<Vec<PathBuf>>>for interior mutability — no more globalRwLockwrapper, eliminates lock poisoning riskProcessConfig.language:String→Cow<'static, str>(zero allocation for string literals)NodeInfo.kind,QueryMatch.captures:String→Cow<'static, str>available_languages()usesHashSetfor O(1) dedup instead of O(n) Vec contains- Chunking line counting uses precomputed newline table with binary search (O(log n) per chunk vs O(n))
- Added
memchrdependency for fast byte scanning in text splitter and chunking - Extension/ambiguity lookups generated from JSON at build time
clone_vendors.pynow copiesqueries/directories alongsidesrc/
- Strong types in all binding stubs: Python
.pyi(TypedDicts), TypeScript.d.ts(interfaces), Ruby.rbs(record types), C#Models.cs(string enums replaceobject) - Pre-existing registry test failures from global
RwLockpoisoning — test helpers now use localLanguageRegistry::new() - Removed ambiguous
.os(bsl) and.cls(apex/LaTeX conflict) extensions
1.0.0 - 2026-03-21
Section titled “1.0.0 - 2026-03-21”Changed
Section titled “Changed”- Docker: separated publish-docker workflow from main publish (180-minute timeout for multiplatform builds)
- Docker: publish-docker now triggers on
releaseevents and includes full smoke tests before push - Test apps: all bindings now download languages before running tests (Ruby, Go, Elixir)
- Test apps: Rust test app adds parse_string validation tests
- Test apps: CLI smoke test adds chunking test
- Test apps: added Homebrew smoke test suite
- npm publish authentication and registry configuration
- Elixir NIF binary build and checksum generation
- Ruby CI and WASM build timeout
- Version sync across binding manifests
Pre-1.0 Releases (Python-only)
Section titled “Pre-1.0 Releases (Python-only)”0.12.0
Section titled “0.12.0”- tree-sitter-cobol grammar support
- MSVC build compatibility for cobol grammar
- Alpine Linux (musl) wheel platform tag support (PEP 656)
- Wheel file discovery in CI test action
0.11.0
Section titled “0.11.0”- tree-sitter-bsl (1C:Enterprise) grammar support
Changed
Section titled “Changed”- Updated all dependencies and relocked
0.10.0
Section titled “0.10.0”- tree-sitter 0.25 support
Changed
Section titled “Changed”- Dropped Python 3.9 support
- Adopted prek pre-commit workflow
- CI: cancel superseded workflow runs
- WASM (wast & wat) grammar support
- F# and F# signature grammar support
- tree-sitter-nim grammar support
- tree-sitter-ini grammar support
- Swift grammar update (trailing comma support)
- sdist build issues resolved
- GraphQL grammar support
- Kotlin grammar support (SAM conversions)
- Netlinx grammar support
Changed
Section titled “Changed”- Swift grammar update (macros + copyable)
- Apex grammar support
- MSYS2 GCC build issues
- OCaml and OCaml Interface grammar support
- Markdown inline parser support
- Pinned elm and rust grammar versions
- Pinned tree-sitter-tcl to known-good revision
- ARM64 Linux CI builds
- Build issue resolved
- Windows DLL loading compatibility issues
- Windows compatibility and encoding issues for non-English locales
- PyCapsule-based language loading
- Protocol Buffers (proto) grammar support
- SPARQL grammar support
Changed
Section titled “Changed”- Updated generation setup and build matrix
- Removed magik and swift grammars (temporarily)
Changed
Section titled “Changed”- Version bump with dependency updates
- Added MANIFEST.in for sdist packaging
- Missing parsers in package data
- Initial release with 100+ tree-sitter language grammars
- Python package with pre-compiled parsers
- Multi-platform wheel builds (Linux, macOS, Windows)