Skip to content

fix(cli): make baml agent install work without the GitHub API and install into the right directory - #3984

Merged
ATX24 merged 16 commits into
canaryfrom
dhilan/update-mechanism
Jul 12, 2026
Merged

ATX24 merged 16 commits into
canaryfrom
dhilan/update-mechanism

Conversation

@ATX24

@ATX24 ATX24 commented Jul 12, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Two bugs in baml agent install, both hit in the wild:

  1. Installs hard-fail on GitHub API rate limits. The default path resolved the
    skill repo head via an unauthenticated GET api.github.com/.../commits/main,
    which is capped at 60 requests/hour per IP. On shared-IP environments (CI, agent
    sandboxes) this 403s and the install dies — no fallback, no offline path. One
    agent run lost access to the official skills entirely after 6 failed attempts.

  2. Skills install into the wrong directory. Root detection walked all
    ancestors and accepted any directory owning a bare baml_src/. A stray
    ~/baml_src made an install run from ~/nomad (no git, no baml.toml) silently
    drop the skills into $HOME instead of the project.

Fix

Drop the GitHub API from the install path entirely. The REST call only existed
to learn the head SHA before downloading the tarball from codeload.github.com —
which is not API-rate-limited and needs no credentials. We now download the
main-branch tarball directly and recover the exact commit for provenance from the
pax global header (comment=<sha>) that git archive embeds in every GitHub
tarball. One unauthenticated request, no token ever required. Archives without the
header (custom mirrors) install fine and just skip provenance, matching --from
semantics. Download failures now point at the --from escape hatch. The wrapper's
passive freshness check still uses the REST API, but it's once-per-day and silent
on failure, so it can't block anything.

Bound root detection to where a project can plausibly be. Inside a git repo the
ancestor walk never leaves the repo (nearest baml.toml/baml_src owner, else the
repo root, where agent harnesses discover .claude/skills/). Outside a git repo it
stops before $HOME and falls back to cwd. A git toplevel that isn't an ancestor
of cwd (dotfiles GIT_DIR/GIT_WORK_TREE pattern) is ignored. The decision lives
in a pure detect_install_root_in(cwd, git_toplevel, home) so every boundary is
unit-tested without env/cwd mutation.

Testing

  • 9 new unit tests: pax parsing (valid SHA / missing header / non-SHA comment) and
    root-detection boundaries (git-scoped, home-bounded, ~/baml_src regression,
    foreign-worktree regression, cwd == home).
  • e2e rewritten so the stub serves only the tarball and 404s everything else —
    any future reintroduction of a GitHub API call fails CI.
  • Live-verified against real GitHub in fresh-user scenarios: empty $HOME, no
    .claude, no git, no baml project, no tokens, empty PATH (no git binary),
    HOME/BAML_HOME unset — all succeed, skills land in cwd/repo root, recorded
    provenance matches the repo head.
  • Adversarial sweep of the pax parser (lying lengths, mid-UTF-8 cuts, NULs,
    overflow-sized records): no panics or hangs; malformed input degrades to
    "no provenance recorded", never a failed install.

Summary by CodeRabbit

  • New Features
    • Default skill installs now derive provenance from the downloaded skill archive when the archive includes it.
  • Bug Fixes
    • Provenance is left unset when installing from custom URLs or local sources that don’t provide archive provenance data.
    • Improved install-root detection in complex directory layouts, including nested repositories and areas near the user’s home directory.
    • More reliable provenance parsing for skill archives and clearer guidance when the default archive can’t be reached (includes --from in error output).

ATX24 and others added 14 commits July 10, 2026 12:30
…helpers

New baml_release::skills module shared by the wrapper and baml-cli: skill
repo URL builders (env-overridable for mirrors/tests), [skills] provenance
state in state.toml written via read-modify-write so it never clobbers the
wrapper-owned [channels.*] sections, a latest-commit freshness cache, and a
GitHub API fetch for the skill repo head commit.

Co-authored-by: Cursor <[email protected]>
…rovenance

