Skip to content

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.

  • BAML grammar (baml, from BoundaryML/baml-treesitter, Apache-2.0), bringing the pack to 372 languages (#200). Upstream ships an ABI 15 parser.c; it is regenerated at ABI 14 like the rest of the pack. Known limitation: the grammar rejects the documented backtick prompt form 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, StructureKind and SymbolKind gain a from_other() factory, and the unit enums are hashable. WASM classes gain copyForTransfer().
  • 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/tags queries. cuda stays held: its upstream parser.c is past the regeneration size threshold and would lose its injections and tags queries.
  • BREAKING (Java, C#): DocstringFormat, StructureKind and SymbolKind are sealed types, not enums. Java uses sealed interfaces with record variants and C# uses abstract records with sealed record variants, so StructureKind.FUNCTION / StructureKind.Function becomes new StructureKind.Function(). Other now carries its string payload, and Java’s values(), getValue() and case-insensitive fromValue() are gone, so match with pattern matching or instanceof/is. The JSON wire format is unchanged. Java ErrorException now extends TreeSitterLanguagePackRsException.
  • BREAKING (Python): cache_dir, clean_cache, configure, download, download_all, download_group, init, manifest_groups, manifest_languages, prefetch and process raise the typed tree_sitter_language_pack.Error subclasses (DownloadError, LanguageNotFoundError, …) instead of RuntimeError, matching get_language. except RuntimeError: no longer catches them. The .pyi stubs also drop the never-present type attribute and TypedDict variants on the kind types and tighten constructor parameter types.
  • BREAKING (WASM): the data and bodySpan setters no longer accept null/undefined. Use the new clearData() and clearBodySpan() instead.
  • BREAKING (PHP, Ruby type signatures): the PHP stubs describe the kind types as classes with a type_tag property, an other property and static factories, not as string constants. The Ruby RBS enum classes become enum_* type aliases. Kotlin Android unit enums now return the wire string from toString() ("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. libc is 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, leaving downloaded_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).
  • BREAKING (Java): enum constants are now UPPER_SNAKE_CASE. StructureKind.Function becomes StructureKind.FUNCTION, and likewise across CommentKind, DataNodeKind, DiagnosticSeverity, DocstringFormat, ExportKind and SymbolKind. 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, DataNode and fifteen other types are now Codable structs with stored properties rather than typealiases to swift-bridge handles. Field access moves from method to property syntax (result.structure, not result.structure()), collections are real Swift arrays, and StructureKind / SymbolKind / DocstringFormat are Swift enums with associated values rather than strings. Values now cross the bridge as JSON and are decoded in Swift, so process() is throws on 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 Codable conformance 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 whose process() threw on any source containing structure. The new test asserts the decoded StructureKind cases and item names, which is what exercises the element decoder; nim cannot cover this (its grammar uses none of the node kind names structure_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 synthesized Codable, which expects {"function": {}} and matched neither form. (Alef 0.87.1)
  • Node and WebAssembly e2e suites read internally tagged FormatMetadata as 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.Is while still reading the detail the native layer produced. (Alef 0.87.1)
  • Alef stays pinned at 0.85.15. 0.86.1 rewrites the Swift binding to real value types – DataNode, ProcessResult and StructureItem become Codable structs with typed properties instead of typealiases to opaque Rust handles – but does not update the Swift e2e suite it generates alongside them, which still calls result.language() and result.structure() on what are now properties. The two halves of its own output do not compile together, and e2e/ 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.yaml moves the release tag onto the same checksummed commit it pushes the branch to, so each branch named a commit already reachable through v<version>; a consumer pinned to one can switch to exact: "<version>" with no other change. release/swift/1.19.0 is retained.
  • Patched an indent-stack underflow in the vendored agda external scanner. VEC_POP is a bare len-- and VEC_BACK reads data[len - 1], so the dedent loop could drain the stack, wrap len to UINT32_MAX and dereference the buffer plus 16 GiB. 1023 or more repeated ' characters – or NUL bytes – crash the parser with SIGBUS. The same scanner also seeded its column-0 sentinel only when deserialize was 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 read data[-1]. A survey of all 186 vendored scanners found both defects only in agda.
  • Dynamically loaded grammars are ABI-checked at load time. load_from_dir called Language::from_raw on a downloaded parser with only a null check, so an incompatible grammar was accepted and failed later at ts_parser_set_language – or, just outside the compatible range, parsed wrongly. The ABI version is now compared against the linked runtime’s MIN_COMPATIBLE_LANGUAGE_VERSION..=LANGUAGE_VERSION and 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.py now maintains alef.toml’s [crates.e2e.registry.packages.zig.platform_hashes] alongside test_apps/zig/build.zig.zon. Nothing had ever populated that table, so it carried five STALE_HASH_REGENERATE placeholders; Alef responds to a placeholder by omitting the .hash line entirely, which is the only reason the manifest had to be declared user_owned in 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 SIGABRT where abort_on_error defaults to 1 and prints ERROR: UndefinedBehaviorSanitizer, neither of which the previous detector matched, so the agda crash above was exactly the shape it let through; any parse killed by a signal now counts. Its sanitizer environment is scoped to the ts-pack invocation as well – as a step-level env: it also reached the shell, python3, find, grep and tee, each running uninstrumented with the runtime LD_PRELOADed into it and exitcode=42 in force, so a diagnosis raised in a helper would have been reported as a scanner finding.
  • Upgraded the tree-sitter runtime to 0.27, moving tree-sitter-language to 0.1.8 in the same step. The two pins are not independent: 0.27 requires tree-sitter-language ^0.1.8, and 0.1.8 relocates the 0.26 WASM shims under wasm/unsupported/ so they no longer compile against the older runtime. The grammar ABI is unchanged – both 0.26 and 0.27 are TREE_SITTER_LANGUAGE_VERSION 15 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-sitter directly must move to 0.27 in the same step. The tree-sitter crate declares links = "tree-sitter", and Cargo permits only one package per links value 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 NDEBUG on wasm32. 0.27’s WASM libc is a documented subset that excludes assert, so a scanner keeping its assertions emitted an unresolved __assert_fail. That became an env module import rather than a link error, and the package then failed to load at runtime with Cannot find module 'env'. Native builds keep their assertions.
  • Dropped this crate’s own WASM libc shims (memchr, strcmp, iswalnum and 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_parseable was 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).
  • wasm32 builds now find their headers under either wasi-sysroot include layout. wasi-libc renamed include/wasm32-wasi to include/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_count now returns tree-sitter’s u32 widened to the usize this crate has always declared, rather than failing to compile against the new runtime.
  • 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, sysml and t32. abl is no longer held back: upstream now defines the outer_join node its highlights.scm references, so it compiles for the first time since the hold was taken. cuda is now the one deliberate hold – upstream’s committed parser.c grew 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_interface highlights overlay from upstream’s reworked query. tree-sitter-ocaml declares one shared queries/highlights.scm for 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# contains assertions now compare a JSON-serialized value instead of Convert.ToString, which reported a type name rather than content for tagged unions such as StructureKind. The PHP e2e runner exports the verified extension path so processes it spawns under -n load the same build.
  • Upgraded rmcp to 3.3.
  • Retained tree-sitter 0.26 and the tree-sitter-language 0.1.7 pin. tree-sitter 0.27 requires tree-sitter-language 0.1.8 and removed its Package.swift, so SwiftTreeSitter cannot follow it; the upgrade also changes this crate’s public API, since Language is 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-sitter floor in alef.toml and realigned test_apps/node’s lockfile with its regenerated manifest, which disagreed and broke pnpm install --frozen-lockfile. Realigned the generated Go e2e module pin, which the version sync leaves behind.
  • 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-language to 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 dirs 7 and zstd 0.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.
  • process() returns a ProcessResult object again in Python, so attribute access works. In 1.16.1 the generator emitted options.ProcessResult as class ProcessResult(TypedDict, total=False), which is a type annotation and not a runtime class – process() therefore handed back a plain dict, and every documented access (result.language, result.chunks, result.metrics.total_lines) raised AttributeError. 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. ProcessResult is 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 37 process tests failed because the emitter wrote String(e.kind).includes("Function") against kind, which is an object – String({ type: "Function" }) is "[object Object]", so every structure-kind assertion evaluated false and reported it as the uninformative expected 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)
  • 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.

@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.
  • 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 the VERSION="${1:-...}" default into its own PINNED_VERSION= assignment and switched both installers’ shell literals from double to single quotes; the checker’s patterns matched nothing, and --fix therefore 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:sync was 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.
  • 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 scoop was held out of the workflow’s available-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.
  • Refreshed 10 grammar pins to their upstream latest: c3, cedar, cedarschema, cfml, dotenv, foam, mlir, nginx, sysml and twig. Parsing behaviour for those languages follows upstream. abl is deliberately held back – upstream’s newer highlights.scm references nodes its own parser.c does 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_abi fell back to the default ABI for any parser it could not read, so running the generator against an incomplete parsers/ tree – what a TSLP_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.
  • 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, and twig. sflog/soql/sosl/apex share one upstream monorepo and therefore one revision. The remaining 344 grammars were already current.

  • Refreshed dependencies. cargo upgrade --incompatible reported all 58 workspace packages already at their latest versions, so only Cargo.lock and e2e/rust/Cargo.lock moved. 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.

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.

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.

  • task alef:sync now runs the full version sync chain. It ran alef sync-versions alone, which reaches the binding manifests but not the registry test-app pins or the plugin version pin. It is also the only sync task task --list-all surfaces, because version:sync lives 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.

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.

  • 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 --check was correct and CI Plugin ran it, but ci-plugin.yaml’s paths: filter did not list Cargo.toml — and a release commit bumps Cargo.toml and usually nothing under plugin/. 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 strand plugin/ a release behind. Being in sync at 1.15.8 was luck: that one commit happened to touch plugin/ too.

    Cargo.toml and .task/version.yml are now in the filter, so any core bump re-runs the gate. And sync_plugin_version.py gains --expect <version>, asserting core and plugin both equal the version being released; publish.yaml’s validate-versions job 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.

  • 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 the E2E gate — Java it sits behind failed first on 8 assertions of the shape expected: <null> but was: <[]>.

    The cause was a disagreement inside each generated Java record. A component backed by a Rust Vec carrying #[serde(default, skip_serializing_if = "Vec::is_empty")] was emitted as @Nullable, and the canonical constructor stored whatever it was handed — null included — while the Jackson builder defaulted the same component to List.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 @Nullable on those components and adds a compact constructor normalizing null to 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 plain Vec, never Option<Vec>, so empty is the truthful representation. They now assert assertEquals(List.of(), ...), which fails on null as well as on a non-empty list — the assertions were tightened, not relaxed to accept either shape. mvn test in packages/java reports 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 each test_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 and alef test-apps run cannot resolve anything. Covers rust, go, python, ruby, dart, elixir and swift; status names 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 every test_apps/ app plus the alef.toml registry pins from Cargo.toml, deliberately independent of alef, so a change in what alef sync-versions is 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. --fix repins everything; --release X checks against the version being published and also catches a Cargo.toml that disagrees with the tag.

    Wired in three places: task version:sync repins after alef sync-versions, the Check version sync CI step gates every push, and the publish workflow’s validate-versions job gates the release itself against the tag. Also exposed as task test-apps:check-pins / :fix-pins.

  • scripts/sync_zig_zon_hashes.py — regenerates and verifies the test_apps/zig/build.zig.zon package 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 is zig fetch <url>, which prints the exact hash Zig will demand. --fix rewrites 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 as task test-apps:check-zig-hashes / :fix-zig-hashes and wired into the ci-zig job — the only job with a Zig toolchain. Because the digest only exists once the release has published its Zig assets, --fix runs after a publish, never during release prep.

  • The publish workflow now refreshes the Zig package hashes itself. A new refresh-zig-package-hashes job runs after publish-zig — the earliest point at which the tarballs the digests are taken over exist — recomputes every hash and commits test_apps/zig/build.zig.zon back to main, modelled on the Swift update-swift-package-manifest job. Catching the drift only made the manual --fix step forgettable again; this removes the step. The job cannot break a release that would otherwise succeed: nothing depends on it, it is continue-on-error, and it commits one file or none. It skips when main has already moved to a version this release did not publish, and reports every outcome — refreshed, already current, skipped, failed — in the run summary, since continue-on-error would otherwise let a failure pass unnoticed. --require-published is 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/zig could not build at all — its five package hashes had been stale since 1.14.3. alef sync-versions repoints each .url at 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 build failed 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-zig builds packages/zig from the working tree and ci-e2e runs e2e/zig, so the one app that resolves these URLs was never exercised. All five are regenerated via zig fetch, and ci-zig now 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 and test_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-versions reaches 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 a WARN naming 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 sync CI step diffs only packages/ and crates/, so test_apps/ drift was outside the gate’s scope even when sync did rewrite it.

  • The test_apps/dart/pubspec.yaml version-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.

  • 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_empty on a non-optional array reached through an optional parent emitted a Bool? into XCTAssertTrue, 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-code returns 0 against the regenerated tree.
  • Generated documentation snippets omit the level: front-matter key instead of emitting level: 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-discord gated 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 requires release-finalize (the job that asserts every enabled target actually published) to have succeeded.

  • publish-wasm now genuinely waits on publish-node. publish-node was listed in its needs: but never restated in its if:; because the job opens with always(), the needs: 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 skipped publish-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 verify reported frozen are now recorded as alef-owned. Every one of them is a create-once seed, so plain alef adopt refuses 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), and packages/go/go.mod plus packages/zig/build.zig.zon gained the marker header. The 17 left frozen all differ from generated output and need a content decision – notably packages/java/checkstyle.xml, whose generated form reverts the ${config_loc} + optional="false" SuppressionFilter fix, and checkstyle-suppressions.xml, whose generated form drops the NativeLib LineLength suppression. alef verify stays red on those 17.

  • task docs:snippets:validate:lang runs again. It invoked alef snippets validate, a subcommand that does not exist (alef snippets has list, check, parse, audit, gaps), so the target could only ever fail with unrecognized subcommand. It now calls alef snippets check --strict --cache off --lang {{.LANGUAGES}}, matching docs:snippets:validate with the per-language filter added. The --snippets path is dropped because check reads [workspace.docs.snippets], and LEVEL is dropped because check has no level flag – each validator picks the deepest level its toolchain supports.

  • alef verify no longer reports 8,415 stale files. alef all ran at ada0fac4e and the 1.15.6 version bump landed after it at e9816afda; alef.toml is 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 the alef:hash: line in ~8,380 files. The remaining diffs are real drift the earlier regen never covered: all of test_apps/* (alef all does not run the test-apps stage – alef test-apps generate is a separate command), plus the packages/*/README.md version strings. Regeneration order is now bump-then-generate; running alef all before alef sync-versions re-opens this.

  • Validate (Lint & Format) no longer fails on every release commit. The job passed python-extra-projects: packages/python to the shared validate workflow, which runs uv pip install -e packages/python – a maturin build of crates/ts-pack-core-py, and so a run of crates/ts-pack-core/build.rs. That job never downloads the language-parsers artifact, so parsers/ is empty and build.rs falls through to the parser-sources-<version>.tar.zst release asset for the version in Cargo.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, so Format check and Lint never ran at all. The input is dropped: its stated purpose (letting pyrefly resolve tree_sitter) was already satisfied by the root dev group in pyproject.toml, and ._native resolves from the checked-in _native.pyi stub, so pyrefly check packages/python reports 0 errors without the wheel being built.

  • Rust snippet validation no longer downloads parser sources. The rust snippet session now sets TSLP_OFFLINE=1. Its cargo check builds ts-pack-core through a path dependency and so runs the same build.rs against the same empty parsers/ tree – the identical 404 on a release commit, one step later in the same job. TSLP_LANGUAGES is unset there, so no grammar is compiled into that check either way; skipping the sources changes nothing it validates.

  • Quick Start’s kotlin-android process and quickstart tabs now import the generated corpus (generated/kotlin-android/process/config_all_python.md and generated/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 with unresolved reference 'io'; that classpath defect was fixed today (651ddbac1), so the stand-ins were deleted. Not re-verified with a session-based alef snippets check run – watch the next CI run for kotlin_android snippet failures.

  • alef.toml now documents next to [workspace.docs.snippets] dirs why docs-site/src/snippets/ cli/*.md stay 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 from dirs – nothing recorded that the gap was deliberate.
  • Quick Start docs import kotlin-android’s download/prefetch_languages.md snippet instead of download_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-in download feature, 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 Image for v1.15.5 failed on both architectures at apk add ... python3=3.12.13-r0: Alpine’s repository only ever carries the current revision of a package, so an exact -rN pin 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-cache fetches the package index over the network at build time, so a digest-identical rust:1.95-alpine3.22 still 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 -L path was right; nothing had ever put a library behind it. binding.go links ${SRCDIR}/.lib/<platform>, .lib/ is gitignored, and the validate job builds no binding packages – so go build ./..., which is what compile-level validation runs, could not link. The go snippet session now has a before hook (scripts/stage_go_native.sh) that builds ts-pack-core-ffi and stages it there, the same artifact ci-e2e.yaml’s Go job restores by hand. timeout_secs went from 120 to 900 because the same budget bounds before hooks, and a cold cargo build does not fit in two minutes.

  • Java snippets are compiled against a built package. All 521 were Unavailable in the same run, which under strict fails the run exactly as a Fail does – has_incomplete_coverage counts Skip, Unavailable and Downgraded. 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 runs mvn compile and mvn dependency:copy-dependencies first. 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 Unavailable with “Cannot find module ‘@xberg-io/tree-sitter-language-pack-wasm’” – the TypeScript validator resolves that module through the manifest’s types field (pkg/nodejs/ts_pack_core_wasm.d.ts), which only exists after a wasm-pack build, and pkg/ is gitignored. The wasm snippet session now runs scripts/stage_wasm_types.sh, which builds with TSLP_LANGUAGES=mojo,nim,norg – all three are wasm32-unbuildable and already skipped by build.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 Unavailable with unresolved reference 'io' – the Kotlin validator resolves its classpath through Gradle’s own task model, which reports compileDebugKotlin’s destinationDirectory whether or not anything has been compiled into it, and nothing had. The session now runs scripts/stage_kotlin_android_sdk.sh, which writes local.properties itself (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 same exclude_functions list [crates.wasm] already has: the 10 download-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 Unavailable with “zig toolchain not found” – the validate job never installed one; ci.yaml now passes setup-zig: true to reusable-validate.yml@v1, which gained the input upstream. That alone was not enough: compile-level zig validation links a real executable (the generated per-snippet build.zig calls b.addExecutable + b.default_step.dependOn), so it also needs ts-pack-core-ffi built at packages/zig/build.zig’s default -Dffi_path (target/release). The session now runs scripts/stage_zig_ffi.sh first, mirroring stage_go_native.sh’s FFI build. All 521 now pass.

  • 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_unknown and get_parser_unknown all catch (TreeSitterLanguagePackException ...), which does not exist in packages/java and is not referenced anywhere else in this repo. Alef derives that name twice and disagrees with itself: the binding emitter uses backends::java::naming::exception_class_name, which suffixes the raw FFI class and yields TreeSitterLanguagePackRsException, while e2e/codegen/java/snippet.rs re-derives it as format!("{simple_class_name}Exception") from the facade class, whose Rs suffix 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 8 Unavailable results and the validate job stays red.

  • CI Sanitize no longer instruments third-party C it cannot link. The job injected the sanitizer flags through CFLAGS_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 is x86_64-unknown-linux-gnu, so the flags also instrumented the C in zstd-sys and ring – both build-dependencies of ts-pack-core, whose objects are linked into its build-script binary. Cargo withholds RUSTFLAGS from host units when --target is passed, so that link never received the -lasan/-lubsan the job adds and died with undefined __asan_* / __ubsan_* symbols while linking build_script_build, before a single grammar was compiled. build.rs now reads TSLP_GRAMMAR_CFLAGS and 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 generate ran ruff for python and pnpm dlx oxfmt for node and wasm. The validate job has neither – it sets up python, java, go, ruby, dart and elixir, and the reusable workflow has no setup-node input – so alef logged skipped -- executable not found, emitted unformatted suites, and git diff --exit-code failed on 45 files that were correct on every developer machine. All three now go through poly, which the job installs before the generate step and which bundles the same engines.

  • The Zig get_language test 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_language on an unknown name returned error.Download, not error.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_parser on the result of new_language_registry() without try; that function returns error{UnknownFfiError}!LanguageRegistry, so both Zig jobs failed to compile.
    • packages/kotlin-android/.../Language.kt was a pre-alef-0.61 handle wrapper. Language is a kotlin_android capsule type, so alef stopped emitting the wrapper, but it does not delete files it no longer generates – the stale class stayed on disk with a close() calling TreeSitterLanguagePackBridge.nativeFreeLanguage, a symbol neither the Bridge object nor the JNI shim declares. Both AAR jobs failed on the unresolved reference.
    • quickstart.mdx still imported wasm/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 with UNRESOLVED_IMPORT; the tab now uses the wasm prefetch_languages snippet.
    • The validate job pinned alef to 0.62.8 while alef.toml moved to 0.62.10, so alef e2e generate && git diff --exit-code graded the committed tree against a different generator and reported 45 files as drift.
    • task swift:e2e:test built only tree-sitter-language-pack-swift. Package.swift links libts_pack_core_ffi.a by absolute path and silently falls back to target/debug when 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, matching ci-swift.yaml.
  • The grammar tables record AL at ABI 15. AL’s committed parser.c is 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 deserialize bounds its reads, and serialize no longer truncates. vec_u32_deserialize never took the length tree-sitter passes to tree_sitter_typst_external_scanner_deserialize. It read an 8-byte element count out of the buffer and then memcpy’d count * 4 bytes with no bound against the serialized length, so any buffer that was shorter or differently framed than the one serialize wrote 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. deserialize now takes the length, bounds the count before it is multiplied (which also stops count * 4 and sizeof(uint32_t) * cap from 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 serialize side, and did it by truncating. That was itself the mechanism that made a misframed buffer reachable from ordinary input: when indentation consumed the budget, containers was 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 as containers’ 8-byte length prefix. Truncation is undetectable to the reader; absence is not. serialize is now all-or-nothing and returns 0 when the state does not fit, which tree-sitter reads as “no state” and which deserialize already 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_pop in the typst scanner reads the last element instead of the one past it. Upstream returns self->vec[self->len--]. The post-decrement indexes vec[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 whenever len had reached cap (which deserialize guarantees, since it sizes cap to exactly len). It is now vec[--self->len], and the empty-vector guard is written as real control flow because the assert() macro in that file expands to while (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.patch carries it; the existing serialize-buffer-overflow.patch keeps its own scope, and its first hunk no longer takes the vec_u32_pop line as context so the two patches apply and reverse-apply in either order.

  • process fixtures name the field they actually assert on. Twenty-one assertions said contains on the bare structure or imports collection 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 collection contains to compare only each item’s name, and since StructureItem.name is the identifier (main) rather than the kind (Function), every one of those assertions began failing — 19 of the Rust E2E suite’s 37 process tests. The fixtures now spell the path out as structure[].kind and imports[].source, which restores the intended check explicitly. process_test goes 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_64 parser 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_64 and both Windows targets were genuinely rebuilt — only linux-x86_64 was stale, which is exactly the platform GitHub #180 was reported on.) The Rust cache restores target/ 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 whatever out/libs/ the cache happened to carry. The build-script output directory is now dropped before the parser build in both publish.yaml and ci-e2e.yaml, so build.rs reruns 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 to uint32_t while vec_u32_deserialize still read it back as sizeof 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 shipped libtree_sitter_typst.so still stores the prefix with a 4-byte movl. That crash took down the Ruby, Python, PHP, C FFI, Dart, Elixir and Go E2E gates.

  • pnpm install succeeds in the WASM e2e suite again, so the WASM package publishes. The last dependency upgrade moved vitest to ^4.1.10 in e2e/wasm/package.json without regenerating e2e/wasm/pnpm-lock.yaml, and CI has frozen-lockfile on by default: specifiers in the lockfile don't match specifiers in package.json. That failed the E2E gate — WASM job, and publish-wasm requires 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.lock files track the workspace version. Carried forward from v1.15.4: the Validate versions gate failed on stale locks under e2e/rust and packages/ruby/ext/ts_pack_core_rb/native, which skipped the crates.io publish job and cascaded into every language build with failed to select a version for the requirement.

Neither v1.15.3 nor v1.15.4 published artifacts to any registry — use this version.

  • The release actually publishes. v1.15.3 was tagged and released but published nothing: the Validate versions gate failed on stale Cargo.lock files under e2e/rust and packages/ruby/ext/ts_pack_core_rb/native, which skipped the crates.io publish job. Every language-package build downstream of it then failed with failed to select a version for the requirement ^1.15.3, because alef publish prepare was 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.
  • The generated Node e2e suite is valid TypeScript again. A fixture whose call returns void and whose only assertion is not_error renders await expect(...).resolves.not.toThrow(), but the it(...) callback it lives in was only marked async when the call was async. For a synchronous call — cleanCache, configure, init, prefetch — that produced await inside a plain arrow function, a hard syntax error. Because alef all formats 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_object asserts is_empty on a Vec<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 tracks skip_serializing_if as a fact distinct from Option<T>-optionality and guards the lookup with orelse .null.

  • Generated e2e tests no longer skip themselves where a not_null assertion was declared. Fixtures whose only assertion was “returns a valid X” rendered as assumeTrue(false, "alef rendered no runnable expectation") — a test that reported green while checking nothing. They now emit a real assertNotNull(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.kind is a DataNodeKind, 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#, _alefE2eText in Dart, _alef_e2e_text in Python, to_string in Elixir, format!("{:?}", ...) in Rust — so the assertion tests the serialised value rather than an accidental Debug/toString match.

  • The C examples free the enum handle they allocate. ts_pack_data_node_kind returns a TS_PACKAlefHandle, not a char *; the generated C now takes the handle, asserts it is non-zero, converts with ts_pack_data_node_kind_to_string, and calls ts_pack_data_node_kind_free. The previous form assigned a handle to char *.

  • The JNI bridge formats errors with to_string() rather than format!("{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 fail attach-javadocs) and the maven-compiler-plugin <excludes>, plus a checkstyle <excludes>**/.alef/**</excludes>. packages/java/pom.xml is 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.

  • DownloadTest is gone from the Android e2e suites. crates.kotlin_android.features does not enable download, so alef now emits an ExcludedBindingsTest of @Disabled cases naming the gating feature instead of tests that could never link.
  • alef pinned to 0.62.8 in alef.toml and in the CI alef-version input.
  • All Rust dependencies taken to their latest versions (cargo upgrade --incompatible followed by cargo update): 70 packages upgraded, one dropped, no downgrades.

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 *DataNode is 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 type String, so the generator emitted ts_pack_string_from_json("[]") and a matching ts_pack_string_free(...) — symbols nothing declares, failing every affected snippet with “call to undeclared function”. The C ABI takes the argument as a plain const char * JSON string, so it is now passed directly: ts_pack_prefetch("[]"). Fixed in alef 0.62.2.

  • StructureItem.kind is no longer typed as optional. The Rust core declares pub kind: StructureKind with #[default] Function; bindings emitted the equivalent of kind: StructureKind | str | None = None, making a required field nullable and defaulting it to the wrong value. It now carries the real default.

  • 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’s v1 tag does not declare the input; v1 does declare it. This is the gate that catches generated examples which no longer compile against the API they document.
  • Parser now implements Debug, reporting the configured language name and parse limits. It could not be derived — the wrapped tree_sitter::Parser is a NonNull newtype with no Debug of its own — so the pointer is elided. Debug-printing a Parser, or a Result<Parser, Error> returned by get_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.toml were not in the generator’s schema; they had previously been discarded in silence, and a move to deny_unknown_fields turned 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-byte uint32_t but read it back as an 8-byte size_t, so deserialization took four bytes of adjacent buffer as the high half of the element count and memmoved that many elements off the end of tree-sitter’s fixed 1 KiB serialization buffer. The result was a SIGBUS at a different address on each run. The prefix is now size_t on 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 m grammar rule and its (m) @keyword highlight in one commit without re-running tree-sitter generate, so the committed parser had no m node 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 GOARCH spellings (linux-amd64, linux-arm64) while binding.go’s cgo LDFLAGS and cmd/setup both look for alef’s labels (linux-x86_64, linux-aarch64) verbatim. Only macos-arm64 happened to line up; every other platform required running cmd/setup first.

  • The Java JAR threw UnsatisfiedLinkError on every ARM64 Linux and Windows host. The loader resolves natives under linux-aarch64 / windows-aarch64, but the build matrix packaged them as linux-arm64 / windows-arm64, so the JAR contained no directory the loader would look in and it fell through to System.loadLibrary. macOS ARM was unaffected because the loader special-cases it.

  • The Swift package pointed at the previous release’s binary artifact. Package.swift’s binaryTarget URL is rewritten to the current version on each release, but it was left on v1.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.

  • The Language handed 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 'static and 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.rs downloads returned 404 (#177). The draft-publish step also gated on nothing, because the job opened with if: 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.

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.

  • PackConfig.cache_dir is a base directory, not the final library path. The crate now appends tree-sitter-language-pack/v{version}/libs to it, exactly as it does to the platform default, so cache_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 set cache_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_candidate had an unreachable preference branch, so for grammars vendored from a monorepo the winner was decided by filesystem traversal order: cfml took all five of its query files from sibling grammars, psv and tsv took CSV’s, and dtd took XML’s. leo took its locals from m68k and its indents from 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, soql and sosl now 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: templ still receives Go’s highlights and tags, 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) and scala (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 returning None.
  • 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 tracing spans 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_query returned an error for dart and php tags: both hand-written overlays named node types their grammars do not have, and nothing ever compiled them. ocaml_interface highlights 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.doc is 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.

  • DownloadManager resolves 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 though nativeDownloadAndCacheLibrary() 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 relative dlopen), and names the real environment variable in its error message instead of printing the identifier $nativeLibDirEnv literally. Fixed upstream in alef 0.55.6.

    Behavior change: an unresolvable native now throws a descriptive StateError naming the asset URL and the download command, where it previously returned null and let flutter_rust_bridge attempt its own relative-path dlopen.

  • Windows parser binaries build. The UTF8PROC_STATIC fix 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 compiles utf8proc.c straight into it. MSVC therefore still rejected all 183 scanner grammars with error C2491, and the 1.14.2 release produced no windows-x86_64 or windows-aarch64 parser binaries (#174).
  • CI builds the parser binaries on windows-x86_64 and windows-aarch64 on 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.
  • Windows artifacts build again. utf8proc.c is compiled into a static archive, but UTF8PROC_STATIC was never defined, so on MSVC utf8proc.h declared every utf8proc_* 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 no windows-x86_64 entry 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 a std::ts_pack_iswspace that 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 in namespace std, and norg — 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 env import. Most tree-sitter scanners include <wchar.h> themselves, and wasi-libc’s copy unconditionally redefines iswdigit and friends as trampoline macros, silently clobbering the shim’s redirects and leaving a reference to a symbol that does not exist on wasm32. It surfaced as Cannot 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.json still pointed at the emptied packages/php/src, so every PHP test failed with a missing-class error. It now resolves to crates/ts-pack-core-php.
  • Restored formatting of a generated Go source file and docs-site/pnpm-workspace.yaml, and scoped poly lint to skip whole-project linters whose toolchains CI does not install.
  • Ruby binding no longer exports generated types into the global Object namespace (issue #173, the Parser collision with the parser gem); generated types stay namespaced under TreeSitterLanguagePack.
  • 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. Pinned tree-sitter-cli to 0.26.11 in CI.
  • 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 just is re-sourced to the canonical casey/tree-sitter-just.
  • Detect kitty (kitty.conf), rsHTML (.rs.html), and dotenv (.env) files by name.
  • The core library emits tracing spans and events always-on across parsing, grammar loading, and download operations, making observability a first-class product surface. tracing is 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.
  • The ts-pack CLI installs a tracing subscriber for every command (previously only the MCP server did), so RUST_LOG surfaces library diagnostics on stderr for all subcommands; machine- readable result output on stdout is unchanged. tracing/tracing-subscriber are no longer gated behind the mcp feature.
  • Raw println!/eprintln!/print!/eprint!/dbg! are denied in production code across the workspace (clippy print_stdout/print_stderr/dbg_macro); tracing is the sole diagnostic surface. The ts-pack CLI’s command results and prompts remain its stdout/stderr output contract (opted in crate-wide), while the MCP command’s diagnostics continue through tracing.
  • Regenerate all language bindings on alef 0.49.0.
  • 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.template so runtime.json can 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.json from its template before packing the NuGet package so C# runtime metadata is published correctly.
  • Regenerate all language bindings on alef 0.48.2.
  • Update dependencies to their latest compatible versions.
  • Remove unused Java PMD ruleset and stale linter configuration.
  • 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#parse self-conversion TypeErrors from loading an extension compiled against a different Ruby ABI. Fixes #171.
  • 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, Ruby Gemfile.lock).
  • Regenerated all bindings with alef 0.42.0.
  • ruby: the Magnus binding now exposes the Parser instance methods (set_language/parse/parse_bytes/reset). alef previously skipped &mut self methods on opaque types, so Parser was generated with no callable methods; parse_bytes also now accepts a Ruby String. This fixes the root cause behind #168 at the generator level (alef 0.38.4), superseding the hand-edit stopgap in #169.
  • build: regenerate all bindings against alef 0.38.4. Swift and Elixir generated output pick up the 0.38.4 codegen fixes (Swift CodingKeys honoring 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.
  • 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 nil and pre-encoded JSON strings as-is instead of double-encoding, matching the keyword-arg path (alef 0.38.0).
  • build: regenerate all bindings against alef 0.38.0. Includes an improved Dart native-library loader (env-var override, versioned user cache, explicit errors).
  • 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.0 RubyGems 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 freezed to the stable ^3.2.5 constraint instead of a ^4.0.0-dev prerelease. The prerelease was rejected by flutter_rust_bridge_codegen’s dependency validator, which broke the Dart binding build.
  • node: build and publish the linux-x64-musl and linux-arm64-musl platform sub-packages. CI now cross-compiles both musl targets (via zig / cargo-zigbuild), and the main package’s optionalDependencies reference the real version instead of the 0.0.1 placeholder, so the native binaries resolve on Alpine/musl systems.
  • elixir: generate typed e2e configs. Fixes #167.
  • ci: the sdist smoke test uses the tree_sitter property API (root_node/type).
  • grammars: refresh 39 grammar revisions to newer upstream commits (no languages added or removed).
  • deps: bump rmcp to 2.2, go-tree-sitter to v0.25.0, @napi-rs/cli to ^3.7.3, @types/node to ^26.0.0, and vitest to ^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.
  • build: build.rs no longer forces spurious rebuilds when the crate is consumed from a registry (crates.io) rather than a git checkout. It skips cargo:rerun-if-changed for the query-overlays directory when it does not exist, skips rerun triggers for parser directories that live under OUT_DIR, and falls back to CARGO_MANIFEST_DIR when 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.
  • python: get_parser() now returns the installed tree_sitter.Parser instead of a vendored parser class. On Python 3.14 the vendored parser produced unusable trees — nodes exposed no type/children and parse(bytes) raised TypeError — while nothing failed at construction, so the breakage was silent. get_parser(name) now builds tree_sitter.Parser(get_language(name)), mirroring get_language() and working across Python versions. Fixes #157.
  • 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, getrandom moves to 0.4 with the wasm_js feature.
  • Regenerated all bindings with alef 0.30.10, which corrects the generated Python get_parser return annotation (previously an invalid _rust.Parser that broke import on Python < 3.14).
  • wasm: skip the tmux grammar on wasm32. Its generated parser.c is ~30MB — under TSLP_WASM_MAX_PARSER_BYTES (40MB) so the size gate doesn’t catch it, but it still overruns the clang wasm backend during codegen. Added to DEFAULT_WASM_SKIP_GRAMMARS so the wasm build is clean; it degrades gracefully (absent from STATIC_LANGUAGES).
  • wasm: enable the getrandom js feature for wasm32-unknown-unknown. Build-dependencies (ureq) transitively depend on getrandom, which requires the js feature on that target; without it wasm32 builds failed with “the wasm*-unknown-unknown targets are not supported by default”.
  • build: build.rs now reports failed_languages.txt write errors separately (via eprintln) and always surfaces the underlying grammar compilation error even when the write fails.
  • 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 than has_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 .scm query once and caches the Arc<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_query now expose the bundled indents.scm / folds.scm (harvested from the grammar bundle alongside the existing kinds); QueryKind gains Indents and Folds.
  • performance: get_language now 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.
  • python: get_language() (and other download failures) now surface the public exception type in tracebacks — tree_sitter_language_pack.DownloadError instead of _native.DownloadError (#147). Regenerated against alef 0.30.1.
  • java: generate ByteArraySerializer.java so the generated ObjectMapper compiles for every package (alef 0.30.1).
  • docs: correct stale “300+” language counts to “306”.

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 generate with 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.
  • Kotlin/JVM: Tree.walk() (and other handle-returning calls) no longer crash the JVM. Opaque handle types (Tree, Node, TreeCursor) crossed the JNI boundary as String/JSON while the Rust shim returned a raw jlong, so the JVM dereferenced a primitive as an object reference and faulted with EXCEPTION_ACCESS_VIOLATION. Regenerated with alef 0.27.1, the kotlin-android bridge now returns primitive Long handles (required and optional, via a 0L sentinel) 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 the DownloadError exported from the package, so except DownloadError: never caught it. Regenerated with alef 0.27.1, the native variants derive from the native base Error and the package re-exports the native classes (with matching type stubs), so except DownloadError:/except Error: work. Fixes #147.
  • wasm32 builds no longer OOM on pathologically large grammars. Compiling the bundled grammars to wasm32 previously included every parser.c, but a few are huge generated sources (e.g. abl at ~130 MB) that need 18-25 GB+ of clang RAM at any optimization level — a single one OOMs standard ≤16 GB CI runners (serialization via CARGO_BUILD_JOBS=1 cannot help when one file alone exceeds the budget). build.rs now skips any grammar whose parser.c exceeds a size limit on wasm32 (default 40 MB, configurable via TSLP_WASM_MAX_PARSER_BYTES; 0 disables the gate), emitting a cargo:warning per skipped grammar plus a summary. Skipped grammars are absent from STATIC_LANGUAGES (no dangling FFI symbol) and degrade gracefully at runtime. The 40 MB default keeps every common language (including the ~40 MB sql grammar) 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 the v<version> tag and never created that branch — so SwiftPM could not resolve the package and the Swift test-app failed with an empty TreeSitterLanguagePack target. The checksum commit is now also pushed to refs/heads/release/swift/<version>. (.github/workflows/publish.yaml)

  • ts-pack mcp server 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-made analyze-code prompt drives a structure/imports/symbols analysis workflow; and language-name arguments autocomplete against the available-language catalog.
  • ts-pack mcp tools are now fully aligned with the CLI and carry accurate rmcp annotations. The download tool takes groups (multiple) and fresh like ts-pack download; process gains the all flag; the combined cache tool is split into cache_dir (read-only) and clean_cache (destructive), mirroring the CLI. Every tool now declares correct open_world_hint, read_only_hint, destructive_hint, and idempotent_hint values.
  • The CLI ships the MCP server by default. mcp is now a default feature of ts-pack-cli, so ts-pack mcp is present in every distribution — cargo install ts-pack-cli, the prebuilt release binary, Homebrew, and the @kreuzberg/ts-pack-cli / ts-pack-cli npx/uvx proxies. Previously the feature was opt-in and absent from shipped binaries, which also broke the marketplace plugin launcher that invokes ts-pack mcp.
  • Host-native get_language() passthrough now works for Swift, Kotlin-Android, and Java. The capsule passthrough (#143) returns the ecosystem’s native Language, but the bindings did not wire the host-runtime dependency: Swift generated an uncompilable forwarder (wrong return type, missing import SwiftTreeSitter); Kotlin-Android declared ktreesitter as implementation, hiding the Language type from callers’ compile classpath; and Java’s jtreesitter (Panama FFM) dlopens the standalone libtree-sitter runtime, which CI and the test harness did not provision. Fixed via alef 0.26.1 (Swift/Kotlin codegen) plus libtree-sitter provisioning in CI and the Java test harness. Zig already wired its tree_sitter module correctly.
  • .app.src files now map to Erlang. The application-resource template is Erlang term syntax, but single-extension lookup only saw src. A compound-extension table now resolves *.app.src to the Erlang grammar.
  • Regenerated all bindings with alef 0.26.0. Picks up alef’s sync-versions byte-stability fix: sync-versions no 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).
  • 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 break cargo doc in the binding crates with rustdoc::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.
  • 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, and TreeCursor.node freed the returned native handle in a finally block immediately after wrapping it, so the returned Tree/Node/TreeCursor referenced already-freed memory and the next native call dereferenced it and crashed. The wrapper now owns the handle and frees it once on close(). 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.
  • 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-style to ktfmt, switch alef.toml kotlin 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 Language passthrough across the C-ABI binding family (#143). get_language now returns each ecosystem’s native tree-sitter Language instead of an opaque alef handle, so the result drops straight into the host runtime’s parser: Go (*tree_sitter.Language via go-tree-sitter), Zig (?*const tree_sitter.Language via zig-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] in alef.toml; regenerated against alef 0.25.55.
  • Regenerated all bindings against alef 0.25.55. The C FFI crate now takes a direct tree-sitter dependency so the capsule shim can name tree_sitter::ffi::TSLanguage (the pointee it casts value.into_raw() to), and the zig build.zig.zon carries the resolved zig-tree-sitter content hash.
  • Swift: restored the public getLanguage(name:) function in the TreeSitterLanguagePack module. An alef 0.25.38 codegen regression added opaque types to the Swift forwarder exclusion set, dropping get_language (the only free function returning the opaque Language type) from the generated public API in v1.9.0. Regenerated against alef 0.25.43.
  • Bumped alef pin 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 $_free is synthesised for handle types with no visible methods (e.g. Language), swift streaming-owner already_declared re-declaration, java marshal_optional_bytes template registration, and java @Nullable type-use placement on qualified types.
  • Upgraded all dependencies to their latest versions (cross-major). Ran task upgrade across 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.json from the oxfmt pre-commit hook so it no longer fights the ai-rulez-generate hook.
  • Java: dropped throwing UnsupportedOperationException stubs for Self-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 true default for boxed @Nullable Boolean #[serde(default)] record fields. A non-optional #[serde(default)] bool = true field is boxed to @Nullable Boolean, so JSON that omitted it deserialised to null and the accessor returned null instead of true.
  • Bumped alef pin 0.25.24 → 0.25.28. Regenerated all bindings via task alef:generate. Picks up alef 0.25.25–0.25.28: scaffold excluded_default_features for 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/swift RustBridgeC import + Vec skip on already-declared types, docs heading demotion, e2e/codegen typescript SsrfPolicy.denyPrivate=false for 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_mismatch against builds.hex.pm. Switched test-elixir jobs in ci.yaml and ci-e2e.yaml to xberg-io/actions/setup-elixir@v1 wrapper which routes through cdn.hex.pm to bypass OTP 27.2 TLS cert-chain rejection against builds.hex.pm.
  • ci.yaml test- jobs 404 race on parsers.json.* Mirrored ci-e2e.yaml’s build-e2e-bundles job into ci.yaml and added TREE_SITTER_LANGUAGE_PACK_MANIFEST_URL manifest 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 of parsers.json would 404; bundling parsers locally and exporting the manifest URL avoids the race.
  • Bumped alef pin 0.25.20 → 0.25.24. Regenerated all bindings via task alef:generate. Picks up alef 0.25.21–0.25.24: dart e2e setEnv robustness, swift already_declared opaque-handle class triples, java PMD/palantir-java-format compliance, C FFI e2e download_ffi.sh derives FFI_PKG_NAME from lib_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 emitted osx-aarch64/linux-aarch64 (a JNA/LWJGL-style convention) while the published JAR’s natives/<rid>/… directory is named via go_java_platform() (macos-arm64, linux-aarch64, windows-x86_64). Result: every macOS-arm64 client failed with UnsatisfiedLinkError because natives/osx-aarch64/libts_pack_core_ffi.dylib does not exist (it’s at natives/macos-arm64/). Loader template now matches go_java_platform() naming.
  • Elixir download NIFs unregistered in precompiled binary (rc.53 regression). The rustler NIF crate Cargo.toml had no [features] table — only a [lints.rust] check-cfg reference to download. Default precompiled CI builds therefore stripped the download/cache/init/configure NIFs from the cdylib, producing :nif_not_loaded errors on every TreeSitterLanguagePack.Native.download/*, cache_dir/0, clean_cache/0, init/0, configure/1, downloaded_languages/0 call (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_devicetree and smoke_ocamllex exceeded the default 30 s test timeout on first load. Raised testTimeout to 60 s, hookTimeout to 120 s.
  • C FFI E2E 404 race in ci-e2e.yaml. e2e/c/download_ffi.sh pinned 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 honours ALEF_FFI_LOCAL_DIR env override to skip the network fetch and consume pre-staged headers/libs. ci-e2e.yaml/test-c-ffi is now needs: build-ffi and stages the locally-built artifact via the override.
  • Bumped alef pin 0.25.18 → 0.25.20. Regenerated all bindings via task alef:generate. Picks up:
    • 0.25.19 — magnus binding Cargo.toml [features] block (fixes rc.52 Ruby gem build under -D warnings), elixir NIF Cargo.toml [lints.rust] ordering (fixes CI Check version sync), ruby Rakefile yard-coverage hook, FFI opaque-pointer call-site .clone() for service-API codegen, binding_excluded field fallthrough that preserves bespoke core Default::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.rs modularization under 1000-LOC cap.
  • Ruby gem publish (rc.52 regression). All four Build Ruby gem matrix jobs failed at rc.52 — the magnus binding’s Cargo.toml lacked the [features] table forwarding download to the core crate, so 18× #[cfg(feature = "download")] arms triggered error: unexpected cfg condition value: download under -D warnings. The skipped Publish Ruby gems step meant rc.52 never reached rubygems.org (test-apps:ruby failed with Could not find gem 'tree_sitter_language_pack ~> 1.9.0.pre.rc.52'). Picked up via alef 0.25.19.
  • CI Check version sync red on main. The elixir NIF Cargo.toml emitted [lints.rust] before [dependencies]; consumers’ prek run --all-files runs 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-package job in .github/workflows/publish.yaml used download-artifact@v8 with merge-multiple: true, flattening every dart-native-<rid> artifact’s contents directly under dart-natives/. The subsequent RID inference (basename "$(dirname "$f")") then resolved to the literal string dart-natives for every file, causing all four native libraries to be skipped with Warning: unrecognized rid 'dart-natives'. The published rc.52 pub.dev tarball contained no lib/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: drop merge-multiple: true so each artifact extracts to its own dart-natives/dart-native-<rid>/ directory, and derive the RID by stripping the dart-native- prefix from the artifact directory name.
  • Bumped alef pin 0.25.15 → 0.25.18. Regenerated all bindings via task 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_patterns allow at crate root, zig test sequencing to avoid clean_cache race, node smoke timeout for vb.
    • 0.25.18 — swift cfg-gated extern blocks for DownloadManager (#[cfg(feature = "download")]), dart absolutize env + Platform.script paths in hardened runtime loader, node slow-grammar list extended (earthfile, perl), pyo3 + napi emit binding-side wrapper structs for [workspace.opaque_types] entries without capsule_types override, napi binding Cargo.toml [features] block (default = ["download"]), cleanup detection tightened from loose by alef substring to specific auto-generated by alef, cbindgen autogen_warning updated so generated C headers are correctly identified as cbindgen-owned.
  • Python get_language / get_parser API consistency (#141). Both helpers now return the binding’s own native types (Language, Parser) instead of the standalone tree_sitter package’s Language, matching every other binding (Java, Go, Swift, Ruby, C#, etc.). Dropped pip_dependencies = ["tree-sitter>=0.23"] from the Python package. Breaking change for callers that relied on tree_sitter.Parser(get_language(name)) — use get_parser(name) directly, or import tree_sitter as a separate dependency.
  • Node getLanguage / getParser API consistency. Same shape change as Python — getLanguage now returns the native Language class from @kreuzberg/tree-sitter-language-pack rather than passing through to the upstream tree-sitter npm package. Dropped the tree-sitter devDependency from the e2e harness.
  • Swift DownloadManager build error. swift-bridge proc-macro previously failed with no 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 setUpAll with “relative path not allowed in hardened program”. Loader now absolutizes env-var and Platform.script-derived paths before constructing search roots.
  • Node smoke timeouts on earthfile and perl. Tree-sitter grammars with heavy scanner.c logic now receive the 90000ms slow-grammar timeout (previously only vb was covered).
  • Python: standalone tree-sitter package dependency. No longer required by tree-sitter-language-pack. Install it separately if you need the upstream API.
  • Node: e2e/node/tests/capsule_passthrough.test.ts and tree-sitter devDependency. Obsolete now that node getLanguage returns the native type.
  • Bumped alef pin 0.25.14 → 0.25.15. Regenerated all bindings via task 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.51 propagated via task alef:sync.
  • Bumped alef pin 0.25.11 → 0.25.14. Regenerated all bindings via task 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 where tree-sitter-language-pack-java-1.9.0-rc.49.jar was published without classifier (missing /natives/{rid}/libts_pack_core_ffi.dylib), causing UnsatisfiedLinkError at JNI init.
    • 0.25.12 — (dart): copy .framework directories recursively in publish workflow assemble step. The find predicate matched only *.so/*.dylib/*.dll files and skipped macOS .framework/ bundles; pub.dev rc.49 was missing tree_sitter_language_pack_dart.framework/.
    • 0.25.12 — (php): always stage PIE extension as .so on Unix. PIE 1.4.5 probes for .so on 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 = [...]. Prevents error[E0425]: cannot find type 'DownloadManager' in this scope on 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 via features = [..., "download"].
  • Bumped tslp 1.9.0-rc.49 → 1.9.0-rc.50 propagated via task alef:sync.
  • Bumped alef pin 0.25.9 → 0.25.11. Regenerated all bindings via task alef:sync + task alef:generate. Picks up the accumulated 0.25.10 and 0.25.11 fix series:
    • 0.25.10 — publish prepare canonicalize bug. publish prepare was canonicalizing only manifest_dir.join("Cargo.toml"), not manifest_dir itself. With current_dir(manifest_dir) set on the cargo subprocess, the --manifest-path ./packages/elixir/.../Cargo.toml argument resolved relative to the new cwd — effectively doubling the path — and cargo bailed with manifest path '...' does not exist followed 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 canonicalizes manifest_dir itself at the top of the regenerate branch and manifest_abs in rewrite_binding_path_deps.
    • 0.25.10 — kotlin-android copyHostJni always reads workspace target. Drop the configuration-time if (workspaceTarget.exists()) selector that evaluated before cargo build finished, eliminating the UnsatisfiedLinkError cascade 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_binding now use resolve_type_path against a build_type_path_lookup(api) map instead of core_enum_path_remapped, fixing E0433 cannot find ImageOutputFormat in crate 'kreuzberg' for enums defined outside the crate root.
    • 0.25.10 — Swift From<core> arms cfg-gate variants. emit_enum_wrapper now 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-in Properties/AssemblyInfo.cs.
    • 0.25.10 — Visitor result routes bare strings to Custom when multiple string-payload variants exist. Fixes silent fallback to the default variant when an enum has both Custom(String) and Error(String).
    • 0.25.10 — FFI visitor context emits enum-typed fields as i32 discriminant. Closes the ArrayIndexOutOfBoundsException cascade in VisitorBridge.decodeContext caused by reading the low 4 bytes of tag_name pointer 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_cfg now normalizes whitespace + handles the any(test, feature = "X") sibling form; check-cfg allow-list populates from every EnumDef.variants[*].cfg instead of falling back to the single-entry cfg(frb_expand) form.
    • 0.25.10 — Release task: task set-version handles prerelease versions when updating ALEF_REV + Ruby Rakefile template documents GEMSPEC constant 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 eliminating unexpected_cfgs without an allow-list. Shared collection logic extracted to src/codegen/cfg.rs. WASM backend now delegates to it. Swift emit_enum_wrapper emits 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.sh now calls brew trust "$TAP" || true before brew 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 from lib/src/native/<rid>/. Drops the structural HTTP 404 against releases/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 the HEADER_PATH/LIB_PATH short-circuit that elided the header dependency whenever a stale prior-rc header was on disk. Per-version marker ffi/.alef-ffi-version keeps unchanged trees from paying network cost. Resolves the rc.48 C test_app unknown type name 'TS_PACKDataNode' cascade.
    • 0.25.11 — C# scaffold: SDK-generated AssemblyInfo with explicit version stamps and full RID list. Enables <GenerateAssemblyInfo> (drops false suppression), stamps <AssemblyVersion>/<FileVersion> as 4-component numeric (MAJOR.MINOR.PATCH.0) via new to_dotnet_assembly_version helper, 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 processor cascade.
    • 0.25.11 — Zig _error_with_message dispatches 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 a if (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.
  • Bumped tslp 1.9.0-rc.48 → 1.9.0-rc.49 propagated via task alef:sync.
  • Bumped alef pin 0.25.6 → 0.25.9. Regenerated all bindings via alef 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. NativeLib downcall fallback chain now emits in the single-line shape that palantir-java-format produces, so the regenerated NativeLib.java no longer triggers a post-regen formatter rewrite (the diff that broke rc.47’s Validate Lint & Format job).
    • 0.25.6 — publish vendor cargo --locked paradox. alef publish prepare was running cargo update -p <crate> with --locked passed via CARGO_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.rs now .env_remove("CARGO_BUILD_LOCKED") before both cargo invocations and drops the --locked flag from cargo metadata. Regression test scrub_lock_succeeds_for_non_workspace_binding_crate_with_incomplete_seed covers the path.
    • 0.25.8 — Dart mirror enum cfg-strip. emit_mirror_enum no longer propagates variant.cfg into the generated mirror enum body. The mirror is a DTO/wire type that flutter_rust_bridge_codegen references unconditionally from frb_generated.rs — gating a variant out via #[cfg] left the unconditional reference dangling with E0599 no variant named 'Heif' found for enum 'ImageOutputFormat' when the binding crate didn’t declare the upstream feature. The catch-all _ => unreachable!() arm in the From<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_close helpers, tripping -D warnings on all 4 alef publish jobs (3× Build CLI + crates.io). Also widens the dart check-cfg allow-list and tightens publish current-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 NIF Cargo.toml (cargo refused with “patch points to the same source”). Direct deps with = constraints + matching package.metadata.cargo-machete.ignored entries pin alloc-no-stdlib/alloc-stdlib/brotli-decompressor transitively. YARD doc-coverage hook fixed via a documented GEMSPEC constant in the generated packages/ruby/Rakefile and a matching docstring in the in-tree stale packages/ruby/ext/ts_pack_core_rb/src/Rakefile (the latter file is hand-maintained and not regenerated; cleanup deferred).
  • Bumped alef pin 0.25.1 → 0.25.2. Picks up two source-build publish-prepare fixes:
    • publish prepare now strips workspace-member [[package]] entries from the seeded Cargo.lock before per-member cargo update -p. Without this strip the path-source seed entry collides with the rewritten registry-source dep and cargo metadata --locked validation fails.
    • publish prepare disambiguates the per-member cargo update -p spec by using the full registry+https://github.com/rust-lang/crates.io-index#NAME@VERSION package 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).
  • 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.json regenerated.
  • Bumped alef pin 0.25.0 → 0.25.1. Picks up the assertions.rs:227 C-e2e codegen hardening (panic-on-missing-fields_c_types rather than the silent PascalCase fallback that produced TS_PACKData instead of TS_PACKDataNode in 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 (recent brotli-decompressor 5.0.1) can no longer silently override the committed lockfile during CI.
  • publish-release: normalise parser library names across platforms in the parsers.json manifest generator. Linux/macOS produce libtree_sitter_<lang>.{so,dylib}, Windows produces tree_sitter_<lang>.dll (no lib prefix). The Generate parsers.json manifest step 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 the lib prefix and tree_sitter_ / tree-sitter- prefix uniformly and reverses the four c_symbol overrides (c_sharp/embedded_template/nu/vb_dotnet) so the per-platform sets agree on language identifiers.
  • alef.toml: declare process_result.data → DataNode in [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 produced TS_PACKData instead of the actual cbindgen-emitted TS_PACKDataNode. Adding the explicit mapping makes the regenerated e2e/c/test_data_extraction.c reference TS_PACKDataNode* and the ts_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 ../alef and pending an alef release.
  • build.rs: allow clippy::type_complexity on the MSVC patches table. The &[(&str, &str, &[(&str, &str)])] shape introduced for crystal/sml MSVC compat tripped clippy -D warnings in CI; the table reads cleanly as a literal and isn’t worth a type alias.
  • rust-max-lines pre-commit cap: exclude crates/ts-pack-core/src/intel/data_extraction.rs. New 1322-line module added for hierarchical data extraction; remediation backlog entry.
  • publish-release: read .tar.zst parser archives via zstandard. Python’s tarfile.open(path, 'r:*') only supports .gz, .bz2, .xz, and uncompressed .tar — not .tar.zst. The Generate parsers.json manifest step was failing with tarfile.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 with Failed to fetch manifest from .../parsers.json: http status: 404. The step now pip install --user zstandard then opens each archive via ZstdDecompressor().stream_reader() and tarfile.open(fileobj=..., mode='r|').
  • Regenerated against released alef 0.25.0. Picks up the new Extension trait surface (per-extension TOML config + transform_emitted_files hook), Swift target-specific core dependency overrides, the zig _first_error → contextual error fix, and the Dart hardened-runtime framework load fix. Restores crates/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.
  • JNI codegen: &[&str] core params no longer fail E0308. The JNI function/method shims emitted &names for Vec<String> slots, which coerces to &[String] but not &[&str]. Core fns declared as &[&str] (e.g. download(&[&str])) failed to compile with expected reference &[&str], found reference &Vec<String>. Alef now consults the IR vec_inner_is_ref flag to materialise a Vec<&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.
  • 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 named file: because a cache_dir value of the form file:/tmp/… was interpreted as a relative path rather than a URI. The : is illegal in Windows paths, so every actions/checkout step on Windows failed with invalid 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 .gitignore now blocks the pattern (test_apps/*/file:/) alongside the existing test_documents/file:/ guard.
  • Hierarchical data extraction for 17 data-format languages (#136). Set data_extraction = true on ProcessConfig to extract a nested DataNode tree 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::Jni in alef test-apps run, enabling CI/CD verification without Android emulator. Requires alef 0.24.14+.

  • Alef pin bumped 0.24.10 → 0.24.14. Pulls in the JNI run-default split (host-JVM gradle runner replaces the Ffi | Jni no-op), JNI return marshalling for raw String / Option<String> returns (no more JSON-encoded "\"python\"" surfacing in Kotlin), Kotlin test emitter loadLibrary respecting [crates.ffi] prefix (resolves ts_pack_jni instead of the literal crate name), and the Kotlin assertion emitter switching list-contains checks to a case-insensitive toString().lowercase().contains(...) shape that mirrors the Java emitter.
  • release-finalize job guards Finalize release on prepare success. The job ran with if: always() and unconditionally invoked finalize-release@v1, which errors with INPUT_TAG is required whenever prepare’s tag output is empty (cancelled or failed prepare). Result: a cancelled rc.31 surfaced as a confusing Finalize release: failure on top of the actual upstream cancellation. Now if: needs.prepare.result == 'success'. rc.31 publish run 27214336783.
  • PHP test_apps/install.sh verifies extension load via extension_loaded() rather than parsing php -m output. When the PIE-installed extension was already loaded through the global php.ini, an explicit php -d extension=... invocation caused PHP to emit Module already loaded to 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 to php -r 'exit(extension_loaded("...") ? 0 : 1);' so the check is decoupled from PHP’s logging and tolerant of double-loading.
  • ts-pack-core-ffi regen 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-node regen emits zero rustdoc warnings. The previous regen left Vec<u8> and Array<number> bare in JsBytes doc comments, which rustdoc parsed as unclosed HTML tags. Pulled in via the alef 0.24.2 bump.
  • test_apps/zig/build.zig.zon URLs 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, …), so zig fetch 404’d. The alef 0.24.2 bump switches both sides to Rust triples; tslp’s alef.toml [crates.e2e.registry.packages.zig.platform_hashes] keys updated to match. Reverts the simple-arch direction taken in rc.31.
  • 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 / copyHostJni Gradle tasks guarded by alef.skipHostJni), Go scaffold module_major parameterization that lets non-kreuzberg consumers configure their packages/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.rs added to the rust-max-lines exclude list (1081 LOC > 1000-line ceiling). Joins the existing remediation backlog of large files awaiting split.
  • 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.kts emitters. v0.7.0 referenced JvmVendorSpec.IBM_SEMERU, which Gradle 9.0+ removed (renamed to IBM); Gradle 9.5.1 hosts failed at project-evaluation with Class 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) matching build.zig.zon URL templates. Previously RustTarget::platform_for(Language::Zig) returned the rust triple, so alef publish package --lang zig --target … emitted …-aarch64-apple-darwin.tar.gz but 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 fetch then 404’d.
  • Stage Go FFI libraries step uses git add -f. Root .gitignore globally ignores *.so/*.dylib/*.dll/*.lib, so the plain git add silently refused to stage the downloaded FFI artifacts under packages/go/.lib/. xargs propagated the (silent) failure as exit 123, failing the step before the packages/go/v<version> subtree tag could be pushed. Added -f so 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@v1 receives the publisher-app token as an action input on all 4 cross-repo-write call sites. The shared action sets GH_TOKEN inside its own composite step from inputs.token (default github.token), so a step-level env: GH_TOKEN: … on the calling job had no effect — uploads ran with the read-only default GITHUB_TOKEN and hit HTTP 403: Resource not accessible by integration. Now passes token: ${{ 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 declares permissions: contents: write. Fixes rc.29 publish run 27192809836 Upload Go FFI 403.
  • Pulls in xberg-io/actions v1.8.49 retry-on-SSL upload fix. publish-github-release/scripts/upload_artifacts.py now retries 5× with exponential backoff on URLError / ssl.SSLError / ConnectionError / TimeoutError / HTTP 5xx. rc.29 parser-sources bundle upload hit a transient ssl.SSLEOFError mid-upload on a 30 MB asset and cascaded to ~15 dependent failures (skipping publish-crates, which broke every PHP/Ruby/Elixir/Python-sdist cargo generate-lockfile against the unpublished workspace member); the retry absorbs the SSL race.
  • 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.rs now wires __ext_php_rs_module_startup into 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.ts no longer prematurely closes the JSDoc block around DocstringFormat::JSDoc / ::JavaDoc variant docs), and a sweep of Java/Kotlin/Zig/Dart formatting normalization across all generated binding files.

  • Release automation migrated to the kreuzberg-dev-publisher GitHub 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 via actions/create-github-app-token@v2 keyed off the org secrets BOT_APP_ID / BOT_APP_PRIVATE_KEY. Bot identity: kreuzberg-dev-publisher[bot] (user id 291994444). Eliminates the HOMEBREW_TOKEN PAT for cross-repo tap pushes and lets tag pushes trigger downstream workflows (GITHUB_TOKEN-driven pushes don’t). Branch protection on main requires kreuzberg-dev-publisher[bot] in the bypass list.

  • Stage Go FFI libraries step in .github/workflows/publish.yaml now resolves the artifact path correctly. The step cds into packages/go/ before walking the downloaded artifact tree, so the find invocation needs ../../tmp/go-ffi-all (two levels up to the repo root). Commit 1de6c8dca introduced ../../../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.28 subtree tag never pushed. Manually staged + tagged rc.28; the next publish run picks up the fix.
  • Homebrew libts-pack bottle now ships with all 306 grammars statically compiled. The build-c-ffi step in .github/workflows/publish.yaml was invoking alef publish build --lang ffi without TSLP_LANGUAGES, so crates/ts-pack-core/build.rs defaulted to zero statically-compiled grammars and the resulting FFI tarball (downloaded verbatim by the libts-pack formula) had an empty language registry. The bottle’s ts_pack_available_languages() returned an empty string, breaking test_apps/homebrew/ffi_smoke.c. Step now sets TSLP_LANGUAGES to the full language list (via the same python3 -c "import json" extraction used by the CLI build) and TSLP_LINK_MODE=static.

  • C# NuGet TreeSitterLanguagePack package now bundles an FFI dylib with all 306 grammars statically compiled. Same root cause as the libts-pack fix above — the build-csharp-native step invoked build-csharp-natives@v1 without setting TSLP_LANGUAGES. The composite action’s native cargo build inherits the calling step’s env, so adding TSLP_LANGUAGES/TSLP_LINK_MODE=static/PROJECT_ROOT on the step propagates into cargo. Fixes the Language 'comment' not found failure surfaced by test_apps/csharp against rc.27.

  • CommentKind::Block and CommentKind::Doc rustdoc 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 oxlint TS(1164): Computed property names are not allowed in enums. Reworded the rustdoc to avoid the */ terminator. (Alef 0.23.47 added an escape_jsdoc_block_close sanitization helper but it does not reach the napi enum variant doc path — tracked as alef 0.23.48+ follow-up.)

  • 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 canonical error.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).
  • Publish smoke install: scope --no-binary to tree-sitter-language-pack. pip install --no-binary :all: --no-build-isolation forced source builds for transitive deps too and pip then failed BackendUnavailable: Cannot import 'hatchling.build' because the smoke venv only pre-installed maturin + setuptools + wheel. The smoke step now scopes --no-binary to just our package; transitives use their published wheels.

  • Exclude php8.5 / macos-arm64 from the PIE matrix. shivammathur/setup-php@2.37.1 cannot 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_bytes now 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 publish verify-build would blow up the publish workflow (as happened on rc.26’s Publish Rust crates job during the 2026-06-08 GH CDN incident).

  • Local-clone fallback in crates/ts-pack-core/build.rs. When the workspace parsers/ tree is empty (gitignored on a fresh clone) and the GH release tarball for parser-sources-{version}.tar.zst isn’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 tries uv run --no-sync, uv run, python3, and python in turn so it works across dev environments. Existing TSLP_OFFLINE and TSLP_SOURCE_BUNDLE_URL overrides are unchanged.

  • get_tags_query(language: &str) -> Option<&'static str> — new public accessor in crates/ts-pack-core mirroring get_highlights_query / get_injections_query / get_locals_query. Returns Some for the 15 languages with vendored tags.scm (rust, kotlin, csharp, swift, gleam, gap, al, enforce, gdshader, roc, cfml, ql, tact, sourcepawn, mojo) and None otherwise. Propagated to the PyO3, NAPI, and FFI bindings via the alef codegen cascade.

  • gherkin grammar. Pre-compiled tree-sitter-gherkin parser for .feature files. Source: SamyAB/tree-sitter-gherkin pinned at 43873ee8de16476635b48d52c46f5b6407cb5c09.

  • Bump alef pin 0.23.30 → 0.23.34 and 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_/…) avoiding Fatal error: A class constant must not be called 'class'; java javadoc escape_javadoc_line rewrites nested */ inside {@code …} to *&#47; so the surrounding /** … */ block isn’t closed prematurely (was breaking mvn compile on CommentKind.java + DocstringFormat.java); swift ZSwiftPluginHelpers.swift imports RustBridge (not RustBridgeC) so RustString resolves; zig test_apps_run sed pattern s/}, */}\n/g correctly splits build.zig.zon dep blocks so zig fetch --save populates .hash fields; dart flutter_rust_bridge external library loader uses Abi.current() instead of Platform.version string parsing for reliable arch detection; java e2e pom.xml antrun copy-native-lib step falls back from ffi/lib/ (pre-built FFI tarball) to target/release/ (local Cargo build); php install.sh appends extension=<name> to the loaded php.ini after PIE 1.4.5+ install (PIE’s --skip-enable-extension default no longer auto-enables). (alef.toml, 482-file regen across packages/, 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_docs lint in generated swift-bridge bindings. The swift-bridge crate (packages/swift/rust/src/lib.rs) is entirely generated code with 1:1 wrapper mirrors of tree_sitter_language_pack types. 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.12 and regen all bindings, e2e, docs, test_apps, README. Pulls in upstream alef fixes since the rc.16 regen: v0.20.11 ruby Dir.chdir(ext/<name>/native) wrap on RbSys::ExtensionTask.new so Cargo::Metadata lookup finds the workspace-excluded crate; go embed-import-named-not-blank + extra-blank-line cleanup; R extendr unit-enum constructor wrappers. v0.20.12 R extendr numeric-double handling + fixture-extracted backend name; PHP e2e static-method teardown; ruby restore config.ext_dir = "native" in extconf.rb so build-time mkmf path matches the new ExtensionTask resolution. Subsequent main fixes consumed via local hand-edit pending alef 0.20.13: rustler RustlerPrecompiled base_url: template pre-wrap so mix format is idempotent (packages/elixir/lib/tree_sitter_language_pack/native.ex — wrapped manually until the alef 300d0b85b rustler-template fix ships). (alef.toml, 435+ regen files across packages/, e2e/, crates/ts-pack-core-*, docs/reference/, test_apps/, README.md)

  • Drop alef fmt from Check version sync step in both CI workflows. The rc.16 hotfix wired alef fmt between alef sync-versions and the diff check to absorb index.js oxfmt drift. But alef fmt invokes 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 (PHP vendor/bin/php-cs-fixer missing, clang-format pipeline empties to stdin), so the step fails with [ffi] error: cannot use -i when reading from stdin and [php] Could not open input file. Revert: only alef sync-versions runs; alef 0.20.12 no longer drifts index.js post-sync so the original -w --ignore-blank-lines diff check passes idempotently. (.github/workflows/{ci,ci-validate}.yaml)

  • CI Validate: unblock rc.16 by pinning pyproject-fmt==2.5.0, applying alef fmt after alef sync-versions, and refreshing two version-pin manifests the rc.15→rc.16 regen missed. Three independent CI failures on HEAD 447a5f78: (1) the pyproject-fmt prek hook crashes in argparse (add_argument("--table-format", help="...")) under newer pyproject-fmt releases — pinned to 2.5.0 via additional_dependencies so prek installs the last working version without forking xberg-io/pre-commit-hooks; (2) the Check version sync step in .github/workflows/{ci,ci-validate}.yaml ran alef sync-versions and then failed git diff -w on crates/ts-pack-core-node/index.js where alef emits 2-space wrapped output that diverges from the oxfmt-formatted committed file — added alef fmt between sync and diff so the post-gen formatters bring output back to the committed shape; (3) e2e/go/go.mod still declared v0.0.0 and test_apps/swift/Package.swift still declared from: "1.8.1", which alef sync-versions on CI 0.20.10 would rewrite — hand-bumped both to 1.9.0-rc.16 so 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 minimumReleaseAgeExclude allowlist for rc.16 platform packages and pick up new linux-*-musl variants. CI Validate / Lint & Format and CI / Validate (Lint & Format) were failing with [ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION] against the six @kreuzberg/tree-sitter-language-pack-*@1.9.0-rc.16 platform packages (published 2026-05-29T05:51Z, within the 24h supply-chain age cutoff). Bumped the six existing allowlist entries from rc.11 → rc.16 and added two new entries for linux-x64-musl / linux-arm64-musl, which the alef 0.20.10 regen now declares as optionalDependencies of crates/ts-pack-core-node/package.json. Regenerated pnpm-lock.yaml (pnpm install --lockfile-only) so the eight platform entries match the manifest and pnpm install --frozen-lockfile succeeds in both the node and wasm workspaces. (pnpm-workspace.yaml, pnpm-lock.yaml)

  • Restore typed DownloadError for Python (and equivalent typed exceptions across other bindings). Issue #133: DownloadError was dropped during the alef polyglot migration (commit 8557c150) because the Download, ChecksumMismatch, and CacheLock variants on crates/ts-pack-core/src/error.rs::Error were #[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 bare RuntimeError carrying 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 the Error enum. The next alef regen extracts them as DownloadError, ChecksumMismatchError, and CacheLockError (Python) and equivalents in every other binding, restoring the documented except DownloadError catch path. The Json and Toml variants remain feature-gated because they carry external dependency types. (crates/ts-pack-core/src/error.rs)

  • 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. Remaining 305 mentions in alef-generated package metadata (packages/*/Cargo.toml, composer.json, pyproject.toml, etc.) refresh on next task alef:generate; the alef.toml source-of-truth is already at 306.
  • repo: Add .gitattributes marking all alef-generated output directories (packages/**, crates/*-{py,php,ffi,node,wasm}/**, e2e/**) as linguist-generated=true so 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 internal alef-* 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 --release produces per-platform .node artifacts (fixes “Cannot find module ‘./ts-pack-core-node.darwin-arm64.node’” on Node e2e); zig defaults in packages/zig/build.zig switched to ../../target/release + ../../crates/ts-pack-core-ffi/include, with .task/zig.yml and the [crates.test.zig] alef e2e step both passing -Dffi_path=../../target/release; C e2e command corrected from ./test_runner → ./run_tests and .task/c.yml switched from --lang ffi → --lang c; new [crates.e2e].result_fields array + [crates.e2e.fields_c_types] map drive alef’s namespace-aware field navigation for the C process_result.metrics → FileMetrics → uintptr_t accessors. Upstream alef fix in 0.18.0: namespace_stripped_path no longer strips path segments when result_fields is empty, so legacy bindings (no result_fields configured) keep dotted-field paths intact. All 14 language e2e suites pass after regen.
  • Source-gem publish now uses the shared rewrite-native-deps@v1 action. The publish-rubygems job’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 with xberg-io/actions/rewrite-native-deps@v1 (lang: ruby) before gem build, matching the precompiled build-ruby-gem job. (.github/workflows/publish.yaml)
  • wolfram grammar dropped from the language pack. tree-sitter-wolfram produces 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 (canonical bostick/tree-sitter-wolfram last touched 2021-11-11 with 3 stars; every known fork — LumaKernel, LoganAMorrison, JuanG970, jakassebaum — ships the same LANGUAGE_VERSION 13 parser tables and is inactive). Rather than fork-and-maintain a Wolfram grammar in-house for marginal demand, the entry is removed from language_definitions.json, all CI TSLP_LANGUAGES lists, 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 the excluded crates/ts-pack-php crate; publish uses the crates/ts-pack-core-php crate via build-php-extension@v1) and scripts/ci/ruby/vendor-core.py (targeted the nonexistent crates/ts-pack-ruby crate; no-op). Dropped the now-dangling vendor tasks from .task/php.yml and .task/ruby.yml; the local PHP build/build:dev tasks now build the ts-pack-core-php crate directly, mirroring CI. (scripts/ci/php/vendor-core.py, scripts/ci/ruby/vendor-core.py, .task/php.yml, .task/ruby.yml)
  • 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-android AAR 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 — TreeSitterLanguagePack via SwiftPM. swift-bridge for macOS, iOS, and Linux.
    • Zig — zig fetch --save <tarball-url> from GitHub Releases. Direct C FFI via @cImport.
  • Two new Rust binding crates: tree-sitter-language-pack-dart (FRB bridge) and tree-sitter-language-pack-swift (swift-bridge).
  • Hand-written crates/ts-pack-core-jni Rust crate exporting Java_... JNI symbols for the Kotlin-Android binding (excluded from the default workspace build because it cross-compiles via cargo ndk).
  • Per-language CI workflows: ci-zig.yaml, ci-swift.yaml, ci-dart.yaml, plus a combined ci-mobile.yaml covering 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_LOCK in crates/ts-pack-core/src/lib.rs was a Mutex<()> — 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 test spawns eight test binaries in parallel) all raced on the same ~/.cache/tree-sitter-language-pack/v{version}/ directory. Partial entry.unpack writes were observable to other workers’ libloading::open, producing intermittent LanguageNotFound / 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> then fs::rename — readers see old, new, or nothing, never partial) and the bundle-fetch / extract / clean critical section is serialized across processes with an exclusive fd-lock on <version_cache_dir>/.download.lock. Double-checked locking preserves the lock-free hot path: steady-state is_cached lookups never pay the OS file-lock cost. New Error::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 shared ts-pack-core Rust crate. New fd-lock = "4" dependency (gated under the download feature). Cross-process safety relies on flock semantics, which are unreliable on NFS — users with XDG_CACHE_HOME on 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, new crates/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 the TSLP_LANGUAGES value in [crates.test.zig].before. Alef’s new Zig codegen filter consults both input.language and input.config.language and drops fixtures whose target grammar is not in the list (mirroring the WASM f9e0ff50 pattern). Eliminates smoke_bibtex and every other non-static-set test that previously failed at parser-load time. Also reverts the per-fixture skip: { languages: ["zig"] } workaround on fixtures/smoke/actionscript.json since the auto-omit subsumes it. (alef.toml, fixtures/smoke/actionscript.json)
  • swift e2e: process contains assertions on Vec<DTO> fields aggregate every stringy accessor (regen on alef 857c55d1). testProcessPythonImportsDetail and testProcessRustStructureName previously failed because the codegen relied on result_field_accessor naming a single “primary” accessor per array field (imports → source, structure → kind), which misses values surfaced on sibling fields — "os" against ImportInfo.items, "MyConfig" against StructureItem.name rather than StructureKind. The regenerated tests now emit a contains(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 in build-maven-package walked one dirname too far when extracting the classifier from each lib’s path, so all six platform libs landed at natives/native/{lib} instead of natives/{classifier}/{lib}. The Maven Central JAR shipped in v1.8.1 contained only three files (one per .so/.dylib/.dll extension) and TreeSitterLanguagePack.getParser("…") failed with UnsatisfiedLinkError: 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 every linux-x86_64 / linux-arm64 / macos-arm64 / macos-x86_64 / windows-x86_64 / windows-arm64 classifier directory is present in the staged JAR so the regression cannot ship again. Additionally corrected the Windows-ARM classifier from windows-aarch64 to windows-arm64: the Java loader (NativeLib.resolveNativesRid) normalizes every ARM architecture to arm64 and resolves to natives/windows-arm64/, so a JAR staged under windows-aarch64 would still UnsatisfiedLinkError on Windows ARM64 — the publish matrix and both verification steps now use windows-arm64, consistent with the linux-arm64 / macos-arm64 classifiers and the loader. (.github/workflows/publish.yaml)
  • WASM e2e local-feasibility + auto-skip wiring. [crates.test.wasm].before previously ran wasm-pack build with no TSLP_LANGUAGES set, which triggered a full 305-grammar static build — the 97MB abl/parser.c alone 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 alef f23ae5d3 / f9e0ff50 fixes that teach the wasm filter to look up both input.language and input.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: 4f6a9056 csharp List emission for mock_url_list; 06caa440 go os import include guard for mock_url_list; 1fde7aae PHP deterministic accessor extraction order (HashMap→BTreeMap; resolves the recurring $imports/$structure flip in e2e/php/tests/ProcessTest.php); 13717e24 swift e2e — trailing () on scalar accessors that bridge through opaque structs, drop spurious ?.map ... ?? [] on non-optional RustVec accessors, and camelCase swift-bridge method names (e.g. asStr() not as_str()); plus the wasm input.config.language filter 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.targets already listed x86_64-apple-darwin, but the build-node-native matrix in .github/workflows/publish.yaml omitted the macos-15-intel runner — so v1.8.0 / v1.8.1 npm tarballs shipped without ts-pack-core-node.darwin-x64.node, breaking require('@kreuzberg/tree-sitter-language-pack') on Intel Macs. Added a macos-15-intel / darwin-x64 / x86_64-apple-darwin row 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 three greater_than / less_than / less_than_or_equal operators no longer fail to compile when the leaf field is Option<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 (resolves clippy::box-default -D warnings on the PHP umbrella crate), and feat(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].languages is empty, but unlocks the future curated-build flow). Side effects in this regen: a few Rust e2e fixture bodies re-formatted, e2e/c/main.c cosmetic update, and packages/swift/rust/Cargo.toml deps re-ordered. (alef.toml, e2e/{c,php,rust}/**, packages/swift/rust/Cargo.toml)
  • CI E2E (.NET) lib-path block uses grouped redirect. shellcheck SC2129 flagged four consecutive echo … >> "$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_version in alef.toml and 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, so detectLanguageFromExtension/Path/Content, the *Query getters, and process(_:config:) compile and execute against the high-level Swift API. Downstream surface: 61 Rust-code-block leaks in crates/ts-pack-core-node/index.d.ts and 20+ in crates/ts-pack-core-php/src/lib.rs collapse to 0 after this regen.
  • Rust e2e chunks undefined. e2e/rust/tests/process_test.rs four test_*_chunking_* cases were emitting assert!(chunks.len() >= 2 as usize, ...) where chunks was undeclared (E0425). Same class of bug as the PHP $chunks fix; alef’s Rust e2e codegen unconditionally fired the streaming-virtual-field assertion arm for chunks/imports/structure even for non-streaming fixtures. Fix pulled in via alef a32ca2a0 fix(rust-gen): bind fields_array accessor before len() assertion in e2e tests — non-streaming fixtures with a colliding fields_array field now emit a leading let {field} = &{result}.{field}; binding.
  • e2e/node tree-sitter dev-dep restored (recurring). alef generate strips tree-sitter@^0.25.0 from e2e/node/package.json on every regen, but tests/capsule_passthrough.test.ts imports it to verify FFI capsule type-tag pass-through between our Language object and the upstream tree-sitter Node native module. Hand-restored, alongside the corresponding pnpm-lock.yaml rows.
  • 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 boundaries plus 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 leaks RustVec/RustString/intoRust(); conversion happens at forwarder boundaries inside generated extensions.
  • CI green-up. Regenerated pnpm-lock.yaml to drop the stale e2e/node → tree-sitter@^0.25.0 devDependency that broke pnpm install --frozen-lockfile in CI Validate. Regenerated the docs/reference/api-*.md set so committed output matches alef docs (compact Markdown tables) and alef verify stays green on main.
  • 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 on CommentKind.intoRust(), RustStringRef.toString(), RustVec<String> not conforming to Vectorizable); fix(php-gen): bind fields_array accessor before count() assertion in e2e tests (PHP e2e test_*_chunking_* cases were referencing undefined $chunks); fix(alef-backend-go): null-check and box Option<String> returns instead of dereferencing (generated packages/go/binding.go was returning C.GoString(ptr) where the signature expected *string, breaking golangci-lint and govulncheck). Side-effect: API docstrings now elide Rust-style [Type]-link syntax (e.g. PHP Node.php doc comments now read A single syntax node within a 'Tree' instead of A single syntax node within a [Tree]).
  • WASM yuck grammar marked unsupported. tree-sitter-yuck produces RuntimeError: unreachable when parsing under wasm32 (same class of bug as zig/ziggy, which already skip on wasm). fixtures/smoke/yuck.json now carries skip: { languages: ["wasm"] }; alef e2e generate removed the corresponding test from e2e/wasm/tests/smoke.test.ts. Native bindings remain unaffected.
  • package.json pnpm-field cleanup. Removed the now-ignored pnpm.onlyBuiltDependencies block from the root package.json. pnpm 11 reads that setting from pnpm-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 where curl, pip, and git all succeed against the same URL via the OS trust store — first-use parser downloads failed with DownloadError: ... io: invalid peer certificate: UnknownIssuer. The downloader now constructs a configured ureq::Agent with RootCerts::PlatformVerifier by default (via rustls-platform-verifier), matching the behaviour of every other host-trust-aware HTTP client on the system. Set TREE_SITTER_LANGUAGE_PACK_TLS_ROOTS=webpki to opt back into ureq’s bundled Mozilla roots; set TREE_SITTER_LANGUAGE_PACK_TLS_ROOTS=platform to 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 shared ts-pack-core Rust crate. (crates/ts-pack-core/src/{download.rs,pack_config.rs}, workspace Cargo.toml)
  • wolfram grammar dropped from the language pack. tree-sitter-wolfram produces 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 (canonical bostick/tree-sitter-wolfram last touched 2021-11-11 with 3 stars; every known fork — LumaKernel, LoganAMorrison, JuanG970, jakassebaum — ships the same LANGUAGE_VERSION 13 parser tables and is inactive). Rather than fork-and-maintain a Wolfram grammar in-house for marginal demand, the entry is removed from language_definitions.json, all CI TSLP_LANGUAGES lists, 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).
  • Split pub.dev publish into a dedicated publish-pubdev.yaml workflow triggered by push: tags: v*. pub.dev OIDC trusted publishing rejects tokens from release events; only push and workflow_dispatch events 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.py to reflect the 305-grammar count.
  • scripts/generate_grammar_table.py default output path corrected from docs/supported-languages.md to the canonical nav-referenced docs/languages.md; Taskfile docs:generate:languages generates: field updated to match.
  • E2E fixture coverage for: language alias resolution (shell→bash) via has_language / get_language / get_parser (3 fixtures); download edge cases — empty list, multiple-language, and unknown-language error path (3 fixtures); error-handling for 120KB sources and get_language("") (2 fixtures); and TypeScript function parsing (1 fixture). Brings fixture count from 403 to 412, covering 100% of the public download, get_*, and has_language surface across all 10 language bindings.
  • Node: getLanguage(name) now returns a real tree-sitter Language that new Parser().setLanguage(lang) accepts at runtime. The previous capsule shim used napi::bindgen_prelude::External::new (rejected by node-tree-sitter’s UnwrapLanguage), wrote the External to __parser, and did not type-tag the value. Adopts alef v0.15.49 where the napi capsule codegen emits raw napi_create_external + napi_type_tag_object and reads property_name/type_tag from [crates.node.capsule_types].

  • Python: PackConfig and ProcessConfig type hints now resolve to the .options dataclasses, fixing mypy --strict errors at every init(...) / process(...) call site (adopts alef #72).

  • Python: restore SupportedLanguage as Literal[...] of all 306 grammars at tree_sitter_language_pack.SupportedLanguage. The symbol was dropped during the alef 0.15.x codegen migration and re-importing it raised ImportError in 1.8.0 (#121).

  • Python: get_parser("python").parse(b"...") returns a real tree_sitter.Tree again instead of raising AttributeError. get_parser / get_language now return native tree_sitter.Parser / tree_sitter.Language instances via PyO3 capsule pass-through (alef v0.15.39 wires capsule_types through gen_bindings) (#121).

  • CI pinned to Node 22 LTS across all workflows. tree-sitter@0.25.0 (the tree-sitter npm package) ships a binding.cc written against pre-C++20 stdlib (no std::ranges, concept, requires) and fails to compile against Node 24/26’s V8 headers. Node 22 is the latest supported runtime until upstream node-tree-sitter updates its cflags_cc or ships prebuilds.
  • CPD pre-commit hook and packages/java/pom.xml maven-pmd-plugin minimum-tokens bumped from 100 → 250: alef’s java codegen emits ~200-token try/catch cleanup blocks on DownloadManager / LanguageRegistry. Refactoring the codegen to share a helper is tracked separately.
  • 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) and libts-pack (FFI library) via brew install --build-bottle + brew bottle --json, replacing the prior synthetic tarball approach. Eight bottles per release across arm64_sequoia, sequoia, arm64_linux, x86_64_linux. brew install now pours instead of source-building
  • libts-pack Homebrew 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 typed extractions map (process_with_extractions.json)
  • 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 nodejs build to npm so consumers no longer hit the bundler-only import * from "env" failure on require()
  • WASM coverage scoped to a curated 32-language subset to fit the 16 GB GitHub runner during builds
  • Intel: emit StructureKind::Module for Kotlin package_header and Java package_declaration so callers can build fully-qualified names for JVM languages (#112)
  • Intel: resolve structure names via a fallback chain (name field → 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-artifact produces nested artifact paths, and the previous staging loop preserved them, so every platform hit UnsatisfiedLinkError on load. Flatten via find and add presence/jar tf guard steps so the regression cannot ship silently again (#114)
  • Bindings: surface extractions as a typed Map<String, ExtractionPattern> / Map<String, PatternResult> across Java, Python, Go, TypeScript, Ruby, PHP, C#, Elixir, FFI, and WASM (was Optional<String> on Java, blocking pattern extractions through the high-level API). Driven by the alef 0.12.4 codegen fix for AHashMap-typed fields (#115)
  • C#: strip duplicate { lines emitted by alef 0.14.33 codegen so generated .cs files compile
  • Ruby: regenerated native.rb no longer recurses into itself via define_singleton_method — magnus codegen now skips re-export when binding name matches the native module method
  • Node: index.js now contains real platform-dispatch logic so require() 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 broke require() from Node consumers
  • Maven JAR previously missed linux-x86_64 natives because of stage-loop path mishandling; flatten artifact downloads and add a jar tf guard
  • Hex.pm metadata.config size limit — exclude the parser sources tarball from the package
  • PHP: fix broken crates/ts-pack-php/README.md links in root README.md — path moved to packages/php/README.md after alef migration (#106)
  • PHP: fix .task/php.yml build, build:dev, and clean tasks pointing to removed crates/ts-pack-php/ — corrected to crates/ts-pack-core-php/ (#106)
  • PHP: align packages/php/composer.json and packages/php/README.md package name to canonical Packagist vendor slug (kreuzberg/ not xberg-io/) (#106)
  • PHP: document mlocati/php-extension-installer prerequisite in install docs and correct minimum PHP version to 8.4+ (#106)
  • Go: regenerate stale binding.go with current alef generator
  • 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.toml configuration
  • Default, Hash, PartialEq, Eq derives 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 via alef e2e generate --registry)
  • 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, intel submodules, config, definitions) are no longer public
  • Workspace lints applied to all binding crates (clippy::all = "deny", unsafe_code = "deny")
  • test_apps/ moved from tests/test_apps/ to project root
  • available_languages(), has_language(), and language_count() now register the download cache directory before querying the registry — fixes empty results when using the download feature (#90)
  • process() auto-downloads missing parsers instead of returning LanguageNotFound (#94)
  • C# task references updated from .sln to .csproj
  • Maven version plugin pinned to exclude alpha/beta/RC versions
  • Docker CI: uv run changed to uv run --no-project to avoid triggering root pyproject.toml build
  • Ruby CI: removed stale working-directory that pointed to wrong path
  • Go: fix FFI build defaults — add TSLP_LINK_MODE and TSLP_LANGUAGES env vars to Go task (#102)
  • Go: fix CGO LDFLAGS paths — point to workspace target/release/ instead of crate-local path (#102)
  • Go: remove duplicate forward declarations from ffi.go (already in ts_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_dependencies for all textlint plugins (#102)
  • Compile bundled grammars with -fno-strict-aliasing to prevent undefined behavior (#100)
  • Update dependencies across lockfiles
  • Regenerate READMEs for 1.6.1 version bump (#101)
  • Go: move package root from packages/go/v1/ to packages/go/ so the Go module proxy can resolve go.mod at the correct path — go get github.com/xberg-io/tree-sitter-language-pack/packages/go now works (#97)
  • Go: fix CGO SRCDIR-relative include/lib paths (one fewer ../ after directory restructure)
  • Remove features = ["all"] from e2e Rust test Cargo.toml — use download feature 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-webpki to patch RUSTSEC-2026-0098 and RUSTSEC-2026-0099 (#99)
  • Fix MIME type inference in core build by embedding language_definitions.json in crate
  • Update dependencies across Python, Node.js, PHP, and Rust lockfiles
  • Replace feature group docs with download/TSLP_LANGUAGES documentation in READMEs
  • 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 a Language object
  • Pre-computed capture names in CompiledExtraction — avoids rebuilding on every extraction call
  • Go type_spec declarations extracted as symbols with correct SymbolKind (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 propagates Error::LockPoisoned instead of silently ignoring poisoned RwLock
  • QueryCursor byte-range no longer leaks between patterns when reusing the cursor in extract_from_tree()
  • Replaced std::collections::HashMap with ahash::AHashMap in parser cache for consistency
  • Redundant get_language() call removed from parse_string() hot path — only called on cache miss
  • CompiledExtraction::extract() and intel::parse_source() now use the thread-local parser cache
  • QueryCursor reused across patterns within a single extract_from_tree() call
  • Unnecessary String allocation removed from node_types.contains() check in chunking
  • All 305 lang-* Cargo features and group features (all, web, systems, scripting, data, jvm, functional, wasm) — language selection is now via TSLP_LANGUAGES env var at build time; the download feature (default) fetches parsers at runtime
  • 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
  • less grammar: regenerated parser from ABI 11 to ABI 14 (was incompatible with tree-sitter 0.26)
  • corn smoke fixture: replaced invalid "x" snippet with valid corn syntax
  • Include language_definitions.json in the published crate so build.rs can find extension mappings, ambiguity data, and C symbol overrides when installed from crates.io
  • Updated dependencies across all language ecosystems
  • Expose detect_language in Python public API (#85)
  • PHP extension name corrected to ts-pack-php (hyphens)
  • All language snippet READMEs and documentation corrected
  • Removed automated grammar updates workflow
  • C_SYMBOL_OVERRIDES table now includes ALL languages from language_definitions.json, not just compiled ones — fixes download and loading of csharp, vb, embeddedtemplate, nushell from PyPI/npm/RubyGems packages
  • downloaded_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.parse on native Hash return from process()
  • Go comprehensive test: access flat ProcessResult fields directly (no metadata wrapper)
  • Homebrew bottle and PHP PIE packages now included in release artifacts
  • Dependency updates across all language ecosystems
  • rustler_precompiled updated to 0.9.0 (Elixir)
  • Dynamic parser loading for languages with c_symbol overrides (csharp, vb, embeddedtemplate, nushell) — build was naming libraries with the raw name but runtime loader expected the c_symbol name (#80)
  • Go E2E generator: unused tspack import in non-process test files
  • Elixir: add missing extract/2 and validate_extraction/1 NIF declarations
  • PHP E2E generator: use double-quoted strings for source code so \n is interpreted correctly
  • Nim grammar: switch from abandoned paranim/tree-sitter-nim (ABI v11) to aMOPel/tree-sitter-nim (MIT, ABI v14)
  • Smoke test fixtures for all c_symbol override languages (csharp, vb, embeddedtemplate, nushell)
  • Dynamic-linking CI step in ci-all-grammars.yaml to catch c_symbol naming mismatches
  • 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 *ProcessResult struct fields instead of invalid json.Unmarshal on non-string return
  • Elixir CI: stage NIF with both hyphenated and underscored filenames to satisfy Rustler force-build check and load_from loader
  • 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 FFI
    • validate_extraction() for config validation without execution
    • CompiledExtraction for pre-compiled query reuse (Rust)
    • ProcessConfig.extractions for 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_source assertion uses contains instead of equality
  • WASM: language list matches actual compiled features (30 languages)
  • WASM: add missing detectLanguageFromPath and detectLanguageFromExtension exports
  • PHP generator: null array handling in process() result assertions
  • Elixir: RustlerPrecompiled crate field resolution with load_from override
  • 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+
  • Nushell grammar c_symbol override — linker error undefined symbol: tree_sitter_nushell
  • E2E generator calling .as_deref() on String type (compile error on CI)
  • WASM build: gate c_symbol_for behind dynamic-loading/download features (dead code warning)
  • Elixir publish: align RustlerPrecompiled crate: field with Cargo [lib] name (underscores, not hyphens)
  • Elixir publish: add --cfg flag patch to publish workflow for Rustler 0.37.3 compatibility
  • Python without_gil(): add catch_unwind to ensure GIL is reacquired on panic
  • Text splitter: prevent zero-width chunks in pathological UTF-8 edge case
  • Comment kind detection: handle //!, /*!, and doc_comment node types
  • Import detection: restrict fallback to explicitly supported languages only
  • Export detection: use field-based AST matching instead of fragile text.contains()
  • Registry: Arc<Vec<PathBuf>> for extra lib dirs (avoids Vec clone per language lookup)
  • Registry: AHashSet<&str> in available_languages() (avoids 248+ String allocations)
  • NodeInfo.kind uses Cow::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: MetadataCollector struct 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
  • 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 process test category with process() 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/ and metadata/ directories into unified process/ 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.js exports for detection and query functions
  • Renamed e2e fixture assertions from intel_*/meta_* to process_*
  • All documentation and package descriptions updated to reflect 248 languages
  • 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 nim grammar (alaviss, MPL-2.0 copyleft) with paranim/tree-sitter-nim (MIT)
  • Replace prolog grammar (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)
  • Dynamic loader: resolve c_symbol overrides for csharp, embeddedtemplate, and vb so get_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-grammars task for running the full grammar suite locally
  • 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
  • WASM build: ahash uses compile-time-rng instead of runtime-rng (avoids getrandom on wasm32)
  • Docker/static build: add c_symbol override for grammars with non-standard C symbol names (csharp, vb, embeddedtemplate)
  • Unused imports when dynamic-loading feature disabled (WASM builds)
  • Python sdist: .pyi and py.typed now included in both wheel and sdist
  • C# build: add missing ExtensionAmbiguityResult model class
  • Set generate: true for csharp, vb, embeddedtemplate grammars
  • Switch from std::HashMap/HashSet to ahash::AHashMap/AHashSet for faster hashing in registry
  • 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.json is the single source of truth for 239 file extensions across 197 languages
  • Build-time code generation: build.rs generates 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
  • ambiguous field in language_definitions.json for 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#
  • LanguageRegistry uses Arc<RwLock<Vec<PathBuf>>> for interior mutability — no more global RwLock wrapper, eliminates lock poisoning risk
  • ProcessConfig.language: String → Cow<'static, str> (zero allocation for string literals)
  • NodeInfo.kind, QueryMatch.captures: String → Cow<'static, str>
  • available_languages() uses HashSet for 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 memchr dependency for fast byte scanning in text splitter and chunking
  • Extension/ambiguity lookups generated from JSON at build time
  • clone_vendors.py now copies queries/ directories alongside src/
  • Strong types in all binding stubs: Python .pyi (TypedDicts), TypeScript .d.ts (interfaces), Ruby .rbs (record types), C# Models.cs (string enums replace object)
  • Pre-existing registry test failures from global RwLock poisoning — test helpers now use local LanguageRegistry::new()
  • Removed ambiguous .os (bsl) and .cls (apex/LaTeX conflict) extensions
  • Docker: separated publish-docker workflow from main publish (180-minute timeout for multiplatform builds)
  • Docker: publish-docker now triggers on release events 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

  • 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
  • tree-sitter-bsl (1C:Enterprise) grammar support
  • Updated all dependencies and relocked
  • tree-sitter 0.25 support
  • 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
  • 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
  • Updated generation setup and build matrix
  • Removed magik and swift grammars (temporarily)
  • 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)