Skip to content

feat: add pklr as opt-in pkl backend#768

Merged
jdx merged 3 commits intomainfrom
feat/pklr-backend
Mar 23, 2026
Merged

feat: add pklr as opt-in pkl backend#768
jdx merged 3 commits intomainfrom
feat/pklr-backend

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Mar 23, 2026

Summary

  • Add pklr (v0.2) as a dependency — a pure Rust pkl evaluator
  • When HK_PKL_BACKEND=pklr is set, use pklr instead of the pkl CLI for both config evaluation and import analysis
  • Default behavior unchanged (pkl CLI)

This eliminates the pkl binary dependency when opted in. Useful for environments where installing the pkl CLI is difficult.

Test plan

  • cargo check passes
  • HK_PKL_BACKEND=pklr hk run check on a project with hk.pkl
  • Verify default behavior (without env var) is unchanged

Note

Medium Risk
Default behavior stays on the pkl CLI, but this adds a new evaluation/import-analysis path with its own async runtime handling; differences in pkl feature support or import resolution could affect config loading/caching when opted in.

Overview
Adds pklr as a dependency and introduces HK_PKL_BACKEND=pklr to switch .pkl config evaluation and import analysis from shelling out to the pkl CLI to using the built-in Rust evaluator (run_pklr, pklr::eval_to_json, pklr::analyze_imports).

Updates docs to describe the new experimental backend and how to enable it, and adds a bats test (test/pklr_backend.bats) to verify basic evaluation and hk validate work under the pklr backend.

Written by Cursor Bugbot for commit be28735. This will update automatically on new commits. Configure here.

Set `HK_PKL_BACKEND=pklr` to use the built-in Rust pkl evaluator
instead of shelling out to the pkl CLI. This eliminates the pkl binary
dependency when enabled.

Both `eval` and `analyze_imports` are routed through pklr when the env
var is set. The default behavior (using the pkl CLI) is unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 23, 2026

Greptile Summary

This PR adds pklr (a pure-Rust pkl evaluator) as an opt-in backend for config evaluation and import analysis, controlled by HK_PKL_BACKEND=pklr. The core path in Config::read and Config::analyze_imports correctly branches on the env var, and run_pklr properly bridges async work into the synchronous call stack using block_in_place when inside the existing multi-thread tokio runtime.

Key changes:

  • pklr = "0.2" added to Cargo.toml; HK_PKL_BACKEND lazy static added to env.rs
  • Config::read dispatches to run_pklr when backend is pklr, otherwise uses the existing run_pkl CLI path
  • Config::analyze_imports similarly branches on the backend
  • apply_hkrc does not respect HK_PKL_BACKEND and always calls run_pkl — users who set the env var to eliminate the pkl CLI dependency will still see a failure if they have any hkrc/global config pkl file
  • pklr::analyze_imports is called without the async-runtime bridging that run_pklr carefully provides for eval_to_json; if pklr performs async work internally, this will panic inside the main tokio runtime
  • Documentation and bats tests are clean and well-written

Confidence Score: 3/5

  • Safe to merge for default users (pkl CLI path unchanged), but the pklr backend is incomplete and will silently break for any user with a global hkrc config file.
  • The default pkl CLI path is entirely untouched. However, the pklr backend has a clear gap: apply_hkrc always calls run_pkl, meaning the pklr backend does not fully eliminate the pkl CLI dependency as advertised. There is also an async-bridging inconsistency in analyze_imports that could cause runtime panics depending on pklr's internal implementation. Both issues are confined to the new opt-in path and do not affect the default behavior, but they do mean the new feature doesn't reliably deliver on its core promise.
  • src/config.rs — specifically the apply_hkrc function and the pklr::analyze_imports call site.

Important Files Changed

Filename Overview
src/config.rs Adds run_pklr helper and two pklr-gated branches in read and analyze_imports; however, apply_hkrc hardcodes run_pkl and will ignore HK_PKL_BACKEND, and pklr::analyze_imports is called without the same async bridging used for eval_to_json.
src/env.rs Adds HK_PKL_BACKEND as a simple Option<String> lazy static; straightforward addition with no issues.
Cargo.toml Adds pklr = "0.2" as an unconditional dependency; prior review already flagged the optional-feature concern.
test/pklr_backend.bats New bats integration tests for pklr backend; covers basic evaluation and validate — reasonable coverage for a first pass.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Config::get] --> B[load_project_config]
    B --> C[load_config_cached]
    C --> D{is pkl file?}
    D -- yes --> E[analyze_imports]
    D -- no --> H[Config::read]
    E --> F{HK_PKL_BACKEND == pklr?}
    F -- yes --> G[pklr::analyze_imports\nno async bridging]
    F -- no --> I[run_pkl analyze imports]
    C --> H
    H --> J{ext == pkl?}
    J -- yes --> K{HK_PKL_BACKEND == pklr?}
    K -- yes --> L[run_pklr\nblock_in_place / new runtime]
    K -- no --> M[run_pkl eval]
    J -- no --> N[toml/yaml/json parser]
    C --> O[apply_hkrc]
    O --> P[run_pkl eval\nALWAYS - ignores HK_PKL_BACKEND]
    style P fill:#f88,stroke:#c00
    style G fill:#ffa,stroke:#aa0