baml agent install now defaults to the current head of BoundaryML/baml-skill:
it resolves the main commit SHA first, downloads the tarball pinned to that
exact commit, and records [skills] provenance in state.toml (plus the
freshness cache) after a successful install. The --latest flag is removed
since it is the standard behavior; --from remains for custom sources and the
toolchain-release snapshot survives only behind
BAML_AGENT_SKILLS_RELEASE_VERSION. Includes an e2e test that runs the real
binary against a stubbed GitHub server and asserts the recorded provenance.

Co-authored-by: Cursor <[email protected]>
…ated

The wrapper now surfaces staleness on normal commands: before exec'ing
baml-cli it compares the recorded channel version against the cached channel
manifest and the [skills] provenance against the cached latest skill commit,
printing a styled bold-yellow warning: line on stderr when either is behind.

Caches auto-refresh over the network at most once per 24h window (5s timeout,
silent on failure, throttled by a .last-check marker so offline machines do
not retry every command), activating the previously dead [update] auto_check
config flag with default-on/opt-out semantics. toolchain status gains a skill
section and toolchain use/update do a best-effort skill check afterward. The
wrapper's State struct now round-trips foreign state.toml sections so it
cannot drop the CLI-written [skills] entry. Covered by unit tests plus an
e2e suite that runs the real wrapper binary against a temp BAML_HOME.

Co-authored-by: Cursor <[email protected]>
Projects without any baml-* agent skills now get a warning on every command
(No baml skill is installed, set it up with baml agent install), instead of
staying silent. Projects with skills keep the upgrade warning when provenance
is missing or behind the cached latest commit; the project-skills check is
now the primary gate, so global provenance no longer triggers upgrade
warnings in skill-less projects. Decision logic extracted into a pure
skill_warning_message helper with unit tests, plus updated e2e coverage.

Co-authored-by: Cursor <[email protected]>
CI runs clippy with -D warnings, which rejected the numeric casts in the
skills date helper (cast_possible_wrap/truncation/sign_loss), a
nonminimal_bool in file_older_than, and format_push_string in the e2e
test. Replace the casts with try_from with in-range fallbacks and
simplify the boolean with is_none_or.

Co-authored-by: Cursor <[email protected]>
…se pipeline

agent install now pulls the skill repo head directly from GitHub, so the
versioned baml-agent-skills-{version}.tar.gz release asset has no
consumers. Remove the build-agent-skills job, the packaging script, and
the BAML_AGENT_SKILLS_RELEASE_VERSION escape hatch that pointed at it.

Co-authored-by: Cursor <[email protected]>
- agent install --from now clears recorded provenance instead of leaving
  a stale commit that misreports custom content as current
- write_skills_state treats only a missing state.toml as empty; corrupt
  or unreadable files error instead of being silently replaced
- passive cache refreshes share one 5s wall-clock budget instead of up
  to 5s each
- skill detection requires a baml-* directory containing SKILL.md, so
  leftover empty directories don't suppress the install prompt

Co-authored-by: Cursor <[email protected]>
…uto-check

The passive path inlined its own fetch-then-persist sequence; route it
through the same helper the explicit toolchain commands use so there is
exactly one fetch-and-cache path and one compare-and-warn path, with
trigger policy (TTL-gated vs forced) as the only difference.

Co-authored-by: Cursor <[email protected]>
…ad of before it

Warnings from existing caches still print immediately (no network).
When a refresh is due (at most once per TTL window), it now runs on a
background thread while baml-cli executes; the wrapper spawns and waits
instead of exec'ing on that rare path, joins the refresh with whatever
remains of the 5s budget after the command exits, and prints only
warnings the fresh caches newly justify (never duplicating one already
shown). The common no-refresh case keeps the zero-overhead exec path.

Co-authored-by: Cursor <[email protected]>
…Hub API

baml agent install resolved the skill repo head via an unauthenticated
GET to api.github.com, whose 60-requests/hour per-IP rate limit made
installs hard-fail with HTTP 403 on shared-IP environments (CI, agent
sandboxes) — with no fallback and no offline path.

The REST call only existed to learn the head SHA before downloading the
tarball from codeload.github.com, which is not API-rate-limited and
needs no credentials. Drop it: download the main-branch tarball
directly, and recover the exact commit for provenance from the pax
global header (comment=<sha>) that git archive embeds in every GitHub
tarball. Archives without the header (custom mirrors) install fine and
just skip provenance, matching --from semantics. The download error now
points at the --from escape hatch.

