Skip to content

chore: bump mill-bun-plugin to 0.2.0 with bun macro + CLAUDE.md rewrite#35

Merged
arcaputo3 merged 7 commits intomainfrom
chore/mill-bun-plugin-bump
Apr 8, 2026
Merged

chore: bump mill-bun-plugin to 0.2.0 with bun macro + CLAUDE.md rewrite#35
arcaputo3 merged 7 commits intomainfrom
chore/mill-bun-plugin-bump

Conversation

@arcaputo3
Copy link
Copy Markdown
Contributor

@arcaputo3 arcaputo3 commented Apr 8, 2026

Summary

  • Bump mill-bun-plugin from 0.1.0 to 0.2.0 — adopts bun"" string interpolator for compile-time validated dependency specifiers and BunPublishModule for embedding META-INF/bun/bun-dependencies.json in published JARs
  • CLAUDE.md rewrite — comprehensive always-applied project guide covering the Zen of Scalagent, type algebra, DSL patterns, build system, and examples

Key Changes

build.mill

  • npmDeps / npmDevDepsbunDeps / bunDevDeps with bun"" macro
  • Added BunPublishModule trait to agent module
  • Added import mill.bun.bun

CLAUDE.md

  • Now alwaysApply: true (was conditional on JS/TS globs)
  • Documents the DSL philosophy, architecture, and idiomatic patterns
  • Covers convenience API, builder pattern, structured output, tools, delegation, evaluation, directory scoping

Follow-up

  • Standalone consumer example (deferred to post-release)

Test plan

  • ./mill agent.compile — bun macro fires correctly
  • ./mill agent.test — 49 suites, 0 failures
  • ./mill examples.compile — transitive deps resolve

🤖 Generated with Claude Code

arcaputo3 and others added 5 commits April 7, 2026 19:18
Adopt compile-time validated dependency specifiers via the bun""
string interpolator and add BunPublishModule for transitive dep
manifests in published JARs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Demonstrates that a project depending on the published Scalagent JAR
gets all npm dependencies (claude-agent-sdk, zod, a2a-js, codex-sdk)
resolved automatically via BunPublishModule's embedded manifest —
no package.json, no bunDeps, no manual bun install needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switch from convenience API (Claude.ask) to the provider-independent
DSL (ClaudeInterpreter.string + ExecutionPolicy + TraceSummary) which
is the central Scalagent API path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document the mill-bun-plugin v0.2.0 build dependency pattern and
the standalone consumer example that demonstrates automatic JAR
manifest dep resolution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comprehensive always-applied project guide covering:
- Philosophy principles (the "zen")
- Type algebra (Agent[-P,-I,+O], AgentRun, ExecutionPolicy, TypedAgent)
- DSL patterns (convenience API, builder, structured output, tools,
  delegation, directory scoping, evaluation)
- Build commands, dependency management, and examples

Replaces the previous conditional Bun-only context file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 8, 2026

PR Review — chore: bump mill-bun-plugin to 0.2.0 with bun macro + standalone example

Overall this is a clean, well-scoped PR. The bun"" macro migration is mechanical and correct; the standalone example nicely demonstrates the zero-config consumer story. A few things worth flagging:


standalone-example/build.mill — SNAPSHOT dependency

mvn"com.tjclp::scalagent_sjs1:0.5.1-SNAPSHOT"

SNAPSHOT coordinates mean this example silently breaks if ./mill agent.publishLocal hasn't been run first. That's fine for a local demo but brittle in CI. Two suggestions:

  1. Add a note in CLAUDE.md (or a README in standalone-example/) that consumers must ./mill agent.publishLocal before running the standalone example, or
  2. Add a CI step that publishes locally and then runs the standalone compile as a smoke test.

standalone-example/build.millmoduleDir override

override def moduleDir = build.moduleDir

This makes app's moduleDir point at the build root rather than the default (standalone-example/app/). Combined with the explicit sources override it works, but it's non-obvious and worth a one-line comment explaining why (i.e. the project root is the module root here since there's only one module).


Main.scala — error type Any

val run: ZIO[Any, Any, Unit] =

ZIOAppDefault.run already has Any as its error channel so the annotation is redundant. Dropping the explicit type or narrowing it to ZIO[Any, Throwable, Unit] would be more idiomatic for ZIO examples that contributors might copy.


Main.scala — events/result ordering

evts <- agentRun.events.runCollect.map(_.toList)
out  <- agentRun.result

The CLAUDE.md DSL guide (and the dsl-basic example) shows result being awaited before events to avoid scope issues. If events is a ZStream that holds the scope open, collecting it before result may work, but the ordering is the reverse of the documented pattern. Worth verifying this is intentional and consistent with AgentRun semantics, or aligning the example with the canonical ordering to avoid confusion.


CLAUDE.md — alwaysApply: true

Switching from glob-gated to always-applied is a meaningful change — every conversation in this repo will now load the full ~300-line document. The content is high-quality and the Zen/architecture sections are genuinely useful, but make sure the Claude Code memory budget is acceptable. If it causes context pressure, consider splitting the Bun/JS section into a separate CLAUDE-bun.md with its own globs since that section is only relevant for JS/TS work.


Minor nits

  • standalone-example/mill is the standard Mill bootstrap script — fine to commit but could live in .gitignore or be symlinked to the root ./mill to avoid duplication.
  • The bunDevDeps only has @types/bun — is there a reason @types/bun wasn't also wrapped by BunPublishModule? Dev deps presumably don't need to be embedded in the JAR, so the current approach is correct; just worth a comment.

Summary

Area Verdict
npmDepsbunDeps / bun"" macro ✅ Clean migration
BunPublishModule + JAR manifest ✅ Good design
Standalone example ✅ Demonstrates the feature well, SNAPSHOT caveat noted
CLAUDE.md rewrite ✅ Substantially better, minor scope concern
Tests ✅ Existing suite passes; standalone acts as integration test

The SNAPSHOT and event-ordering points are worth addressing before merge; the rest are low-priority suggestions.

🤖 Generated with Claude Code

- Add TODO comment for SNAPSHOT → release version after publish
- Add comment explaining moduleDir override in standalone example
- Drop redundant ZIO type annotation in Main.scala
- Note publishLocal prerequisite in CLAUDE.md standalone section

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 8, 2026

Code Review

Overall this is a clean, well-structured PR. The mill-bun-plugin upgrade is straightforward, the standalone example is a great addition for demonstrating JAR-embedded npm dep resolution, and the CLAUDE.md rewrite is a significant improvement in project documentation.

✅ Positives

  • The bun"" macro migration is clean and the diff is minimal — exactly the right scope for a plugin version bump.
  • BunPublishModule embedding META-INF/bun/bun-dependencies.json is a compelling DX win; the standalone example proves the concept end-to-end.
  • The CLAUDE.md rewrite is excellent. The type algebra section, flow diagram, and capability accumulation examples are exactly what contributors need to get oriented quickly. Moving to alwaysApply: true is the right call for a project-wide guide.

Issues / Suggestions

1. SNAPSHOT dependency in standalone example (standalone-example/build.mill:23)

// TODO: Update to release version after next publish (e.g. 0.5.1)
def mvnDeps = Seq(
  mvn"com.tjclp::scalagent_sjs1:0.5.1-SNAPSHOT",
  ...
)

This is fine while iterating, but it means the example in main will silently fail for anyone who clones fresh without running ./mill agent.publishLocal first. The README-style docs in CLAUDE.md do cover this, but consider whether the TODO should also be tracked in an issue so it doesn't get forgotten post-release.

2. No .gitignore for standalone-example/

The standalone-example is a nested Mill project that will generate out/ and potentially node_modules/ on ./mill app.compile. Without a .gitignore, these build artifacts risk being committed accidentally. A minimal:

out/
node_modules/

would protect this.

3. Vendored mill script (standalone-example/mill)

This is 199 lines copied from the official mill launcher. It works and is standard practice for self-contained Mill projects, but it can drift from the upstream version over time. If there's a lint/CI step, it's worth ensuring the parent project's mill launcher and the standalone one stay in sync (or at least note the version it was copied from in a comment at the top).

4. AgentRun usage pattern in Main.scala:49-55

(events, output) <- ZIO.scoped {
  val agentRun = agent.run("user", "Name three prime numbers under 20.", policy)
  for
    evts <- agentRun.events.runCollect.map(_.toList)
    out  <- agentRun.result
  yield (evts, out)
}

Collecting events before reading result works, but depending on how AgentRun is implemented, there's a subtle ordering question: if events must be fully consumed before result is available, this is correct. If they're truly independent (e.g., result resolves from a shared ref), then running events.runCollect first could be blocking unnecessarily. The CLAUDE.md example uses .tapEvents(...).events.runCollect alongside result as separate forks — consider aligning the standalone example with that pattern using ZIO.zip or agentRun.tapEvents(...).events.runCollect <&> agentRun.result to make the intent explicit. Minor, but worth consistency with the documented pattern.

5. CLAUDE.md code snippets use <- without for/yield wrappers

Several DSL examples in CLAUDE.md use assignment-style <- outside of a for-comprehension:

val answer <- Claude.ask("What is 2 + 2?", options)
val chunks <- Claude.query("Count to 5.", options).textOnly.runCollect

These are pseudo-code, not valid Scala 3. It's fine for documentation, but may confuse readers unfamiliar with ZIO. A brief // pseudo-code comment or wrapping in for { ... } yield ... would improve clarity.


Summary

Area Status
build.mill migration ✅ Clean
BunPublishModule ✅ Good addition
Standalone example (build) ✅ Works, SNAPSHOT note above
Standalone example (Main.scala) ✅ Good demo, minor ordering note
CLAUDE.md rewrite ✅ Excellent
.gitignore for standalone ⚠️ Missing

The SNAPSHOT TODO and missing .gitignore are the two concrete items worth addressing before merge. The rest are suggestions.

Deferring standalone consumer example to a follow-up after the next
Scalagent release. This PR focuses on the mill-bun-plugin bump and
CLAUDE.md rewrite.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@arcaputo3 arcaputo3 changed the title chore: bump mill-bun-plugin to 0.2.0 with bun macro + standalone example chore: bump mill-bun-plugin to 0.2.0 with bun macro + CLAUDE.md rewrite Apr 8, 2026
@claude
Copy link
Copy Markdown

claude bot commented Apr 8, 2026

PR Review: mill-bun-plugin 0.2.0 + CLAUDE.md rewrite

Overall this is a clean, well-scoped PR. The bun"" macro upgrade is a genuine improvement, and the CLAUDE.md rewrite is a long overdue transformation from a generic Bun cheatsheet into a proper Scalagent architecture guide.


build.mill

Positive:

  • The bun"" string interpolator is a strict improvement: compile-time validation of npm specifiers catches typos (malformed versions, missing @, invalid scope syntax) before any bun install runs.
  • BunPublishModule embedding META-INF/bun/bun-dependencies.json into published JARs is the right approach for a library — downstream consumers get transitive JS deps resolved automatically.

Minor: trait linearization order

object agent extends BunScalaJSModule with BunPublishModule with PublishModule

BunPublishModule and PublishModule likely overlap on publishArtifacts/jar-related tasks. Scala linearizes right-to-left, so PublishModule wins over BunPublishModule on conflicts — if BunPublishModule is meant to extend PublishModule's artifact output, the ordering is correct. Since compilation succeeds, this is likely fine; just worth verifying against mill-bun-plugin 0.2.0 docs that no specific ordering is required.

Observation: examples module unchanged — correct

examples inherits JS deps via moduleDeps = Seq(agent), so no bunDeps / bunDevDeps needed there.


CLAUDE.md

Positive:

  • alwaysApply: true is correct for a primary project guide.
  • The architecture diagram, type algebra table, and "Zen of Scalagent" section are excellent for orienting new contributors.
  • The examples table now matches what is actually registered in build.mill — was missing several DSL examples before.

Bug: missing for block in Convenience API examples

The section currently shows:

val answer <- Claude.ask("What is 2 + 2?", options)
val chunks <- Claude.query("Count to 5.", options).textOnly.runCollect
val result <- Claude.conversation(options) { session => ... }

These use <- generator syntax outside a for comprehension, which is not valid Scala. Copy-pasting these will produce a compile error. Suggest wrapping in a for block (consistent with the DSL Basic section):

for
  answer <- Claude.ask("What is 2 + 2?", options)
  chunks <- Claude.query("Count to 5.", options).textOnly.runCollect
yield ...

Or at minimum add a comment: // inside ZIO for-comprehension.

Minor: alwaysApply: true context window cost

~300 lines loaded on every Claude Code invocation vs ~120 before. Reasonable trade-off; if context pressure becomes an issue later, the "Bun / JS Context" section could be split into a separate conditional file since it is rarely relevant compared to the Scala DSL sections.

Minor: dsl-cells example name mismatch

CLAUDE.md shows ./mill examples.run dsl-cells but build.mill maps it to DslClandestineCellExample. Inconsistency is harmless now but worth aligning if the class is ever renamed.


Test Coverage

Solid test plan:

  • agent.compile — validates the macro fires
  • agent.test — 49 suites, 0 failures
  • examples.compile — transitive deps resolve

Gap: does agent.publishLocal produce a JAR that actually contains META-INF/bun/bun-dependencies.json? That is the primary value-add of BunPublishModule. A quick jar tf ~/.ivy2/local/.../scalagent.jar | grep bun would confirm the artifact is correct.


Summary

Area Verdict
bun"" macro migration Clean and correct
BunPublishModule addition Right approach for library publishing
CLAUDE.md architecture guide Major improvement
Code example syntax (Convenience API) Bug — missing for context, misleading for readers
Publish artifact verification Gap in test plan

The for-block issue in the convenience API examples is the one thing worth fixing before merge. Everything else is positive or minor.

@arcaputo3 arcaputo3 merged commit 7346984 into main Apr 8, 2026
3 checks passed
@arcaputo3 arcaputo3 deleted the chore/mill-bun-plugin-bump branch April 8, 2026 02:44
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