Loading

Comments Outside Diff (1)

  1. src/config.rs, line 297 (link)

    P1 apply_hkrc ignores HK_PKL_BACKEND

    apply_hkrc always calls run_pkl for hkrc files regardless of the backend setting. If a user sets HK_PKL_BACKEND=pklr specifically to avoid the pkl CLI requirement, but they have a ~/.config/hk/config.pkl, .hkrc.pkl, or ~/.hkrc.pkl, this code will still invoke the pkl CLI (and fail with "install pkl cli to use pkl config files" if it isn't present). This defeats the whole point of the feature for users with a global config.

    Fix in Claude Code

Fix All in Claude Code

Reviews (3): Last reviewed commit: "docs: document pklr backend, update pkl ..." | Re-trigger Greptile

Comment thread Cargo.toml
libc = "0.2"
log = "0.4"
once_cell = "1"
pklr = "0.2"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 pklr is a hard dependency, not opt-in

The PR description calls pklr an "opt-in" backend, but it is added as an unconditional dependency. This increases binary size and compile time for all users, including those who never use pkl files or never set HK_PKL_BACKEND. Consider making it a Cargo feature:

pklr = { version = "0.2", optional = true }

Then gate the pklr code paths with #[cfg(feature = "pklr")] in config.rs. This keeps the default binary lean and makes "opt-in" accurate in both the runtime and build-time senses.

Fix in Claude Code

Comment thread src/env.rs Outdated
autofix-ci Bot and others added 2 commits March 23, 2026 21:45
- Getting started: pkl CLI no longer required when HK_PKL_BACKEND=pklr
- pkl introduction: document pklr as built-in evaluator, note it will
  eventually become the default
- Configuration: mention pklr option
- Add bats test for pklr backend

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@jdx jdx merged commit a4a3fe8 into main Mar 23, 2026
15 of 18 checks passed
@jdx jdx deleted the feat/pklr-backend branch March 23, 2026 22:02
Comment thread src/config.rs
Comment on lines +53 to +55
return pklr::analyze_imports(path)
.map(|v| v.into_iter().collect())
.map_err(|e| eyre::eyre!("{e}"));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 pklr::analyze_imports called without async runtime bridging

pklr::eval_to_json is async and requires the run_pklr wrapper to safely bridge into sync code (handling both existing multi-thread runtimes via block_in_place and the no-runtime case). pklr::analyze_imports is called here without any such bridging.

Since pklr depends on tokio and reqwest (for downloading package imports over HTTP), analyze_imports likely needs async I/O internally. If it creates its own tokio::runtime::Runtime internally while already running inside the main #[tokio::main] multi-thread runtime, it will panic with "Cannot start a runtime from within a runtime."

If pklr::analyze_imports is also an async function (or wraps async work), it should be called through run_pklr or an equivalent wrapper:

if env::HK_PKL_BACKEND.as_deref() == Some("pklr") {
    let paths = run_pklr_analyze_imports(path)?;
    return Ok(paths.into_iter().collect());
}

It is worth verifying the exact signature and internal runtime handling of pklr::analyze_imports before shipping.

Fix in Claude Code

@jdx jdx mentioned this pull request Mar 23, 2026
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment thread src/config.rs
.filter(|s| !s.is_empty())
}