The wrapper's passive freshness check still uses the REST API, but it
is throttled to once per day and silent on failure, so a rate limit
there can no longer block anything.

Co-Authored-By: Claude Fable 5 <[email protected]>
… dir

Without --dir, baml agent install picked its root via an unbounded
ancestor walk that accepted any directory owning a bare baml_src/. A
stray ~/baml_src made installs run from ~/some/project land the skills
in $HOME instead of the project.

Detection is now scoped to where a project can plausibly be: inside a
git repo the walk never leaves the repo (nearest baml.toml or baml_src
owner, else the repo root, where agent harnesses discover
.claude/skills); outside one it stops before the home directory and
falls back to the current directory. The decision lives in a pure
detect_install_root_in so the boundaries are unit-testable without env
or cwd mutation.

Co-Authored-By: Claude Fable 5 <[email protected]>
@vercel

vercel Bot commented Jul 12, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
beps Ready Ready Preview, Comment Jul 12, 2026 3:43am
promptfiddle2 Ready Ready Preview, Comment Jul 12, 2026 3:43am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
promptfiddle Skipped Skipped Jul 12, 2026 3:43am

Request Review

@github-actions

Copy link
Copy Markdown

⏭️ Performance benchmarks were skipped

Perf benchmarks (CodSpeed) are opt-in on pull requests — they no longer run on every push. They always run automatically after merge to canary/main.

To run them on this PR, do any of the following, then push a commit (or re-run CI):

  • Add RUN_CODSPEED=1 to the PR description, or
  • Include run-perf or /perf in the PR title or any commit message.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9d9a9318-b97e-4698-a404-206112d2e1b5

📥 Commits

Reviewing files that changed from the base of the PR and between 9a7b21d and d4f908d.

📒 Files selected for processing (1)
  • baml_language/crates/baml_cli/src/agent_command.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • baml_language/crates/baml_cli/src/agent_command.rs

📝 Walkthrough

Walkthrough

The agent install flow derives default-install commit provenance from tarball pax headers, constrains automatic install-root detection, and expands unit and end-to-end coverage for archive metadata, routing, missing provenance, and installation failures.

Changes

Agent install behavior

Layer / File(s) Summary
Tarball provenance extraction
baml_language/crates/baml_cli/src/agent_command.rs
Default installs fetch the main archive and extract an optional valid commit SHA from pax global headers; custom sources continue without provenance.
Constrained install-root selection
baml_language/crates/baml_cli/src/agent_command.rs
Install-root detection canonicalizes paths, respects git boundaries, limits non-git searches to the home directory, and falls back to the current directory.
Archive and install-flow validation
baml_language/crates/baml_cli/src/agent_command.rs, baml_language/crates/baml_cli/tests/agent_install_e2e.rs
Tests cover pax parsing, archive construction, root selection, codeload-only requests, headerless archives, and unreachable sources.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant codeload_archive
  participant skills_from_archive
  participant state_toml
  CLI->>codeload_archive: request main tarball
  codeload_archive->>skills_from_archive: return archive with optional pax comment
  skills_from_archive->>CLI: return skills and optional commit
  CLI->>state_toml: save installation provenance
Loading

Possibly related PRs

Poem

A bunny found a tarball bright,
With commit crumbs tucked out of sight.
The roots stayed home, the skills hopped in,
Pax shared where they had been.
No API maze—hooray! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: removing the GitHub API dependency and improving install-directory detection.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dhilan/update-mechanism

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 12, 2026 03:16 Inactive
@github-actions

github-actions Bot commented Jul 12, 2026 •

Copy link
Copy Markdown

Binary size checks passed

✅ 7 passed

Artifact Platform File Gzip Gated on Baseline Delta Status
✅ baml-cli Linux 🔒 21.9 MB 9.4 MB file 21.5 MB +356.3 KB (+1.7%) OK
✅ packed-program Linux 🔒 15.8 MB 6.7 MB file 15.6 MB +138.7 KB (+0.9%) OK
✅ baml-cli macOS 🔒 16.8 MB 8.1 MB file 16.6 MB +283.0 KB (+1.7%) OK
✅ packed-program macOS 🔒 12.2 MB 5.9 MB file 12.1 MB +83.9 KB (+0.7%) OK
✅ baml-cli Windows 🔒 18.4 MB 8.3 MB file 18.1 MB +271.9 KB (+1.5%) OK
✅ packed-program Windows 🔒 13.1 MB 6.0 MB file 13.0 MB +65.0 KB (+0.5%) OK
✅ bridge_wasm WASM 14.6 MB 🔒 4.1 MB gzip 4.1 MB +72.4 KB (+1.8%) OK

🔒 = the size this artifact is GATED on (ceiling + delta). Binaries gate on file size (installed binary); WASM gates on gzip (download size). The other size is shown for information only.


Generated by cargo size-gate · workflow run

@vercel
vercel Bot temporarily deployed to Preview – promptfiddle July 12, 2026 03:21 Inactive
@vercel
vercel Bot temporarily deployed to Preview – beps July 12, 2026 03:22 Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
baml_language/crates/baml_cli/src/agent_command.rs (1)

154-158: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant field-by-field reconstruction.

loaded can just be returned directly.

♻️ Simplify
-    let loaded = skills_from_archive(&archive)?;
-    Ok(LoadedSkills {
-        skills: loaded.skills,
-        commit: loaded.commit,
-    })
+    skills_from_archive(&archive)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@baml_language/crates/baml_cli/src/agent_command.rs` around lines 154 - 158,
Update the return logic after skills_from_archive to return loaded directly,
removing the redundant LoadedSkills field-by-field reconstruction while
preserving the existing error propagation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@baml_language/crates/baml_cli/src/agent_command.rs`:
- Around line 269-274: Update the ancestor collection in the no-git-repository
branch so it stops at the current directory when home is None, while retaining
the existing home-boundary behavior when home is available. Ensure the fallback
cannot walk ancestors up to the filesystem root and preserves the documented
install-root scope.
- Around line 259-268: Update the git_toplevel filtering before the ancestors
computation to reject a toplevel equal to the user’s home directory, while
retaining the existing cwd containment check. Ensure a dotfiles worktree rooted
at $HOME cannot contribute ancestor candidates, but preserve normal project
toplevel handling.

---

Nitpick comments:
In `@baml_language/crates/baml_cli/src/agent_command.rs`:
- Around line 154-158: Update the return logic after skills_from_archive to
return loaded directly, removing the redundant LoadedSkills field-by-field
reconstruction while preserving the existing error propagation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2a81def1-ecba-4438-9283-74cf05bba445

📥 Commits

Reviewing files that changed from the base of the PR and between 7c50603 and 06dd7ea.

📒 Files selected for processing (2)
  • baml_language/crates/baml_cli/src/agent_command.rs
  • baml_language/crates/baml_cli/tests/agent_install_e2e.rs

@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 12, 2026 03:29 Inactive
Two review findings on the bounded walk: a git toplevel rooted at (or
containing) the home directory — dotfiles worktrees, home-as-git-repo —
slipped past the ancestor filter and reopened the stray-~/baml_src
hole, and with no determinable home directory the non-git walk was
unbounded to the filesystem root. Reject home-containing toplevels, and
without a home boundary don't walk above cwd at all: installing at cwd
is always safe. Also satisfies clippy's unnecessary_to_owned in the
test archive helper.

Co-Authored-By: Claude Fable 5 <[email protected]>
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle July 12, 2026 03:35 Inactive
@vercel
vercel Bot temporarily deployed to Preview – beps July 12, 2026 03:36 Inactive
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 12, 2026 03:43 Inactive
@ATX24
ATX24 added this pull request to the merge queue Jul 12, 2026
Merged via the queue into canary with commit 663ad17 Jul 12, 2026
53 checks passed
@ATX24
ATX24 deleted the dhilan/update-mechanism branch July 12, 2026 04:17

This branch was previously deployed

3 inactive deployments
Preview – promptfiddle2 — d4f908d2 Deployed Jul 12, 2026 by vercel[bot]
Preview – beps — d4f908d2 Deployed Jul 12, 2026 by vercel[bot]
Preview – promptfiddle — d4f908d2 Deployed Jul 12, 2026 by vercel[bot]
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