get-tbd 0.1.13 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +47 -28
  2. package/dist/bin.mjs +410 -170
  3. package/dist/bin.mjs.map +1 -1
  4. package/dist/cli.mjs +202 -94
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/docs/README.md +47 -28
  7. package/dist/docs/SKILL.md +61 -18
  8. package/dist/docs/guidelines/bun-monorepo-patterns.md +2096 -0
  9. package/dist/docs/guidelines/cli-agent-skill-patterns.md +79 -5
  10. package/dist/docs/guidelines/error-handling-rules.md +66 -0
  11. package/dist/docs/guidelines/pnpm-monorepo-patterns.md +2868 -0
  12. package/dist/docs/guidelines/release-notes-guidelines.md +140 -0
  13. package/dist/docs/guidelines/{sync-troubleshooting.md → tbd-sync-troubleshooting.md} +1 -1
  14. package/dist/docs/guidelines/typescript-sorting-patterns.md +234 -0
  15. package/dist/docs/guidelines/typescript-yaml-handling-rules.md +195 -0
  16. package/dist/docs/install/claude-header.md +13 -6
  17. package/dist/docs/shortcuts/standard/agent-handoff.md +1 -0
  18. package/dist/docs/shortcuts/standard/checkout-third-party-repo.md +50 -0
  19. package/dist/docs/shortcuts/standard/{cleanup-all.md → code-cleanup-all.md} +3 -2
  20. package/dist/docs/shortcuts/standard/{cleanup-update-docstrings.md → code-cleanup-docstrings.md} +1 -0
  21. package/dist/docs/shortcuts/standard/{cleanup-remove-trivial-tests.md → code-cleanup-tests.md} +1 -0
  22. package/dist/docs/shortcuts/standard/{commit-code.md → code-review-and-commit.md} +1 -0
  23. package/dist/docs/shortcuts/standard/coding-spike.md +54 -0
  24. package/dist/docs/shortcuts/standard/create-or-update-pr-simple.md +1 -0
  25. package/dist/docs/shortcuts/standard/create-or-update-pr-with-validation-plan.md +1 -0
  26. package/dist/docs/shortcuts/standard/implement-beads.md +1 -0
  27. package/dist/docs/shortcuts/standard/merge-upstream.md +1 -0
  28. package/dist/docs/shortcuts/standard/new-architecture-doc.md +1 -0
  29. package/dist/docs/shortcuts/standard/new-guideline.md +8 -0
  30. package/dist/docs/shortcuts/standard/new-plan-spec.md +1 -0
  31. package/dist/docs/shortcuts/standard/new-research-brief.md +1 -0
  32. package/dist/docs/shortcuts/standard/new-shortcut.md +27 -1
  33. package/dist/docs/shortcuts/standard/new-validation-plan.md +1 -0
  34. package/dist/docs/shortcuts/standard/plan-implementation-with-beads.md +1 -0
  35. package/dist/docs/shortcuts/standard/precommit-process.md +1 -0
  36. package/dist/docs/shortcuts/standard/review-code-python.md +1 -0
  37. package/dist/docs/shortcuts/standard/review-code-typescript.md +1 -0
  38. package/dist/docs/shortcuts/standard/review-code.md +1 -0
  39. package/dist/docs/shortcuts/standard/review-github-pr.md +89 -17
  40. package/dist/docs/shortcuts/standard/revise-all-architecture-docs.md +1 -0
  41. package/dist/docs/shortcuts/standard/revise-architecture-doc.md +1 -0
  42. package/dist/docs/shortcuts/standard/setup-github-cli.md +1 -0
  43. package/dist/docs/shortcuts/standard/sync-failure-recovery.md +6 -53
  44. package/dist/docs/shortcuts/standard/update-specs-status.md +1 -0
  45. package/dist/docs/shortcuts/standard/welcome-user.md +2 -1
  46. package/dist/docs/shortcuts/system/skill-brief.md +1 -1
  47. package/dist/docs/shortcuts/system/skill.md +48 -12
  48. package/dist/docs/skill-brief.md +1 -1
  49. package/dist/docs/tbd-design.md +13 -1
  50. package/dist/index.d.mts +20 -6
  51. package/dist/index.mjs +2 -2
  52. package/dist/{src-BfhjLZXE.mjs → src-Ct16P2Ox.mjs} +154 -22
  53. package/dist/src-Ct16P2Ox.mjs.map +1 -0
  54. package/dist/tbd +410 -170
  55. package/package.json +1 -1
  56. package/dist/docs/guidelines/typescript-monorepo-patterns.md +0 -72
  57. package/dist/src-BfhjLZXE.mjs.map +0 -1
@@ -0,0 +1,2096 @@
1
+ ---
2
+ title: Bun Monorepo Patterns
3
+ description: Modern patterns for Bun-based TypeScript monorepo architecture
4
+ author: Joshua Levy (github.com/jlevy) with LLM assistance
5
+ ---
6
+ # Bun Monorepo Patterns
7
+
8
+ **Last Updated**: 2026-02-02
9
+
10
+ **Related**:
11
+
12
+ - [Bun Workspaces Documentation](https://bun.sh/docs/install/workspaces)
13
+ - [Bunup Documentation](https://bunup.dev/)
14
+ - [Changesets Documentation](https://github.com/changesets/changesets)
15
+ - [Biome Documentation](https://biomejs.dev/)
16
+ - [Companion: pnpm Monorepo Patterns](./pnpm-monorepo-patterns.md)
17
+
18
+ * * *
19
+
20
+ ## Updating This Document
21
+
22
+ ### Last Researched Versions
23
+
24
+ | Tool / Package | Version | Check For Updates |
25
+ | --- | --- | --- |
26
+ | **Bun** | 1.3.8 | [bun.sh/blog](https://bun.sh/blog) — Runtime, bundler, package manager, test runner. Acquired by Anthropic (Dec 2025). |
27
+ | **TypeScript** | ^5.9.3 | [github.com/microsoft/TypeScript/releases](https://github.com/microsoft/TypeScript/releases) — 5.9.3 stable. TS 6.0 is "bridge" release; TS 7.0 (Go rewrite) in VS 2026 Insiders preview. |
28
+ | **Bunup** | ^0.16.0 | [npmjs.com/package/bunup](https://www.npmjs.com/package/bunup) — Build tool for TS libs. Rapid iteration (0.16.20 latest). |
29
+ | **Biome** | ^2.3.0 | [biomejs.dev](https://biomejs.dev/) — Formatter + linter. v2.0 added plugins and type-aware linting; 2.3.x is latest stable. |
30
+ | **@changesets/cli** | ^2.29.0 | [github.com/changesets/changesets/releases](https://github.com/changesets/changesets/releases) — 2.29.8 latest. No native Bun support yet. |
31
+ | **publint** | ^0.3.17 | [npmjs.com/package/publint](https://www.npmjs.com/package/publint) — 0.3.17 latest |
32
+ | **actions/checkout** | v6 | [github.com/actions/checkout/releases](https://github.com/actions/checkout/releases) |
33
+ | **oven-sh/setup-bun** | v2 | [github.com/oven-sh/setup-bun](https://github.com/oven-sh/setup-bun) — Verified on GitHub Marketplace |
34
+ | **lefthook** | ^2.0.15 | [github.com/evilmartians/lefthook/releases](https://github.com/evilmartians/lefthook/releases) — 2.0.15 latest |
35
+ | **npm-check-updates** | ^19.0.0 | [npmjs.com/package/npm-check-updates](https://www.npmjs.com/package/npm-check-updates) |
36
+
37
+ ### Reminders When Updating
38
+
39
+ 1. **Check each version** in the table above using the linked release pages
40
+
41
+ 2. **Update the table** with new versions and any relevant notes
42
+
43
+ 3. **Search and update code examples** — version numbers appear in:
44
+
45
+ - GitHub Actions workflows (CI and Release sections)
46
+
47
+ - `bunup.config.ts` examples
48
+
49
+ - `tsconfig.base.json` examples
50
+
51
+ - `package.json` examples (`devDependencies`)
52
+
53
+ - Appendices (complete examples)
54
+
55
+ 4. **Verify compatibility** — check that tools still work together
56
+
57
+ 5. **Update the “Last Updated” date** at the top of the document
58
+
59
+ 6. **Review “Open Research Questions”** section for any resolved items
60
+
61
+ * * *
62
+
63
+ ## Executive Summary
64
+
65
+ This research brief provides a comprehensive guide for setting up a modern TypeScript
66
+ monorepo using the **Bun ecosystem** end-to-end — Bun as runtime, package manager,
67
+ bundler (via Bunup), and test runner.
68
+ It serves as a direct comparison to the companion document on pnpm-based monorepos,
69
+ covering the same architectural scope but using Bun-native tooling wherever possible.
70
+
71
+ The recommended stack uses **Bun workspaces** for dependency management, **Bunup** for
72
+ building ESM/CJS dual outputs with TypeScript declarations, **Changesets** (with Bun
73
+ workarounds) for versioning and release automation, **Biome** for formatting and
74
+ linting, **publint** for package validation, and **lefthook** for git hooks.
75
+ The architecture also covers Bun’s unique capability for **compiling standalone
76
+ executables** — a native binary distribution path unavailable in the pnpm ecosystem.
77
+
78
+ **Research Questions**:
79
+
80
+ 1. Can the Bun ecosystem fully replace pnpm + Node.js + tsdown for a TypeScript
81
+ monorepo?
82
+
83
+ 2. How does complexity compare between a full-Bun and full-pnpm monorepo setup?
84
+
85
+ 3. What are the trade-offs in ecosystem maturity, tooling gaps, and CI/CD support?
86
+
87
+ 4. What unique capabilities does Bun offer that pnpm-based setups cannot?
88
+
89
+ * * *
90
+
91
+ ## Research Methodology
92
+
93
+ ### Approach
94
+
95
+ Research was conducted through official Bun documentation, web searches for current best
96
+ practices (2025–2026), analysis of real-world Bun monorepo implementations, evaluation
97
+ of Bunup and Biome documentation, and direct comparison with the companion pnpm monorepo
98
+ research stock.
99
+
100
+ ### Sources
101
+
102
+ - Official documentation (Bun, Bunup, Biome, TypeScript, Changesets)
103
+
104
+ - Developer blog posts and migration guides
105
+
106
+ - GitHub discussions and issue threads
107
+
108
+ - Benchmark data and comparison articles
109
+
110
+ - Real-world Bun monorepo implementations
111
+
112
+ * * *
113
+
114
+ ## Research Findings
115
+
116
+ ### 1. Package Manager & Workspace Structure
117
+
118
+ #### Bun Workspaces
119
+
120
+ **Status**: Recommended (with caveats)
121
+
122
+ **Details**:
123
+
124
+ - Bun provides built-in workspace support via the `workspaces` field in `package.json`
125
+
126
+ - Dramatically faster installs than pnpm, npm, or yarn (often 2–10x)
127
+
128
+ - Uses `bun.lock` (text-based JSONC lockfile, default since Bun 1.2) for diffable,
129
+ deterministic resolution.
130
+ The older binary `bun.lockb` is deprecated.
131
+
132
+ - Supports `workspace:*` protocol for inter-package references
133
+
134
+ - Does not use a content-addressable store like pnpm — installs are flat in
135
+ `node_modules`
136
+
137
+ - Missing some pnpm features: no `pnpm deploy`, less strict `node_modules` (phantom
138
+ dependencies possible)
139
+
140
+ - **Notable**: Bun was acquired by Anthropic in December 2025. Bun now powers Claude
141
+ Code, Claude Agent SDK, and other Anthropic AI tooling, signaling strong ongoing
142
+ investment and maintenance.
143
+
144
+ **Assessment**: Bun workspaces are functional and fast, but less strict than pnpm.
145
+ The text-based `bun.lock` format (since Bun 1.2) resolves the earlier diffability
146
+ concern. The lack of content-addressable storage means higher disk usage in large
147
+ monorepos.
148
+ For projects prioritizing speed over strictness, Bun workspaces are excellent.
149
+ For projects requiring hermetic dependency isolation, pnpm remains superior.
150
+
151
+ **Key Configuration** (root `package.json`):
152
+
153
+ ```json
154
+ {
155
+ "private": true,
156
+ "workspaces": [
157
+ "packages/*",
158
+ "apps/*"
159
+ ]
160
+ }
161
+ ```
162
+
163
+ **Adding dependencies to specific workspaces**:
164
+
165
+ ```bash
166
+ # Use --cwd to target a specific workspace
167
+ bun add express --cwd packages/server
168
+
169
+ # Or from the workspace directory
170
+ cd packages/server && bun add express
171
+ ```
172
+
173
+ **References**:
174
+
175
+ - [Bun Workspaces](https://bun.sh/docs/install/workspaces)
176
+
177
+ - [Guide to Monorepo Setup: NPM, Yarn, Pnpm & Bun Workspaces](https://jsdev.space/mastering-monorepos/)
178
+
179
+ * * *
180
+
181
+ #### Monorepo Structure Strategy
182
+
183
+ **Status**: Recommended
184
+
185
+ **Details**:
186
+
187
+ The same “start mono, stay sane” approach from the pnpm research applies here.
188
+ Place packages in `packages/` from day one, even with a single package.
189
+
190
+ **Recommended Directory Structure**:
191
+
192
+ ```
193
+ project-root/
194
+ .changeset/
195
+ config.json
196
+ README.md
197
+ .github/
198
+ workflows/
199
+ ci.yml
200
+ release.yml
201
+ packages/
202
+ package-name/
203
+ src/
204
+ core/ # Future: package-name-core
205
+ cli/ # Future: package-name-cli
206
+ adapters/ # Future: package-name-adapters
207
+ bin.ts
208
+ index.ts
209
+ package.json
210
+ tsconfig.json
211
+ bunup.config.ts
212
+ biome.json
213
+ bun.lock
214
+ lefthook.yml
215
+ package.json
216
+ tsconfig.base.json
217
+ ```
218
+
219
+ **Key differences from pnpm structure**:
220
+
221
+ | File | pnpm Monorepo | Bun Monorepo |
222
+ | --- | --- | --- |
223
+ | Lockfile | `pnpm-lock.yaml` | `bun.lock` (text JSONC, diffable) |
224
+ | Workspace config | `pnpm-workspace.yaml` | `workspaces` in `package.json` |
225
+ | Package manager config | `.npmrc` | `bunfig.toml` (optional) |
226
+ | Lint/format config | `.prettierrc` + `eslint.config.js` | `biome.json` (single file) |
227
+ | Build config | `tsdown.config.ts` | `bunup.config.ts` |
228
+
229
+ **Assessment**: The directory structure is nearly identical.
230
+ Bun consolidates configuration into fewer files (no separate workspace config, single
231
+ `biome.json` instead of Prettier + ESLint configs).
232
+
233
+ * * *
234
+
235
+ ### 2. TypeScript Configuration
236
+
237
+ #### Base Configuration
238
+
239
+ **Status**: Recommended
240
+
241
+ **Details**:
242
+
243
+ TypeScript configuration for Bun monorepos is nearly identical to pnpm monorepos.
244
+ The main difference is that Bun natively executes TypeScript, so the configuration
245
+ primarily serves IDE support, type checking, and declaration generation.
246
+
247
+ **`tsconfig.base.json`**:
248
+
249
+ ```json
250
+ {
251
+ "compilerOptions": {
252
+ "target": "ES2024",
253
+ "lib": ["ES2024"],
254
+ "module": "ESNext",
255
+ "moduleResolution": "Bundler",
256
+ "resolveJsonModule": true,
257
+ "strict": true,
258
+ "skipLibCheck": true,
259
+ "noUncheckedIndexedAccess": true,
260
+ "forceConsistentCasingInFileNames": true,
261
+ "verbatimModuleSyntax": true,
262
+ "isolatedDeclarations": true
263
+ }
264
+ }
265
+ ```
266
+
267
+ **Package-level `tsconfig.json`**:
268
+
269
+ ```json
270
+ {
271
+ "extends": "../../tsconfig.base.json",
272
+ "compilerOptions": {
273
+ "types": ["bun-types"],
274
+ "noEmit": true
275
+ },
276
+ "include": ["src"]
277
+ }
278
+ ```
279
+
280
+ **Key difference**: `isolatedDeclarations: true` is strongly recommended for Bun
281
+ projects because Bunup’s DTS generation is dramatically faster when this is enabled (it
282
+ avoids invoking the full TypeScript compiler).
283
+
284
+ **Assessment**: Nearly identical to the pnpm setup.
285
+ The addition of `isolatedDeclarations` and `bun-types` are the only differences.
286
+ Using `moduleResolution: "Bundler"` is appropriate since Bunup handles the final output.
287
+
288
+ **References**:
289
+
290
+ - [Bun TypeScript Documentation](https://bun.sh/docs/typescript)
291
+
292
+ - [TypeScript: Choosing Compiler Options](https://www.typescriptlang.org/docs/handbook/modules/guides/choosing-compiler-options.html)
293
+
294
+ * * *
295
+
296
+ #### Bun-Specific TypeScript Features
297
+
298
+ **Status**: Informational
299
+
300
+ **Details**:
301
+
302
+ Bun supports a `"bun"` export condition in `package.json` that allows consumers running
303
+ Bun to directly import TypeScript source files, bypassing compiled output entirely:
304
+
305
+ ```json
306
+ {
307
+ "exports": {
308
+ ".": {
309
+ "bun": "./src/index.ts",
310
+ "import": {
311
+ "types": "./dist/index.d.ts",
312
+ "default": "./dist/index.js"
313
+ }
314
+ }
315
+ }
316
+ }
317
+ ```
318
+
319
+ This means during development within the monorepo, Bun can consume TypeScript directly
320
+ without a build step — a significant DX advantage over Node.js-based setups.
321
+
322
+ **Assessment**: This is a unique Bun advantage.
323
+ During local development, packages can be consumed without building.
324
+ Published packages still need compiled output for Node.js consumers.
325
+
326
+ * * *
327
+
328
+ ### 3. Build Tooling
329
+
330
+ #### Bunup
331
+
332
+ **Status**: Strongly Recommended
333
+
334
+ **Details**:
335
+
336
+ Bunup is the modern build tool for TypeScript libraries, powered by Bun’s native
337
+ bundler. It is the Bun-ecosystem analog to tsdown in the pnpm ecosystem.
338
+
339
+ Key advantages:
340
+
341
+ - **Extremely fast**: ~37ms builds vs multi-second builds with tsdown/tsup
342
+
343
+ - **Dual format output**: Generates both ESM (`.js`) and CJS (`.cjs`)
344
+
345
+ - **TypeScript declarations**: Built-in `.d.ts` and `.d.cts` generation (much faster
346
+ with `isolatedDeclarations`)
347
+
348
+ - **Multi-entry support**: Build multiple entry points in one config
349
+
350
+ - **Workspace support**: `defineWorkspace()` for monorepo builds with incremental
351
+ rebuilds
352
+
353
+ - **Auto-exports**: Automatically generates and updates `package.json` `exports` field
354
+
355
+ - **Compile support**: Can produce standalone executables via `bun --compile`
356
+
357
+ - **Rapid iteration**: Bunup is under active development (0.16.x as of Jan 2026), with
358
+ frequent releases. Pin to a specific minor version for stability.
359
+
360
+ **Configuration (`bunup.config.ts`)**:
361
+
362
+ ```typescript
363
+ import { defineConfig } from 'bunup';
364
+
365
+ export default defineConfig({
366
+ entry: {
367
+ index: 'src/index.ts',
368
+ cli: 'src/cli/index.ts',
369
+ bin: 'src/bin.ts',
370
+ },
371
+ format: ['esm', 'cjs'],
372
+ dts: true,
373
+ clean: true,
374
+ sourcemap: 'linked',
375
+ banner: '"use strict";',
376
+ });
377
+ ```
378
+
379
+ **Workspace configuration (`bunup.config.ts` at root)**:
380
+
381
+ ```typescript
382
+ import { defineWorkspace } from 'bunup';
383
+
384
+ export default defineWorkspace([
385
+ {
386
+ name: 'core',
387
+ root: 'packages/core',
388
+ config: {
389
+ entry: ['src/index.ts'],
390
+ format: ['esm', 'cjs'],
391
+ dts: true,
392
+ },
393
+ },
394
+ {
395
+ name: 'cli',
396
+ root: 'packages/cli',
397
+ config: {
398
+ entry: ['src/index.ts', 'src/bin.ts'],
399
+ format: ['esm'],
400
+ dts: true,
401
+ },
402
+ },
403
+ ]);
404
+ ```
405
+
406
+ **Comparison with tsdown**:
407
+
408
+ | Criteria | Bunup | tsdown |
409
+ | --- | --- | --- |
410
+ | Build speed | ~37ms | ~200ms–1s |
411
+ | Runtime dependency | Bun | Node.js |
412
+ | DTS generation | Built-in (fast with isolatedDeclarations) | Built-in |
413
+ | Auto-exports | Yes (generates `exports` field) | No |
414
+ | Workspace mode | Built-in `defineWorkspace()` | No (use pnpm -r) |
415
+ | Plugin ecosystem | Growing (Bun plugins) | Rolldown/Rollup/Vite |
416
+ | Maturity | Newer (2025) | More established |
417
+ | Standalone executables | Yes (`compile: true`) | No |
418
+
419
+ **Assessment**: Bunup is the clear choice for Bun-native projects.
420
+ The auto-exports feature eliminates a common source of configuration errors.
421
+ The workspace mode provides monorepo-aware builds that tsdown does not offer natively.
422
+
423
+ **References**:
424
+
425
+ - [Bunup Documentation](https://bunup.dev/)
426
+
427
+ - [Building a TypeScript Library in 2026 with Bunup](https://dev.to/arshadyaseen/building-a-typescript-library-in-2026-with-bunup-3bmg)
428
+
429
+ * * *
430
+
431
+ ### 4. Package Exports & Dual Module Support
432
+
433
+ #### Subpath Exports
434
+
435
+ **Status**: Essential
436
+
437
+ **Details**:
438
+
439
+ Package exports work identically to the pnpm setup.
440
+ The key difference is that Bunup can **auto-generate** the exports field, reducing
441
+ manual configuration errors.
442
+
443
+ **With Bunup auto-exports** (`bunup.config.ts`):
444
+
445
+ ```typescript
446
+ import { defineConfig } from 'bunup';
447
+
448
+ export default defineConfig({
449
+ entry: ['src/index.ts', 'src/cli.ts'],
450
+ format: ['esm', 'cjs'],
451
+ dts: true,
452
+ exports: true, // Auto-generates package.json exports
453
+ });
454
+ ```
455
+
456
+ This auto-generates:
457
+
458
+ ```json
459
+ {
460
+ "exports": {
461
+ ".": {
462
+ "import": {
463
+ "types": "./dist/index.d.ts",
464
+ "default": "./dist/index.js"
465
+ },
466
+ "require": {
467
+ "types": "./dist/index.d.cts",
468
+ "default": "./dist/index.cjs"
469
+ }
470
+ },
471
+ "./cli": {
472
+ "import": {
473
+ "types": "./dist/cli.d.ts",
474
+ "default": "./dist/cli.js"
475
+ },
476
+ "require": {
477
+ "types": "./dist/cli.d.cts",
478
+ "default": "./dist/cli.cjs"
479
+ }
480
+ }
481
+ }
482
+ }
483
+ ```
484
+
485
+ **Manual configuration** (if not using auto-exports):
486
+
487
+ Identical to the pnpm research — use the same `exports` structure with `"types"` before
488
+ `"default"` in each condition block.
489
+
490
+ **Bun export condition**: Optionally add a `"bun"` condition pointing to TypeScript
491
+ source for Bun consumers:
492
+
493
+ ```json
494
+ {
495
+ "exports": {
496
+ ".": {
497
+ "bun": "./src/index.ts",
498
+ "import": {
499
+ "types": "./dist/index.d.ts",
500
+ "default": "./dist/index.js"
501
+ },
502
+ "require": {
503
+ "types": "./dist/index.d.cts",
504
+ "default": "./dist/index.cjs"
505
+ }
506
+ }
507
+ }
508
+ }
509
+ ```
510
+
511
+ **Assessment**: Bunup’s auto-exports feature is a significant DX improvement over manual
512
+ exports configuration.
513
+ It eliminates a common class of publishing errors.
514
+
515
+ **References**:
516
+
517
+ - [Bunup Exports Configuration](https://bunup.dev/)
518
+
519
+ - [Guide to package.json exports field](https://hirok.io/posts/package-json-exports)
520
+
521
+ * * *
522
+
523
+ ### 5. Optional Peer Dependencies
524
+
525
+ **Status**: Recommended
526
+
527
+ **Details**:
528
+
529
+ This pattern is identical to the pnpm ecosystem approach.
530
+ Bun handles peer dependencies the same way as npm/pnpm.
531
+
532
+ ```json
533
+ {
534
+ "peerDependencies": {
535
+ "@modelcontextprotocol/sdk": "^1.0.0",
536
+ "ai": "^5.0.0"
537
+ },
538
+ "peerDependenciesMeta": {
539
+ "@modelcontextprotocol/sdk": { "optional": true },
540
+ "ai": { "optional": true }
541
+ }
542
+ }
543
+ ```
544
+
545
+ **Assessment**: No differences from the pnpm approach.
546
+ Works identically in Bun.
547
+
548
+ * * *
549
+
550
+ ### 6. Package Validation
551
+
552
+ #### publint
553
+
554
+ **Status**: Essential
555
+
556
+ **Details**:
557
+
558
+ publint works identically in the Bun ecosystem.
559
+ Run it via Bun:
560
+
561
+ ```bash
562
+ bunx publint
563
+ ```
564
+
565
+ **Integration**:
566
+
567
+ ```json
568
+ {
569
+ "scripts": {
570
+ "publint": "bunx publint",
571
+ "prepack": "bun run build"
572
+ }
573
+ }
574
+ ```
575
+
576
+ **Assessment**: No changes needed from the pnpm approach.
577
+ publint is runtime-agnostic.
578
+
579
+ * * *
580
+
581
+ ### 7. Versioning & Release Automation
582
+
583
+ #### Changesets (with Bun Workarounds)
584
+
585
+ **Status**: Recommended (with workarounds)
586
+
587
+ **Details**:
588
+
589
+ Changesets is the de facto standard for monorepo versioning, but it has known issues
590
+ with Bun workspaces.
591
+ The key problem is that `changeset version` does not resolve `workspace:*` references to
592
+ actual version numbers, which breaks published packages.
593
+
594
+ **Setup**:
595
+
596
+ ```bash
597
+ bun add -d @changesets/cli @changesets/changelog-github
598
+ bunx changeset init
599
+ ```
600
+
601
+ **`.changeset/config.json`**: Identical to the pnpm setup:
602
+
603
+ ```json
604
+ {
605
+ "$schema": "https://unpkg.com/@changesets/config/schema.json",
606
+ "changelog": "@changesets/changelog-github",
607
+ "commit": false,
608
+ "fixed": [],
609
+ "linked": [],
610
+ "access": "public",
611
+ "baseBranch": "main",
612
+ "updateInternalDependencies": "patch"
613
+ }
614
+ ```
615
+
616
+ **Critical workaround scripts**:
617
+
618
+ ```json
619
+ {
620
+ "scripts": {
621
+ "changeset": "changeset",
622
+ "version-packages": "changeset version && bun update",
623
+ "release": "bun run build && bunx publint && bun run publish-packages",
624
+ "publish-packages": "for dir in packages/*; do (cd \"$dir\" && bun publish || true); done && changeset tag"
625
+ }
626
+ }
627
+ ```
628
+
629
+ **Why `bun update` after `changeset version`**: When Changesets updates version numbers
630
+ in `package.json` files, the `workspace:*` references in the lockfile become stale.
631
+ Running `bun update` regenerates the lockfile with resolved versions.
632
+
633
+ **Why `bun publish` per package**: The standard `changeset publish` uses npm under the
634
+ hood. Using `bun publish` directly for each package ensures proper Bun compatibility and
635
+ workspace reference resolution.
636
+
637
+ **Comparison with pnpm Changesets workflow**:
638
+
639
+ | Aspect | pnpm | Bun |
640
+ | --- | --- | --- |
641
+ | Setup | Works out of the box | Requires workarounds |
642
+ | Version command | `changeset version` | `changeset version && bun update` |
643
+ | Publish command | `changeset publish` | Custom per-package loop with `bun publish` |
644
+ | GitHub Action | `changesets/action` works directly | Needs custom publish step |
645
+ | Workspace resolution | Automatic | Requires `bun update` fixup |
646
+
647
+ **Assessment**: Changesets works with Bun but requires workarounds.
648
+ This is the most significant friction point in the Bun ecosystem compared to pnpm.
649
+ Monitor for native Bun support in Changesets or a Bun-native alternative.
650
+
651
+ **References**:
652
+
653
+ - [Setting up Changesets with Bun Workspaces](https://ianm.com/posts/2025-08-18-setting-up-changesets-with-bun-workspaces)
654
+
655
+ - [Changesets GitHub repository](https://github.com/changesets/changesets)
656
+
657
+ * * *
658
+
659
+ #### Dynamic Git-Based Versioning
660
+
661
+ **Status**: Recommended for dev builds
662
+
663
+ **Details**:
664
+
665
+ The git-based versioning pattern from the pnpm research works identically with Bun.
666
+ The only difference is the dev script uses `bun` instead of `tsx`:
667
+
668
+ **Dev Script** (`package.json`):
669
+
670
+ ```json
671
+ {
672
+ "scripts": {
673
+ "dev": "PROJECT_DEV_VERSION=$(bun scripts/git-version.mjs) bun src/cli/bin.ts"
674
+ }
675
+ }
676
+ ```
677
+
678
+ **Key advantage**: Bun executes TypeScript directly, so there is no need for `tsx`. The
679
+ `scripts/git-version.mjs` script works unchanged since it uses only Node.js built-in
680
+ modules which Bun supports.
681
+
682
+ **Assessment**: Identical pattern, simpler execution.
683
+ No `tsx` dependency needed.
684
+
685
+ * * *
686
+
687
+ ### 8. Testing
688
+
689
+ #### Bun Test Runner
690
+
691
+ **Status**: Recommended (with caveats)
692
+
693
+ **Details**:
694
+
695
+ Bun ships with a built-in test runner that is API-compatible with Jest/Vitest.
696
+ It is extremely fast — roughly 2x faster than Node’s built-in test runner and
697
+ significantly faster than Jest or Vitest.
698
+
699
+ **Key features**:
700
+
701
+ - Zero-config: works out of the box with TypeScript
702
+
703
+ - Jest-compatible API (`describe`, `it`, `expect`, `mock`, etc.)
704
+
705
+ - Watch mode with HMR (re-runs only affected tests)
706
+
707
+ - Snapshot testing
708
+
709
+ - Code coverage (built-in, `--coverage`)
710
+
711
+ - Lifecycle hooks (`beforeAll`, `afterAll`, `beforeEach`, `afterEach`)
712
+
713
+ **Running tests**:
714
+
715
+ ```bash
716
+ bun test # Run all tests
717
+ bun test --watch # Watch mode
718
+ bun test --coverage # With coverage
719
+ bun test --timeout 10000 # Custom timeout
720
+ ```
721
+
722
+ **Test file patterns**: `*.test.ts`, `*.test.tsx`, `*.spec.ts`, `*.spec.tsx`, or files
723
+ in `__tests__/` directories.
724
+
725
+ **Example test**:
726
+
727
+ ```typescript
728
+ import { describe, it, expect, mock } from 'bun:test';
729
+ import { processData } from '../src/index.ts';
730
+
731
+ describe('processData', () => {
732
+ it('handles valid input', () => {
733
+ expect(processData({ key: 'value' })).toEqual({ key: 'VALUE' });
734
+ });
735
+
736
+ it('throws on invalid input', () => {
737
+ expect(() => processData(null)).toThrow('Invalid input');
738
+ });
739
+ });
740
+ ```
741
+
742
+ **Known limitations**:
743
+
744
+ - Tests are not isolated by default (side effects can leak between suites)
745
+
746
+ - Less mature IDE integration than Vitest
747
+
748
+ - No browser mode, benchmarking, type testing, or sharding (features Vitest has)
749
+
750
+ **Fake timers** (added in Bun v1.3.4, Dec 2025):
751
+
752
+ Bun’s test runner now supports Jest-compatible fake timers via `jest.useFakeTimers()`,
753
+ `jest.advanceTimersByTime(ms)`, and `jest.useRealTimers()`. This was previously a major
754
+ gap. The system time mock (`setSystemTime` from `bun:test`) has been available longer.
755
+ Bun v1.3.6 added further compatibility fixes for `@testing-library/react` fake timer
756
+ detection.
757
+
758
+ **Comparison with Vitest / Node test runner**:
759
+
760
+ | Criteria | Bun test | Vitest | Node test runner |
761
+ | --- | --- | --- | --- |
762
+ | Speed | Fastest | Fast | Fast |
763
+ | Setup | Zero-config | Requires install + config | Zero-config (Node 20+) |
764
+ | TypeScript | Native | Via Vite transform | Via `--experimental-strip-types` or tsx |
765
+ | API | Jest-compatible | Jest-compatible | Node-native API |
766
+ | Watch mode | HMR-based | HMR-based | File-system based |
767
+ | Isolation | No isolation | Isolated by default | Isolated |
768
+ | Fake timers | Jest-compatible (v1.3.4+) | Full support | Full support |
769
+ | Coverage | Built-in | Built-in (c8/v8) | Built-in (Node 20+) |
770
+ | IDE support | Basic | Excellent (VS Code) | Moderate |
771
+ | Ecosystem | Growing | Mature | Node-native |
772
+
773
+ **Assessment**: With fake timers added in Bun v1.3.4, the main remaining gap is test
774
+ isolation and advanced features (browser mode, sharding).
775
+ Use `bun test` for most Bun-native projects.
776
+ For projects requiring test isolation or cross-runtime compatibility, Vitest remains the
777
+ safer choice.
778
+
779
+ **References**:
780
+
781
+ - [Bun Test Runner](https://bun.sh/docs/cli/test)
782
+
783
+ - [Comparing Test Frameworks: Jest vs Vitest vs Bun](https://dev.to/kcsujeet/your-tests-are-slow-you-need-to-migrate-to-bun-9hh)
784
+
785
+ - [Node Test Runner vs Bun Test Runner](https://dev.to/boscodomingo/node-test-runner-vs-bun-test-runner-with-typescript-and-esm-44ih)
786
+
787
+ * * *
788
+
789
+ ### 9. Code Formatting & Linting
790
+
791
+ #### Biome
792
+
793
+ **Status**: Recommended
794
+
795
+ **Details**:
796
+
797
+ Biome is an all-in-one formatter and linter written in Rust.
798
+ It replaces both Prettier and ESLint with a single tool, aligning well with the Bun
799
+ ecosystem’s philosophy of consolidation and speed.
800
+
801
+ **Key advantages**:
802
+
803
+ - **10–25x faster** than ESLint + Prettier combined
804
+
805
+ - **Single configuration file** (`biome.json`) instead of `.prettierrc` +
806
+ `eslint.config.js`
807
+
808
+ - **Single binary** instead of 127+ npm packages
809
+
810
+ - **97% Prettier-compatible** formatting
811
+
812
+ - **300+ lint rules** including type-aware linting (v2.0+, without requiring `tsc`)
813
+
814
+ - **Built-in migration** from ESLint and Prettier configs
815
+
816
+ - **v2.3.x** (latest as of Jan 2026) adds nursery rules for floating promises
817
+ (`noFloatingPromises`), duplicate HTML attributes, JSX prop binding, and more
818
+
819
+ **Installation**:
820
+
821
+ ```bash
822
+ bun add -d @biomejs/biome
823
+ ```
824
+
825
+ **`biome.json`**:
826
+
827
+ ```json
828
+ {
829
+ "$schema": "https://biomejs.dev/schemas/2.3.0/schema.json",
830
+ "organizeImports": {
831
+ "enabled": true
832
+ },
833
+ "formatter": {
834
+ "enabled": true,
835
+ "indentStyle": "space",
836
+ "indentWidth": 2,
837
+ "lineWidth": 100
838
+ },
839
+ "linter": {
840
+ "enabled": true,
841
+ "rules": {
842
+ "recommended": true,
843
+ "correctness": {
844
+ "noUnusedVariables": "error",
845
+ "noUnusedImports": "error"
846
+ },
847
+ "suspicious": {
848
+ "noExplicitAny": "warn"
849
+ },
850
+ "style": {
851
+ "useConst": "error",
852
+ "noNonNullAssertion": "warn"
853
+ }
854
+ }
855
+ },
856
+ "files": {
857
+ "ignore": ["dist", "node_modules", ".changeset"]
858
+ }
859
+ }
860
+ ```
861
+
862
+ **Scripts**:
863
+
864
+ ```json
865
+ {
866
+ "scripts": {
867
+ "format": "biome format --write .",
868
+ "format:check": "biome format .",
869
+ "lint": "biome lint --write .",
870
+ "lint:check": "biome lint .",
871
+ "check": "biome check --write .",
872
+ "check:ci": "biome check ."
873
+ }
874
+ }
875
+ ```
876
+
877
+ **Biome `check` vs separate commands**: `biome check` runs both formatting and linting
878
+ in a single pass, which is faster than running them separately.
879
+ Use `check` for local development and `check:ci` (without `--write`) for CI.
880
+
881
+ **Comparison with Prettier + ESLint**:
882
+
883
+ | Criteria | Biome | Prettier + ESLint |
884
+ | --- | --- | --- |
885
+ | Speed | 10–25x faster | Baseline |
886
+ | Config files | 1 (`biome.json`) | 2–4 files |
887
+ | npm packages | 1 | 5–10+ (with plugins) |
888
+ | Type-aware linting | v2.0+ (plugin) | ESLint + typescript-eslint |
889
+ | Language support | JS/TS/JSON/CSS | Broader (HTML, Markdown, SCSS, etc.) |
890
+ | Plugin ecosystem | Growing (v2.0) | Mature (decade of plugins) |
891
+ | IDE support | Good (VS Code, JetBrains) | Excellent |
892
+ | Prettier compatibility | 97% | 100% (is Prettier) |
893
+
894
+ **Known limitations**:
895
+
896
+ - Does not format HTML, Markdown, or SCSS (use Prettier alongside if needed)
897
+
898
+ - Younger plugin ecosystem than ESLint
899
+
900
+ - Missing some specialized ESLint plugins (security, accessibility)
901
+
902
+ **Assessment**: Biome is the natural choice for a full-Bun ecosystem.
903
+ It provides the same quality guarantees as Prettier + ESLint with dramatically less
904
+ configuration and better performance.
905
+ For projects that need HTML/Markdown formatting or specialized ESLint plugins, a hybrid
906
+ approach (Biome + targeted Prettier/ESLint) is viable.
907
+
908
+ **References**:
909
+
910
+ - [Biome Documentation](https://biomejs.dev/)
911
+
912
+ - [Biome vs ESLint + Prettier: The 2025 Linting Revolution](https://medium.com/better-dev-nextjs-react/biome-vs-eslint-prettier-the-2025-linting-revolution-you-need-to-know-about-ec01c5d5b6c8)
913
+
914
+ - [Migrating from Prettier and ESLint to BiomeJS](https://blog.appsignal.com/2025/05/07/migrating-a-javascript-project-from-prettier-and-eslint-to-biomejs.html)
915
+
916
+ * * *
917
+
918
+ ### 10. CI/CD Configuration
919
+
920
+ #### GitHub Actions: CI Workflow
921
+
922
+ **Status**: Recommended
923
+
924
+ **`.github/workflows/ci.yml`**:
925
+
926
+ ```yaml
927
+ name: CI
928
+
929
+ on:
930
+ pull_request:
931
+ push:
932
+ branches: [main]
933
+
934
+ jobs:
935
+ test:
936
+ runs-on: ubuntu-latest
937
+ steps:
938
+ - uses: actions/checkout@v6
939
+
940
+ - uses: oven-sh/setup-bun@v2
941
+ with:
942
+ bun-version: latest
943
+
944
+ - run: bun install --frozen-lockfile
945
+
946
+ - run: bun run check:ci
947
+ - run: bun run build
948
+ - run: bunx publint
949
+ - run: bun test
950
+ ```
951
+
952
+ **Key differences from pnpm CI**:
953
+
954
+ | Aspect | pnpm CI | Bun CI |
955
+ | --- | --- | --- |
956
+ | Setup action | `pnpm/action-setup@v4` + `actions/setup-node@v6` | `oven-sh/setup-bun@v2` (one action) |
957
+ | Install | `pnpm install --frozen-lockfile` | `bun install --frozen-lockfile` |
958
+ | Lockfile | `pnpm-lock.yaml` (YAML) | `bun.lock` (JSONC, text-based since Bun 1.2) |
959
+ | Lint + format | Separate `format:check` + `lint:check` | Single `biome check` |
960
+ | Node.js setup | Required | Not required (Bun includes runtime) |
961
+
962
+ **Assessment**: Bun CI is simpler — one setup action instead of two, and a single check
963
+ command instead of separate format and lint steps.
964
+ The text-based `bun.lock` (since Bun 1.2) is diffable in PRs, on par with
965
+ `pnpm-lock.yaml`.
966
+
967
+ **References**:
968
+
969
+ - [Bun CI/CD Guide](https://bun.sh/docs/guides/runtime/cicd)
970
+
971
+ - [oven-sh/setup-bun](https://github.com/oven-sh/setup-bun)
972
+
973
+ * * *
974
+
975
+ #### GitHub Actions: Release Workflow
976
+
977
+ **Status**: Recommended
978
+
979
+ **`.github/workflows/release.yml`**:
980
+
981
+ ```yaml
982
+ name: Release
983
+
984
+ on:
985
+ push:
986
+ branches: [main]
987
+
988
+ permissions:
989
+ contents: write
990
+ pull-requests: write
991
+
992
+ jobs:
993
+ release:
994
+ runs-on: ubuntu-latest
995
+ steps:
996
+ - uses: actions/checkout@v6
997
+ with:
998
+ fetch-depth: 0
999
+
1000
+ - uses: oven-sh/setup-bun@v2
1001
+ with:
1002
+ bun-version: latest
1003
+
1004
+ - run: bun install --frozen-lockfile
1005
+
1006
+ - name: Create Release PR or Publish
1007
+ id: changesets
1008
+ uses: changesets/action@v1
1009
+ with:
1010
+ version: bun run version-packages
1011
+ publish: bun run release
1012
+ env:
1013
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1014
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
1015
+ ```
1016
+
1017
+ **Important**: The `version` command must be `bun run version-packages` (which includes
1018
+ the `bun update` fixup), not just `changeset version`. The `publish` command uses the
1019
+ custom per-package publish script.
1020
+
1021
+ **Assessment**: Slightly more complex than the pnpm release workflow due to the
1022
+ Changesets workarounds, but functionally equivalent.
1023
+
1024
+ * * *
1025
+
1026
+ ### 11. Git Hooks & Local Validation
1027
+
1028
+ #### Lefthook with Biome
1029
+
1030
+ **Status**: Recommended
1031
+
1032
+ **Details**:
1033
+
1034
+ Lefthook works identically in the Bun ecosystem.
1035
+ The main change is replacing Prettier + ESLint commands with Biome.
1036
+
1037
+ **Installation**:
1038
+
1039
+ ```bash
1040
+ bun add -d lefthook
1041
+ bunx lefthook install
1042
+ ```
1043
+
1044
+ **`lefthook.yml`**:
1045
+
1046
+ ```yaml
1047
+ pre-commit:
1048
+ parallel: true
1049
+
1050
+ commands:
1051
+ # Format + lint with Biome (~200ms)
1052
+ check:
1053
+ glob: '*.{js,ts,tsx,json}'
1054
+ run: bunx biome check --write {staged_files}
1055
+ stage_fixed: true
1056
+ priority: 1
1057
+
1058
+ # Type check (~2s)
1059
+ typecheck:
1060
+ glob: '*.{ts,tsx}'
1061
+ run: bunx tsc --noEmit --incremental
1062
+ priority: 2
1063
+
1064
+ pre-push:
1065
+ commands:
1066
+ verify-tests:
1067
+ run: |
1068
+ COMMIT_HASH=$(git rev-parse HEAD)
1069
+ CACHE_DIR="node_modules/.test-cache"
1070
+ CACHE_FILE="$CACHE_DIR/$COMMIT_HASH"
1071
+
1072
+ if ! git diff --quiet || ! git diff --cached --quiet; then
1073
+ bun test
1074
+ exit $?
1075
+ fi
1076
+
1077
+ if [ -f "$CACHE_FILE" ]; then
1078
+ exit 0
1079
+ fi
1080
+
1081
+ bun test
1082
+
1083
+ if [ $? -eq 0 ]; then
1084
+ mkdir -p "$CACHE_DIR"
1085
+ touch "$CACHE_FILE"
1086
+ else
1087
+ exit 1
1088
+ fi
1089
+ ```
1090
+
1091
+ **Key advantage**: Using `biome check` in pre-commit combines formatting and linting
1092
+ into a single command, reducing hook complexity from 2–3 commands to 1.
1093
+
1094
+ **Assessment**: Simpler and faster hooks than the pnpm equivalent thanks to Biome’s
1095
+ unified check command.
1096
+
1097
+ * * *
1098
+
1099
+ ### 12. Standalone Executable Compilation
1100
+
1101
+ #### Bun Compile
1102
+
1103
+ **Status**: Unique Capability
1104
+
1105
+ **Details**:
1106
+
1107
+ Bun can compile TypeScript/JavaScript into standalone executables that include the Bun
1108
+ runtime. This is a capability not available in the pnpm/Node.js ecosystem without
1109
+ third-party tools like `pkg` (deprecated) or `vercel/pkg`.
1110
+
1111
+ **Basic usage**:
1112
+
1113
+ ```bash
1114
+ bun build --compile ./src/bin.ts --outfile myapp
1115
+ ```
1116
+
1117
+ **Cross-compilation**:
1118
+
1119
+ ```bash
1120
+ # Build for Linux x64 from any platform
1121
+ bun build --compile --target=bun-linux-x64 ./src/bin.ts --outfile myapp
1122
+
1123
+ # Build for Windows
1124
+ bun build --compile --target=bun-windows-x64 ./src/bin.ts --outfile myapp.exe
1125
+
1126
+ # Build for macOS ARM
1127
+ bun build --compile --target=bun-darwin-arm64 ./src/bin.ts --outfile myapp
1128
+ ```
1129
+
1130
+ **Via Bunup config**:
1131
+
1132
+ ```typescript
1133
+ import { defineConfig } from 'bunup';
1134
+
1135
+ export default defineConfig({
1136
+ entry: 'src/bin.ts',
1137
+ compile: true, // Compile for current platform
1138
+ });
1139
+
1140
+ // Or cross-compile:
1141
+ export default defineConfig({
1142
+ entry: 'src/bin.ts',
1143
+ compile: {
1144
+ target: 'bun-linux-x64',
1145
+ outfile: './bin/myapp',
1146
+ },
1147
+ });
1148
+ ```
1149
+
1150
+ **Embedding assets**:
1151
+
1152
+ ```typescript
1153
+ // Files can be embedded directly into the binary
1154
+ const config = await Bun.file(import.meta.dir + '/config.json').text();
1155
+ ```
1156
+
1157
+ **Available targets**:
1158
+
1159
+ | Target | Architecture |
1160
+ | --- | --- |
1161
+ | `bun-linux-x64` | Linux x86_64 |
1162
+ | `bun-linux-arm64` | Linux ARM64 |
1163
+ | `bun-darwin-x64` | macOS Intel |
1164
+ | `bun-darwin-arm64` | macOS Apple Silicon |
1165
+ | `bun-windows-x64` | Windows x86_64 |
1166
+
1167
+ **Optimization flags**:
1168
+
1169
+ ```bash
1170
+ # Reduce startup time (not binary size) with bytecode pre-compilation
1171
+ bun build --compile --bytecode --minify ./src/bin.ts --outfile myapp
1172
+
1173
+ # Minify source to reduce embedded code size
1174
+ bun build --compile --minify --sourcemap ./src/bin.ts --outfile myapp
1175
+ ```
1176
+
1177
+ **Known limitations**:
1178
+
1179
+ - Binary size starts at ~50–100MB (includes Bun runtime).
1180
+ This is an open issue ([#5854](https://github.com/oven-sh/bun/issues/5854)).
1181
+
1182
+ - Embedded directory support is beta-quality
1183
+
1184
+ - The executable is Bun-runtime-specific (not a true native binary like Go/Rust)
1185
+
1186
+ **Comparison with other distribution methods**:
1187
+
1188
+ | Method | Size | Runtime needed | Cross-platform | Speed |
1189
+ | --- | --- | --- | --- | --- |
1190
+ | npm publish | Small (~KB) | Node.js or Bun | Yes | Startup depends on runtime |
1191
+ | `bun --compile` | ~50–100MB | None | Cross-compile | Fast startup |
1192
+ | Go binary | ~10–20MB | None | Cross-compile | Fast startup |
1193
+ | Rust binary | ~5–15MB | None | Cross-compile | Fast startup |
1194
+
1195
+ **Assessment**: Bun’s compile feature is valuable for distributing CLI tools to users
1196
+ who don’t have Node.js or Bun installed.
1197
+ The binary size is the main downside.
1198
+ This is a unique advantage that the pnpm ecosystem does not offer natively.
1199
+
1200
+ **References**:
1201
+
1202
+ - [Bun Single-file Executables](https://bun.sh/docs/bundler/executables)
1203
+
1204
+ - [Bunup Compile Documentation](https://bunup.dev/docs/advanced/compile.html)
1205
+
1206
+ - [Bun Cross-Compilation](https://developer.mamezou-tech.com/en/blogs/2024/05/20/bun-cross-compile/)
1207
+
1208
+ * * *
1209
+
1210
+ ### 13. Dependency Upgrade Management
1211
+
1212
+ **Status**: Recommended
1213
+
1214
+ **Details**:
1215
+
1216
+ npm-check-updates (`ncu`) works with Bun.
1217
+ The main difference is using `bun install` instead of `pnpm install` after updates.
1218
+
1219
+ **Root `package.json` scripts**:
1220
+
1221
+ ```json
1222
+ {
1223
+ "scripts": {
1224
+ "upgrade:check": "bunx npm-check-updates --format group",
1225
+ "upgrade": "bunx npm-check-updates --target minor -u && bun install && bun test",
1226
+ "upgrade:major": "bunx npm-check-updates --target latest --interactive --format group"
1227
+ }
1228
+ }
1229
+ ```
1230
+
1231
+ **Assessment**: Identical workflow.
1232
+ `bunx` replaces `npx`.
1233
+
1234
+ * * *
1235
+
1236
+ ### 14. CLI Development Workflow
1237
+
1238
+ #### Running CLI from Source
1239
+
1240
+ **Status**: Strongly Recommended
1241
+
1242
+ **Details**:
1243
+
1244
+ Bun natively executes TypeScript, eliminating the need for `tsx` entirely.
1245
+
1246
+ **The dual-script pattern** (simplified vs pnpm):
1247
+
1248
+ ```json
1249
+ {
1250
+ "scripts": {
1251
+ "cli-name": "bun packages/package-name/src/cli/bin.ts",
1252
+ "cli-name:bin": "bun packages/package-name/dist/bin.js"
1253
+ }
1254
+ }
1255
+ ```
1256
+
1257
+ **Comparison with pnpm approach**:
1258
+
1259
+ | Aspect | pnpm (tsx) | Bun |
1260
+ | --- | --- | --- |
1261
+ | Dev command | `tsx packages/pkg/src/cli/bin.ts` | `bun packages/pkg/src/cli/bin.ts` |
1262
+ | Extra dependency | `tsx` (~4.21.0) | None (built-in) |
1263
+ | TypeScript support | Via esbuild transform | Native |
1264
+ | Startup time | ~50ms | ~5ms |
1265
+
1266
+ **Assessment**: Bun eliminates the `tsx` dependency entirely.
1267
+ One fewer devDependency, faster startup, simpler scripts.
1268
+
1269
+ * * *
1270
+
1271
+ ### 15. Private Package Distribution
1272
+
1273
+ **Status**: Same as pnpm
1274
+
1275
+ **Details**:
1276
+
1277
+ Distribution options are identical:
1278
+
1279
+ 1. **GitHub Packages** (recommended for teams)
1280
+
1281
+ 2. **Direct GitHub install**: `bun add github:org/repo` — Bun has limited support for
1282
+ monorepo subdirectory installs (lagging behind pnpm)
1283
+
1284
+ 3. **Local linking**: `bun link` works similarly to `pnpm link`
1285
+
1286
+ **Bun-specific option**: Standalone executables via `bun --compile` provide an
1287
+ additional distribution path that bypasses package registries entirely.
1288
+
1289
+ * * *
1290
+
1291
+ ### 16. Library/CLI Hybrid Packages
1292
+
1293
+ **Status**: Same as pnpm
1294
+
1295
+ **Details**:
1296
+
1297
+ The Node-free core pattern from the pnpm research applies identically.
1298
+ Keep `node:` imports out of core library code and isolated to CLI-specific modules.
1299
+
1300
+ The only addition for Bun projects: you may also want to avoid Bun-specific APIs
1301
+ (`Bun.file()`, `Bun.serve()`, etc.)
1302
+ in core library code if the library needs to work in Node.js environments.
1303
+
1304
+ * * *
1305
+
1306
+ ## Comparative Analysis
1307
+
1308
+ ### Full-Stack Tooling Comparison
1309
+
1310
+ | Concern | pnpm Monorepo | Bun Monorepo |
1311
+ | --- | --- | --- |
1312
+ | **Package manager** | pnpm | Bun |
1313
+ | **Runtime** | Node.js | Bun |
1314
+ | **Build tool** | tsdown | Bunup |
1315
+ | **Test runner** | Node test / Vitest | bun test |
1316
+ | **Formatter** | Prettier | Biome |
1317
+ | **Linter** | ESLint | Biome |
1318
+ | **Git hooks** | Lefthook | Lefthook |
1319
+ | **Versioning** | Changesets | Changesets (with workarounds) |
1320
+ | **Validation** | publint | publint |
1321
+ | **Dev execution** | tsx | Bun (native TS) |
1322
+ | **Native binaries** | N/A | bun --compile |
1323
+ | **CI setup** | 2 actions (pnpm + node) | 1 action (setup-bun) |
1324
+
1325
+ ### Complexity Comparison
1326
+
1327
+ | Aspect | pnpm | Bun | Winner |
1328
+ | --- | --- | --- | --- |
1329
+ | **Config file count** | 6–8 | 4–5 | Bun |
1330
+ | **devDependencies** | 10–15 | 5–8 | Bun |
1331
+ | **Setup actions (CI)** | 2 | 1 | Bun |
1332
+ | **Lint + format config** | 2–4 files | 1 file | Bun |
1333
+ | **Build config** | Equivalent | Equivalent | Tie |
1334
+ | **Changesets workflow** | Works natively | Requires workarounds | pnpm |
1335
+ | **Dependency isolation** | Strict (content-addressable) | Loose (flat node_modules) | pnpm |
1336
+ | **Lockfile diffability** | YAML (diffable) | JSONC (diffable since Bun 1.2) | Tie |
1337
+ | **Ecosystem maturity** | Very mature | Growing rapidly (Anthropic backing) | pnpm |
1338
+ | **Test runner maturity** | Vitest (mature) | bun test (fake timers added, isolation gap) | pnpm |
1339
+
1340
+ ### Speed Comparison
1341
+
1342
+ | Operation | pnpm + Node.js | Bun | Improvement |
1343
+ | --- | --- | --- | --- |
1344
+ | Package install | Baseline | 2–10x faster | Bun |
1345
+ | Build (tsdown vs Bunup) | ~200ms–1s | ~37ms | 5–25x (Bun) |
1346
+ | Test execution | Baseline | ~2x faster | Bun |
1347
+ | Lint + format | ~3–5s | ~200ms | 10–25x (Biome) |
1348
+ | CI total | Baseline | Significantly faster | Bun |
1349
+ | Dev server startup | ~50ms (tsx) | ~5ms | 10x (Bun) |
1350
+
1351
+ * * *
1352
+
1353
+ ## Best Practices
1354
+
1355
+ 1. **Use Bun workspaces** with `"workspaces"` in root `package.json`. Use `--cwd` to
1356
+ target specific workspaces when adding dependencies.
1357
+
1358
+ 2. **Enable `isolatedDeclarations`** in `tsconfig.base.json` for dramatically faster DTS
1359
+ generation with Bunup.
1360
+
1361
+ 3. **Use Bunup’s auto-exports** (`exports: true`) to keep `package.json` exports
1362
+ synchronized with build output.
1363
+
1364
+ 4. **Use Biome for formatting + linting** via a single `biome.json`. Use `biome check`
1365
+ for combined format + lint in one pass.
1366
+
1367
+ 5. **Add `bun update` after `changeset version`** to fix workspace reference resolution
1368
+ in the lockfile.
1369
+
1370
+ 6. **Use `bun publish` per package** instead of `changeset publish` to ensure proper
1371
+ workspace resolution.
1372
+
1373
+ 7. **Run CLI from source with `bun`** directly — no need for `tsx` or any TypeScript
1374
+ execution wrapper.
1375
+
1376
+ 8. **Consider `bun --compile`** for distributing CLI tools as standalone executables,
1377
+ especially for users who don’t have Node.js or Bun installed.
1378
+
1379
+ 9. **Use `bun test`** for testing — fake timers are now supported (v1.3.4+). Switch to
1380
+ Vitest only if you need test isolation, browser mode, or sharding.
1381
+
1382
+ 10. **Keep the root `package.json` private** with `"private": true` and only workspace
1383
+ tooling.
1384
+
1385
+ 11. **Scope your package names** with `@org/package-name` for GitHub Packages
1386
+ compatibility.
1387
+
1388
+ 12. **Validate before publish** with `publint` in CI and before every release.
1389
+
1390
+ 13. **Use lefthook** for git hooks with `biome check` in pre-commit (single command
1391
+ replaces separate format + lint hooks).
1392
+
1393
+ 14. **Add the `"bun"` export condition** to let Bun consumers import TypeScript source
1394
+ directly, bypassing compiled output.
1395
+
1396
+ 15. **Use dynamic git-based versioning** for dev builds — the pattern works identically
1397
+ with Bun, and `bun` replaces `tsx` for script execution.
1398
+
1399
+ * * *
1400
+
1401
+ ## Open Research Questions
1402
+
1403
+ 1. **Changesets Bun support**: As of Jan 2026, Changesets still has no native Bun
1404
+ workspace support. The `workspace:*` resolution issue remains open
1405
+ ([#1468](https://github.com/changesets/changesets/issues/1468),
1406
+ [oven-sh/bun#16074](https://github.com/oven-sh/bun/issues/16074)). The workaround
1407
+ (`bun update` after `changeset version`, per-package `bun publish`) remains
1408
+ necessary. Monitor for a Bun-native alternative.
1409
+
1410
+ 2. ~~**Bun test runner: fake timers**~~: **RESOLVED** in Bun v1.3.4 (Dec 2025).
1411
+ Jest-compatible fake timers are now supported.
1412
+ Remaining gaps: test isolation, browser mode, sharding.
1413
+
1414
+ 3. ~~**Bun lockfile format**~~: **RESOLVED** in Bun 1.2. The text-based `bun.lock`
1415
+ (JSONC format) is now the default.
1416
+ It is diffable in code review and supported by GitHub rendering.
1417
+ The binary `bun.lockb` is deprecated.
1418
+
1419
+ 4. **Biome plugin ecosystem**: Biome v2.0+ introduced plugins and type-aware linting
1420
+ without `tsc`. As of v2.3.x, the rule set continues growing (300+ rules,
1421
+ `noFloatingPromises` in nursery).
1422
+ Monitor for parity with security and accessibility ESLint plugins.
1423
+ Custom ESLint rules remain a reason to keep ESLint (see craft-agents-oss case study
1424
+ in Appendix G).
1425
+
1426
+ 5. **Bun workspace strictness**: Bun still uses flat `node_modules` (no
1427
+ content-addressable store).
1428
+ Phantom dependencies remain possible.
1429
+ Monitor for improvements.
1430
+
1431
+ 6. **`bun --compile` binary size**: Standalone executables still start at ~50–100MB
1432
+ (includes Bun runtime).
1433
+ No significant size reductions announced.
1434
+ The open issue ([#5854](https://github.com/oven-sh/bun/issues/5854)) remains tracked.
1435
+ The `--minify` and `--bytecode` flags help with startup time but not binary size.
1436
+
1437
+ 7. **TypeScript 6.0/7.0**: TypeScript 6.0 is a “bridge” release; TypeScript 7.0 (Go
1438
+ rewrite) promises 10x faster builds.
1439
+ TS 7.0 is now available in Visual Studio 2026 Insiders preview with ~8x faster
1440
+ project load times. Install via `npm install -D @typescript/native-preview`. May
1441
+ change the DTS generation landscape for Bunup and tsdown once stable.
1442
+
1443
+ 8. **Bunup maturity**: Bunup is iterating rapidly (0.16.x as of Jan 2026, up from 0.4.x
1444
+ a few months earlier).
1445
+ The API surface (`defineConfig`, `defineWorkspace`, `exports`, `compile`) appears
1446
+ stable, but pin versions carefully.
1447
+
1448
+ 9. **Bun + Anthropic**: Bun was acquired by Anthropic (Dec 2025) and now powers Claude
1449
+ Code and Claude Agent SDK. This signals strong continued investment but also a shift
1450
+ toward AI-tooling use cases.
1451
+ Monitor whether the broader open-source community continues to benefit equally.
1452
+
1453
+ * * *
1454
+
1455
+ ## Recommendations
1456
+
1457
+ ### Summary
1458
+
1459
+ For projects that prioritize **speed and simplicity**, the full-Bun ecosystem provides a
1460
+ compelling alternative to the pnpm stack.
1461
+ Fewer configuration files, fewer dependencies, faster builds, and native TypeScript
1462
+ execution reduce overall complexity.
1463
+
1464
+ For projects that prioritize **ecosystem maturity and strictness**, the pnpm stack
1465
+ remains the safer choice, particularly due to pnpm’s strict dependency isolation,
1466
+ Changesets’ native support, ESLint’s mature plugin ecosystem, and Vitest’s comprehensive
1467
+ testing features.
1468
+
1469
+ ### When to Choose the Bun Ecosystem
1470
+
1471
+ - New projects without legacy Node.js dependencies
1472
+
1473
+ - Projects that value speed and simplicity over ecosystem maturity
1474
+
1475
+ - CLI tools that could benefit from standalone executable distribution
1476
+
1477
+ - Teams comfortable with occasional workarounds for younger tooling
1478
+
1479
+ - Projects where the Bun runtime will be the primary execution environment
1480
+
1481
+ ### When to Stay with pnpm
1482
+
1483
+ - Projects with strict dependency isolation requirements
1484
+
1485
+ - Projects requiring mature ESLint plugins (security, accessibility, custom rules)
1486
+
1487
+ - Projects needing advanced testing features (test isolation, browser mode, sharding)
1488
+
1489
+ - Projects that must support Node.js as the primary runtime
1490
+
1491
+ ### Recommended Approach (Full-Bun)
1492
+
1493
+ 1. **Initialize workspace** with Bun and packages in `packages/`
1494
+
1495
+ 2. **Configure Bunup** for dual ESM/CJS output with auto-exports
1496
+
1497
+ 3. **Enable `isolatedDeclarations`** in TypeScript config
1498
+
1499
+ 4. **Set up Biome** for formatting and linting
1500
+
1501
+ 5. **Add Changesets** with the Bun workaround scripts
1502
+
1503
+ 6. **Configure lefthook** with `biome check` in pre-commit
1504
+
1505
+ 7. **Set up CI** with `oven-sh/setup-bun@v2`
1506
+
1507
+ 8. **Configure release workflow** with custom publish scripts
1508
+
1509
+ 9. **Validate with publint** before every release
1510
+
1511
+ 10. **Optionally configure `bun --compile`** for standalone executable distribution
1512
+
1513
+ * * *
1514
+
1515
+ ## References
1516
+
1517
+ ### Official Documentation
1518
+
1519
+ - [Bun Documentation](https://bun.sh/docs)
1520
+
1521
+ - [Bun Workspaces](https://bun.sh/docs/install/workspaces)
1522
+
1523
+ - [Bun TypeScript](https://bun.sh/docs/typescript)
1524
+
1525
+ - [Bun Test Runner](https://bun.sh/docs/cli/test)
1526
+
1527
+ - [Bun Bundler](https://bun.sh/docs/bundler)
1528
+
1529
+ - [Bun Single-file Executables](https://bun.sh/docs/bundler/executables)
1530
+
1531
+ - [Bunup Documentation](https://bunup.dev/)
1532
+
1533
+ - [Biome Documentation](https://biomejs.dev/)
1534
+
1535
+ - [Changesets GitHub](https://github.com/changesets/changesets)
1536
+
1537
+ - [publint Documentation](https://publint.dev/docs/)
1538
+
1539
+ ### Guides & Articles
1540
+
1541
+ - [Building a TypeScript Library in 2026 with Bunup](https://dev.to/arshadyaseen/building-a-typescript-library-in-2026-with-bunup-3bmg)
1542
+
1543
+ - [Setting up Changesets with Bun Workspaces](https://ianm.com/posts/2025-08-18-setting-up-changesets-with-bun-workspaces)
1544
+
1545
+ - [Monorepo with Bun](https://dev.to/vikkio88/monorepo-with-bun-474n)
1546
+
1547
+ - [Guide to Monorepo Setup: NPM, Yarn, Pnpm & Bun Workspaces](https://jsdev.space/mastering-monorepos/)
1548
+
1549
+ - [Setting up a Bun Workspace](https://medium.com/@oluijks/setting-up-a-bun-workspace-23543df61e52)
1550
+
1551
+ - [Biome vs ESLint + Prettier](https://medium.com/better-dev-nextjs-react/biome-vs-eslint-prettier-the-2025-linting-revolution-you-need-to-know-about-ec01c5d5b6c8)
1552
+
1553
+ - [Migrating from Prettier and ESLint to BiomeJS](https://blog.appsignal.com/2025/05/07/migrating-a-javascript-project-from-prettier-and-eslint-to-biomejs.html)
1554
+
1555
+ - [CommonJS is not going away (Bun Blog)](https://bun.sh/blog/commonjs-is-not-going-away)
1556
+
1557
+ - [Bun’s New Text-Based Lockfile](https://bun.com/blog/bun-lock-text-lockfile)
1558
+
1559
+ - [Fake Timers in Bun Test](https://js2brain.com/blog/fake-timers-in-bun-test/)
1560
+
1561
+ - [Bun v1.3.4 Release (Fake Timers)](https://bun.com/blog/bun-v1.3.4)
1562
+
1563
+ - [Bun Cross-Compilation](https://developer.mamezou-tech.com/en/blogs/2024/05/20/bun-cross-compile/)
1564
+
1565
+ - [Bun Joins Anthropic](https://www.infoq.com/news/2026/01/bun-v3-1-release/)
1566
+
1567
+ ### GitHub Actions
1568
+
1569
+ - [oven-sh/setup-bun](https://github.com/oven-sh/setup-bun)
1570
+
1571
+ - [changesets/action](https://github.com/changesets/action)
1572
+
1573
+ * * *
1574
+
1575
+ ## Appendices
1576
+
1577
+ ### Appendix A: Complete Package package.json Example
1578
+
1579
+ ```json
1580
+ {
1581
+ "name": "@scope/package-name",
1582
+ "version": "0.1.0",
1583
+ "description": "Package description",
1584
+ "license": "MIT",
1585
+ "type": "module",
1586
+ "sideEffects": false,
1587
+ "main": "./dist/index.cjs",
1588
+ "module": "./dist/index.js",
1589
+ "types": "./dist/index.d.ts",
1590
+ "exports": {
1591
+ ".": {
1592
+ "bun": "./src/index.ts",
1593
+ "import": {
1594
+ "types": "./dist/index.d.ts",
1595
+ "default": "./dist/index.js"
1596
+ },
1597
+ "require": {
1598
+ "types": "./dist/index.d.cts",
1599
+ "default": "./dist/index.cjs"
1600
+ }
1601
+ },
1602
+ "./cli": {
1603
+ "bun": "./src/cli/index.ts",
1604
+ "import": {
1605
+ "types": "./dist/cli.d.ts",
1606
+ "default": "./dist/cli.js"
1607
+ },
1608
+ "require": {
1609
+ "types": "./dist/cli.d.cts",
1610
+ "default": "./dist/cli.cjs"
1611
+ }
1612
+ },
1613
+ "./package.json": "./package.json"
1614
+ },
1615
+ "bin": {
1616
+ "package-name": "./dist/bin.js"
1617
+ },
1618
+ "files": ["dist"],
1619
+ "scripts": {
1620
+ "build": "bunup",
1621
+ "dev": "bunup --watch",
1622
+ "typecheck": "tsc -p tsconfig.json --noEmit",
1623
+ "test": "bun test",
1624
+ "publint": "bunx publint",
1625
+ "prepack": "bun run build"
1626
+ },
1627
+ "dependencies": {},
1628
+ "peerDependencies": {
1629
+ "optional-sdk": "^1.0.0"
1630
+ },
1631
+ "peerDependenciesMeta": {
1632
+ "optional-sdk": { "optional": true }
1633
+ },
1634
+ "devDependencies": {
1635
+ "bun-types": "latest",
1636
+ "bunup": "^0.16.0",
1637
+ "typescript": "^5.9.0"
1638
+ }
1639
+ }
1640
+ ```
1641
+
1642
+ ### Appendix B: Root package.json Example
1643
+
1644
+ ```json
1645
+ {
1646
+ "name": "project-workspace",
1647
+ "private": true,
1648
+ "workspaces": [
1649
+ "packages/*"
1650
+ ],
1651
+ "scripts": {
1652
+ "build": "bunup",
1653
+ "typecheck": "tsc -b",
1654
+ "test": "bun test",
1655
+ "publint": "bunx publint",
1656
+ "format": "biome format --write .",
1657
+ "format:check": "biome format .",
1658
+ "lint": "biome lint --write .",
1659
+ "lint:check": "biome lint .",
1660
+ "check": "biome check --write .",
1661
+ "check:ci": "biome check .",
1662
+ "prepare": "lefthook install",
1663
+ "changeset": "changeset",
1664
+ "version-packages": "changeset version && bun update",
1665
+ "release": "bun run build && bunx publint && bun run publish-packages",
1666
+ "publish-packages": "for dir in packages/*; do (cd \"$dir\" && bun publish || true); done && changeset tag",
1667
+ "upgrade:check": "bunx npm-check-updates --format group",
1668
+ "upgrade": "bunx npm-check-updates --target minor -u && bun install && bun test",
1669
+ "upgrade:major": "bunx npm-check-updates --target latest --interactive --format group"
1670
+ },
1671
+ "devDependencies": {
1672
+ "@biomejs/biome": "^2.3.0",
1673
+ "@changesets/cli": "^2.29.0",
1674
+ "@changesets/changelog-github": "^0.5.0",
1675
+ "lefthook": "^2.0.0",
1676
+ "publint": "^0.3.0",
1677
+ "typescript": "^5.9.0"
1678
+ }
1679
+ }
1680
+ ```
1681
+
1682
+ ### Appendix C: Complete biome.json Example
1683
+
1684
+ ```json
1685
+ {
1686
+ "$schema": "https://biomejs.dev/schemas/2.3.0/schema.json",
1687
+ "organizeImports": {
1688
+ "enabled": true
1689
+ },
1690
+ "formatter": {
1691
+ "enabled": true,
1692
+ "indentStyle": "space",
1693
+ "indentWidth": 2,
1694
+ "lineWidth": 100,
1695
+ "lineEnding": "lf"
1696
+ },
1697
+ "javascript": {
1698
+ "formatter": {
1699
+ "quoteStyle": "single",
1700
+ "trailingCommas": "all",
1701
+ "semicolons": "always",
1702
+ "arrowParentheses": "always"
1703
+ }
1704
+ },
1705
+ "linter": {
1706
+ "enabled": true,
1707
+ "rules": {
1708
+ "recommended": true,
1709
+ "correctness": {
1710
+ "noUnusedVariables": "error",
1711
+ "noUnusedImports": "error",
1712
+ "useExhaustiveDependencies": "warn"
1713
+ },
1714
+ "suspicious": {
1715
+ "noExplicitAny": "warn",
1716
+ "noConfusingVoidType": "error"
1717
+ },
1718
+ "style": {
1719
+ "useConst": "error",
1720
+ "noNonNullAssertion": "warn",
1721
+ "useImportType": "error"
1722
+ },
1723
+ "complexity": {
1724
+ "noForEach": "warn"
1725
+ },
1726
+ "nursery": {
1727
+ "noFloatingPromises": "error"
1728
+ }
1729
+ }
1730
+ },
1731
+ "overrides": [
1732
+ {
1733
+ "include": ["**/*.test.ts", "**/*.spec.ts", "**/tests/**/*.ts"],
1734
+ "linter": {
1735
+ "rules": {
1736
+ "suspicious": {
1737
+ "noExplicitAny": "off"
1738
+ }
1739
+ }
1740
+ }
1741
+ },
1742
+ {
1743
+ "include": ["**/scripts/**/*.ts"],
1744
+ "linter": {
1745
+ "rules": {
1746
+ "suspicious": {
1747
+ "noExplicitAny": "off"
1748
+ }
1749
+ }
1750
+ }
1751
+ }
1752
+ ],
1753
+ "files": {
1754
+ "ignore": [
1755
+ "dist",
1756
+ "node_modules",
1757
+ ".changeset",
1758
+ "bun.lock",
1759
+ "coverage"
1760
+ ]
1761
+ }
1762
+ }
1763
+ ```
1764
+
1765
+ ### Appendix D: Complete bunup.config.ts Example
1766
+
1767
+ ```typescript
1768
+ // bunup.config.ts (workspace root)
1769
+ import { defineWorkspace } from 'bunup';
1770
+
1771
+ export default defineWorkspace([
1772
+ {
1773
+ name: 'core',
1774
+ root: 'packages/core',
1775
+ config: {
1776
+ entry: ['src/index.ts'],
1777
+ format: ['esm', 'cjs'],
1778
+ dts: true,
1779
+ clean: true,
1780
+ sourcemap: 'linked',
1781
+ exports: true,
1782
+ },
1783
+ },
1784
+ {
1785
+ name: 'cli',
1786
+ root: 'packages/cli',
1787
+ config: [
1788
+ {
1789
+ name: 'library',
1790
+ entry: ['src/index.ts'],
1791
+ format: ['esm', 'cjs'],
1792
+ dts: true,
1793
+ clean: true,
1794
+ exports: true,
1795
+ },
1796
+ {
1797
+ name: 'binary',
1798
+ entry: ['src/bin.ts'],
1799
+ format: ['esm'],
1800
+ banner: '"#!/usr/bin/env bun";',
1801
+ },
1802
+ ],
1803
+ },
1804
+ ]);
1805
+ ```
1806
+
1807
+ **Single-package config** (`packages/core/bunup.config.ts`):
1808
+
1809
+ ```typescript
1810
+ import { defineConfig } from 'bunup';
1811
+
1812
+ export default defineConfig({
1813
+ entry: {
1814
+ index: 'src/index.ts',
1815
+ cli: 'src/cli/index.ts',
1816
+ bin: 'src/bin.ts',
1817
+ },
1818
+ format: ['esm', 'cjs'],
1819
+ dts: true,
1820
+ clean: true,
1821
+ sourcemap: 'linked',
1822
+ exports: true,
1823
+ define: {
1824
+ __VERSION__: JSON.stringify(process.env.PROJECT_VERSION ?? 'development'),
1825
+ },
1826
+ });
1827
+ ```
1828
+
1829
+ ### Appendix E: Complete lefthook.yml Example
1830
+
1831
+ ```yaml
1832
+ # lefthook.yml
1833
+ # Git hooks for code quality (Bun + Biome edition)
1834
+ # Pre-commit: Fast checks with auto-fix (target: <1 second)
1835
+ # Pre-push: Full test validation with caching
1836
+
1837
+ pre-commit:
1838
+ parallel: true
1839
+
1840
+ commands:
1841
+ # Format + lint with Biome (~200ms for staged files)
1842
+ check:
1843
+ glob: '*.{js,ts,tsx,json,css}'
1844
+ run: bunx biome check --write {staged_files}
1845
+ stage_fixed: true
1846
+ priority: 1
1847
+
1848
+ # Type check with incremental mode (~2s)
1849
+ typecheck:
1850
+ glob: '*.{ts,tsx}'
1851
+ run: bunx tsc --noEmit --incremental
1852
+ priority: 2
1853
+
1854
+ pre-push:
1855
+ commands:
1856
+ verify-tests:
1857
+ run: |
1858
+ COMMIT_HASH=$(git rev-parse HEAD)
1859
+ CACHE_DIR="node_modules/.test-cache"
1860
+ CACHE_FILE="$CACHE_DIR/$COMMIT_HASH"
1861
+
1862
+ # Check for uncommitted changes
1863
+ if ! git diff --quiet || ! git diff --cached --quiet; then
1864
+ bun test
1865
+ exit $?
1866
+ fi
1867
+
1868
+ # Check cache
1869
+ if [ -f "$CACHE_FILE" ]; then
1870
+ SHORT_HASH=$(echo "$COMMIT_HASH" | cut -c1-8)
1871
+ echo "Tests already passed for commit $SHORT_HASH"
1872
+ exit 0
1873
+ fi
1874
+
1875
+ # Run tests
1876
+ bun test
1877
+
1878
+ # Cache on success
1879
+ if [ $? -eq 0 ]; then
1880
+ mkdir -p "$CACHE_DIR"
1881
+ touch "$CACHE_FILE"
1882
+ exit 0
1883
+ else
1884
+ echo "Tests failed - push blocked"
1885
+ echo "Bypass with: git push --no-verify"
1886
+ exit 1
1887
+ fi
1888
+ ```
1889
+
1890
+ ### Appendix F: Standalone Executable Build Example
1891
+
1892
+ ```typescript
1893
+ // bunup.config.ts for CLI with standalone executable
1894
+ import { defineConfig } from 'bunup';
1895
+
1896
+ export default defineConfig([
1897
+ // Standard library build
1898
+ {
1899
+ name: 'library',
1900
+ entry: ['src/index.ts'],
1901
+ format: ['esm', 'cjs'],
1902
+ dts: true,
1903
+ exports: true,
1904
+ },
1905
+ // Standalone executable
1906
+ {
1907
+ name: 'standalone',
1908
+ entry: 'src/bin.ts',
1909
+ compile: {
1910
+ target: 'bun-linux-x64',
1911
+ outfile: './bin/myapp-linux',
1912
+ },
1913
+ },
1914
+ ]);
1915
+ ```
1916
+
1917
+ **Multi-platform build script** (`package.json`):
1918
+
1919
+ ```json
1920
+ {
1921
+ "scripts": {
1922
+ "build:binary": "bun run build:binary:linux && bun run build:binary:mac && bun run build:binary:windows",
1923
+ "build:binary:linux": "bun build --compile --target=bun-linux-x64 src/bin.ts --outfile bin/myapp-linux",
1924
+ "build:binary:mac": "bun build --compile --target=bun-darwin-arm64 src/bin.ts --outfile bin/myapp-darwin",
1925
+ "build:binary:windows": "bun build --compile --target=bun-windows-x64 src/bin.ts --outfile bin/myapp.exe"
1926
+ }
1927
+ }
1928
+ ```
1929
+
1930
+ ### Appendix G: Case Study — craft-agents-oss (Real-World Bun Monorepo)
1931
+
1932
+ **Repository**:
1933
+ [lukilabs/craft-agents-oss](https://github.com/lukilabs/craft-agents-oss) (Apache 2.0,
1934
+ Electron desktop app for AI agent interactions)
1935
+
1936
+ This appendix documents how a real-world, production Bun monorepo is structured.
1937
+ It serves as a concrete reference implementation for the patterns described in this
1938
+ research.
1939
+
1940
+ #### Structure Overview
1941
+
1942
+ ```
1943
+ craft-agents-oss/
1944
+ ├── apps/
1945
+ │ ├── electron/ # Electron desktop app (primary product)
1946
+ │ └── viewer/ # Web viewer for sessions
1947
+ ├── packages/
1948
+ │ ├── core/ # Types and core utilities (no deps)
1949
+ │ ├── shared/ # Business logic (agent, auth, config, MCP)
1950
+ │ └── ui/ # React UI components
1951
+ ├── scripts/ # Bun build/dev scripts (TypeScript)
1952
+ ├── biome.json # (not present — uses ESLint)
1953
+ ├── bunfig.toml # Minimal: preload only
1954
+ ├── bun.lock # Text lockfile (JSONC, default since Bun 1.2)
1955
+ ├── package.json # Root workspace config
1956
+ └── tsconfig.json # Root TypeScript config
1957
+ ```
1958
+
1959
+ **Workspace configuration** (root `package.json`):
1960
+
1961
+ ```json
1962
+ {
1963
+ "name": "craft-agent",
1964
+ "version": "0.2.34",
1965
+ "type": "module",
1966
+ "private": true,
1967
+ "workspaces": [
1968
+ "packages/*",
1969
+ "apps/*",
1970
+ "!apps/online-docs"
1971
+ ]
1972
+ }
1973
+ ```
1974
+
1975
+ #### Key Patterns Worth Adopting
1976
+
1977
+ **1. Source-Level Exports (No Build Step for Internal Consumption)**
1978
+
1979
+ All internal packages export TypeScript source directly — no `dist/` directory:
1980
+
1981
+ ```json
1982
+ // packages/shared/package.json — 27 subpath exports
1983
+ {
1984
+ "exports": {
1985
+ ".": "./src/index.ts",
1986
+ "./agent": "./src/agent/index.ts",
1987
+ "./auth": "./src/auth/index.ts",
1988
+ "./config": "./src/config/index.ts",
1989
+ "./mcp": "./src/mcp/index.ts",
1990
+ "./sessions": "./src/sessions/index.ts",
1991
+ // ... 21 more subpaths
1992
+ }
1993
+ }
1994
+ ```
1995
+
1996
+ This works because Bun natively imports TypeScript.
1997
+ Apps consume packages directly from source, eliminating build-watch complexity for local
1998
+ development.
1999
+ Published packages would need compiled output for Node.js consumers, but for
2000
+ internal-only packages this is ideal.
2001
+
2002
+ **2. TypeScript Build Scripts (No Shell Scripts)**
2003
+
2004
+ All build orchestration is in TypeScript, executed directly by Bun:
2005
+
2006
+ ```json
2007
+ {
2008
+ "scripts": {
2009
+ "electron:build:main": "bun run scripts/electron-build-main.ts",
2010
+ "electron:build:preload": "bun run scripts/electron-build-preload.ts",
2011
+ "electron:build:renderer": "bun run scripts/electron-build-renderer.ts",
2012
+ "electron:dev": "bun run scripts/electron-dev.ts"
2013
+ }
2014
+ }
2015
+ ```
2016
+
2017
+ This eliminates platform-specific shell script issues and provides type safety for build
2018
+ logic.
2019
+
2020
+ **3. Hybrid Build Architecture**
2021
+
2022
+ Uses esbuild for Node.js targets (Electron main/preload), Vite for browser targets
2023
+ (renderer, viewer), and TypeScript for type checking only (`noEmit: true`). Bun serves
2024
+ as the orchestrator but does not replace specialized build tools.
2025
+
2026
+ **4. Global Preload via bunfig.toml**
2027
+
2028
+ ```toml
2029
+ # bunfig.toml
2030
+ preload = ["./packages/shared/src/network-interceptor.ts"]
2031
+ ```
2032
+
2033
+ Preloads a network interceptor for API error capture and MCP schema injection across all
2034
+ Bun processes. This is a Bun-unique pattern for cross-cutting concerns.
2035
+
2036
+ **5. Single-Source Version Management**
2037
+
2038
+ Version lives in one TypeScript constant (`packages/shared/src/version/app-version.ts`),
2039
+ synced to all `package.json` files via `bun run scripts/sync-version.ts`. Avoids
2040
+ Changesets entirely for a private monorepo that distributes as a single Electron app.
2041
+
2042
+ **6. Custom ESLint Rules for Architectural Constraints**
2043
+
2044
+ Instead of Biome, this project uses ESLint with 4 custom rules:
2045
+
2046
+ - `no-direct-navigation-state` — enforces navigation state abstraction
2047
+ - `no-localstorage` — prevents browser localStorage usage (Bun incompatible)
2048
+ - `no-direct-platform-check` — enforces platform detection abstraction
2049
+ - `no-hardcoded-path-separator` — enforces cross-platform paths
2050
+
2051
+ This demonstrates that ESLint’s custom rule ecosystem remains valuable for
2052
+ domain-specific architectural constraints that Biome cannot yet replicate.
2053
+
2054
+ **7. Inter-Package Dependencies (Workspace Protocol)**
2055
+
2056
+ ```
2057
+ apps/electron → @craft-agent/core, shared, ui (workspace:*)
2058
+ apps/viewer → @craft-agent/core, ui (workspace:*)
2059
+ packages/shared → @craft-agent/core (workspace:*)
2060
+ packages/ui → @craft-agent/core (workspace:*)
2061
+ packages/core → (no internal deps)
2062
+ ```
2063
+
2064
+ Clean dependency graph with `core` as the leaf package.
2065
+
2066
+ #### What This Repo Does NOT Have
2067
+
2068
+ - **No Bunup**: Uses esbuild + Vite directly (hybrid approach)
2069
+ - **No Biome**: Uses ESLint (needed custom rules)
2070
+ - **No Changesets**: Single-version private app, manual sync script
2071
+ - **No CI/CD workflows**: No `.github/workflows/` directory
2072
+ - **No publint**: Not publishing to npm
2073
+ - **No git hooks**: No lefthook, husky, or pre-commit
2074
+ - **No tests**: No test files found in the codebase
2075
+ - **No `bun --compile`**: Ships Bun binary in vendor/ directory via electron-builder
2076
+
2077
+ #### Lessons for Published Library Monorepos
2078
+
2079
+ This project optimizes for a different use case (private Electron app) than a published
2080
+ npm library monorepo.
2081
+ Key takeaways:
2082
+
2083
+ | craft-agents-oss Pattern | Adaptation for Published Library |
2084
+ | --- | --- |
2085
+ | Source-level exports | Add `"bun"` condition + compiled dist for Node.js consumers |
2086
+ | esbuild + Vite build | Use Bunup for library/CLI packages |
2087
+ | Manual version sync | Use Changesets with Bun workarounds |
2088
+ | No CI | Add GitHub Actions with `oven-sh/setup-bun@v2` |
2089
+ | ESLint with custom rules | Use Biome unless custom rules needed |
2090
+ | No tests | Add `bun test` suite |
2091
+ | No publint | Add publint validation |
2092
+ | No git hooks | Add lefthook with `biome check` |
2093
+
2094
+ The source-level exports pattern and TypeScript build scripts are directly applicable.
2095
+ The hybrid esbuild/Vite approach is more relevant for Electron/full-stack apps than for
2096
+ pure library packages where Bunup would be simpler.