fn run_pklr<T: DeserializeOwned>(path: &Path) -> Result<T> {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hkrc evaluation ignores pklr backend setting

High Severity

The apply_hkrc method always calls run_pkl (the CLI-based evaluator) to parse hkrc pkl files, even when HK_PKL_BACKEND=pklr is set. This means users who opt in to the pklr backend to avoid needing the pkl CLI will still fail if they have an hkrc config file (e.g., ~/.config/hk/config.pkl). The backend check that was added to read and analyze_imports is missing from this code path.

Fix in Cursor Fix in Web

jdx added a commit that referenced this pull request Apr 1, 2026
### 🚀 Features

- **(betterleaks)** add betterleaks config to hk builtin config by
[@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in
[#750](#750)
- **(builtins)** add google-java-format to builtins by
[@timothysparg](https://github.com/timothysparg) in
[#777](#777)
- **(builtins)** add dclint to builtins by
[@timothysparg](https://github.com/timothysparg) in
[#779](#779)
- **(config)** set default value for exclude to List() by
[@timothysparg](https://github.com/timothysparg) in
[#781](#781)
- **(core)** add required field to prevent unconfigured steps from
running by [@timothysparg](https://github.com/timothysparg) in
[#785](#785)
- **(gitleaks)** add gitleaks config to hk builtin config by
[@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in
[#749](#749)
- **(mdschema)** add mdschema config to hk builtin config by
[@hituzi-no-sippo](https://github.com/hituzi-no-sippo) in
[#748](#748)
- **(pkl)** add pklr as opt-in pkl backend by
[@jdx](https://github.com/jdx) in
[#769](#769)
- add pklr as opt-in pkl backend by [@jdx](https://github.com/jdx) in
[#768](#768)

### 🐛 Bug Fixes

- **(docs)** replace invalid /latest/ pkl package URIs with versioned
format by [@jdx](https://github.com/jdx) in
[#770](#770)
- **(stage)** do not stage pre-existing untracked files by
[@jdx](https://github.com/jdx) in
[#788](#788)

### 📚 Documentation

- add benchmarks page and reproducible benchmark suite by
[@jdx](https://github.com/jdx) in
[#766](#766)
- add recommended setup section to mise integration by
[@timothysparg](https://github.com/timothysparg) in
[#780](#780)

### 📦️ Dependency Updates

- lock file maintenance by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#762](#762)
- update rust crate pklr to 0.4 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#776](#776)
- update apple-actions/import-codesign-certs digest to fe74d46 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#774](#774)
- update anthropics/claude-code-action digest to 094bd24 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#773](#773)
- update taiki-e/upload-rust-binary-action digest to 0e34102 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#775](#775)
- bump usage to 3.2.0 and pkl to 0.31.1, add windows platforms by
[@jdx](https://github.com/jdx) in
[#787](#787)
- lock file maintenance by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#786](#786)

### New Contributors

- @timothysparg made their first contribution in
[#781](#781)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Primarily a version/documentation bump, but it also updates the Rust
dependency lockfile (e.g., `hyper` and `windows-sys`), which could
introduce build/runtime regressions.
> 
> **Overview**
> Bumps hk to **v1.40.0** and publishes the corresponding release notes
in `CHANGELOG.md`.
> 
> Updates generated CLI/docs and all Pkl package URL references in
docs/examples to point at `v1.40.0`, and refreshes `Cargo.lock` with
dependency updates/removals consistent with the new release.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
da00ab8. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: mise-en-dev <[email protected]>
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Apr 2, 2026
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [hk](https://github.com/jdx/hk) | minor | `1.39.0` → `1.40.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>jdx/hk (hk)</summary>

### [`v1.40.0`](https://github.com/jdx/hk/blob/HEAD/CHANGELOG.md#1400---2026-04-01)

[Compare Source](jdx/hk@v1.39.0...v1.40.0)

##### 🚀 Features

- **(betterleaks)** add betterleaks config to hk builtin config by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;750](jdx/hk#750)
- **(builtins)** add google-java-format to builtins by [@&#8203;timothysparg](https://github.com/timothysparg) in [#&#8203;777](jdx/hk#777)
- **(builtins)** add dclint to builtins by [@&#8203;timothysparg](https://github.com/timothysparg) in [#&#8203;779](jdx/hk#779)
- **(config)** set default value for exclude to List() by [@&#8203;timothysparg](https://github.com/timothysparg) in [#&#8203;781](jdx/hk#781)
- **(core)** add required field to prevent unconfigured steps from running by [@&#8203;timothysparg](https://github.com/timothysparg) in [#&#8203;785](jdx/hk#785)
- **(gitleaks)** add gitleaks config to hk builtin config by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;749](jdx/hk#749)
- **(mdschema)** add mdschema config to hk builtin config by [@&#8203;hituzi-no-sippo](https://github.com/hituzi-no-sippo) in [#&#8203;748](jdx/hk#748)
- **(pkl)** add pklr as opt-in pkl backend by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;769](jdx/hk#769)
- add pklr as opt-in pkl backend by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;768](jdx/hk#768)

##### 🐛 Bug Fixes

- **(docs)** replace invalid /latest/ pkl package URIs with versioned format by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;770](jdx/hk#770)
- **(stage)** do not stage pre-existing untracked files by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;788](jdx/hk#788)

##### 📚 Documentation

- add benchmarks page and reproducible benchmark suite by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;766](jdx/hk#766)
- add recommended setup section to mise integration by [@&#8203;timothysparg](https://github.com/timothysparg) in [#&#8203;780](jdx/hk#780)

##### 📦️ Dependency Updates

- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;762](jdx/hk#762)
- update rust crate pklr to 0.4 by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;776](jdx/hk#776)
- update apple-actions/import-codesign-certs digest to [`fe74d46`](jdx/hk@fe74d46) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;774](jdx/hk#774)
- update anthropics/claude-code-action digest to [`094bd24`](jdx/hk@094bd24) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;773](jdx/hk#773)
- update taiki-e/upload-rust-binary-action digest to [`0e34102`](jdx/hk@0e34102) by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;775](jdx/hk#775)
- bump usage to 3.2.0 and pkl to 0.31.1, add windows platforms by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;787](jdx/hk#787)
- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;786](jdx/hk#786)

##### New Contributors

- [@&#8203;timothysparg](https://github.com/timothysparg) made their first contribution in [#&#8203;781](jdx/hk#781)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDIuMTAiLCJ1cGRhdGVkSW5WZXIiOiI0My4xMDIuMTAiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbIlJlbm92YXRlIEJvdCIsImF1dG9tYXRpb246Ym90LWF1dGhvcmVkIiwiZGVwZW5kZW5jeS10eXBlOjptaW5vciJdfQ==-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant