get-tbd 0.1.21 → 0.1.23

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.
@@ -5,7 +5,7 @@ author: Joshua Levy (github.com/jlevy) with LLM assistance
5
5
  ---
6
6
  # Bun Monorepo Patterns
7
7
 
8
- **Last Updated**: 2026-02-02
8
+ **Last Updated**: 2026-02-18
9
9
 
10
10
  **Related**:
11
11
 
@@ -24,10 +24,13 @@ author: Joshua Levy (github.com/jlevy) with LLM assistance
24
24
  | Tool / Package | Version | Check For Updates |
25
25
  | --- | --- | --- |
26
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. |
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.22 | [npmjs.com/package/bunup](https://www.npmjs.com/package/bunup) — Build tool for TS libs. Rapid iteration (0.16.22 latest). |
29
+ | **Biome** | ^2.3.14 | [biomejs.dev](https://biomejs.dev/) — Formatter + linter. v2.0 added plugins and type-aware linting; 2.3.14 is latest stable. |
30
+ | **@changesets/cli** | ^2.29.8 | [github.com/changesets/changesets/releases](https://github.com/changesets/changesets/releases) — 2.29.8 latest. No native Bun support yet. |
31
+ | **bun-types** | ^1.3.8 | [npmjs.com/package/bun-types](https://www.npmjs.com/package/bun-types) — Type definitions for Bun runtime APIs. |
32
+ | **tryscript** | ^0.1.6 | [npmjs.com/package/tryscript](https://www.npmjs.com/package/tryscript) — Golden/CLI testing via Markdown test files. |
33
+ | **flowmark** | latest | [github.com/jlevy/flowmark](https://github.com/jlevy/flowmark) — Markdown auto-formatter (via `uvx`). |
31
34
  | **publint** | ^0.3.17 | [npmjs.com/package/publint](https://www.npmjs.com/package/publint) — 0.3.17 latest |
32
35
  | **actions/checkout** | v6 | [github.com/actions/checkout/releases](https://github.com/actions/checkout/releases) |
33
36
  | **oven-sh/setup-bun** | v2 | [github.com/oven-sh/setup-bun](https://github.com/oven-sh/setup-bun) — Verified on GitHub Marketplace |
@@ -69,9 +72,9 @@ It serves as a direct comparison to the companion document on pnpm-based monorep
69
72
  covering the same architectural scope but using Bun-native tooling wherever possible.
70
73
 
71
74
  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
+ building ESM (or dual ESM/CJS) outputs with TypeScript declarations, **Changesets**
76
+ (with Bun workarounds) for versioning and release automation, **Biome** for formatting
77
+ and linting, **publint** for package validation, and **lefthook** for git hooks.
75
78
  The architecture also covers Bun’s unique capability for **compiling standalone
76
79
  executables** — a native binary distribution path unavailable in the pnpm ecosystem.
77
80
 
@@ -281,6 +284,24 @@ primarily serves IDE support, type checking, and declaration generation.
281
284
  projects because Bunup’s DTS generation is dramatically faster when this is enabled (it
282
285
  avoids invoking the full TypeScript compiler).
283
286
 
287
+ **Override at package level**: For packages with complex types that cannot satisfy
288
+ isolated declaration emit, override to `false` at the package level while keeping `true`
289
+ in the base config:
290
+
291
+ ```json
292
+ {
293
+ "extends": "../../tsconfig.base.json",
294
+ "compilerOptions": {
295
+ "types": ["bun-types"],
296
+ "noEmit": true,
297
+ "isolatedDeclarations": false
298
+ },
299
+ "include": ["src", "tests", "*.config.ts"]
300
+ }
301
+ ```
302
+
303
+ This lets most packages benefit from fast DTS generation while allowing exceptions.
304
+
284
305
  **Assessment**: Nearly identical to the pnpm setup.
285
306
  The addition of `isolatedDeclarations` and `bun-types` are the only differences.
286
307
  Using `moduleResolution: "Bundler"` is appropriate since Bunup handles the final output.
@@ -428,6 +449,175 @@ The workspace mode provides monorepo-aware builds that tsdown does not offer nat
428
449
 
429
450
  * * *
430
451
 
452
+ #### ESM-Only Build Strategy
453
+
454
+ **Status**: Recommended (when appropriate)
455
+
456
+ **Details**:
457
+
458
+ Not every package needs dual ESM/CJS output.
459
+ For Bun-native CLI tools and packages targeting modern Node.js (>=22), ESM-only is
460
+ simpler and avoids CJS compatibility concerns entirely.
461
+
462
+ **When ESM-only is appropriate**:
463
+
464
+ - CLI tools that run via `#!/usr/bin/env bun` or `#!/usr/bin/env node`
465
+ - Libraries targeting only modern consumers (Node.js >=22, Bun, Deno)
466
+ - Internal monorepo packages not published to npm
467
+
468
+ **When you still need dual ESM/CJS**:
469
+
470
+ - Libraries consumed by older Node.js versions or legacy bundlers
471
+ - Packages that must work in CommonJS-only environments
472
+
473
+ **ESM-only configuration** (`bunup.config.ts`):
474
+
475
+ ```typescript
476
+ import { defineConfig } from 'bunup';
477
+
478
+ export default defineConfig({
479
+ entry: ['src/index.ts'],
480
+ format: ['esm'], // No CJS output
481
+ dts: true,
482
+ clean: true,
483
+ sourcemap: 'linked',
484
+ });
485
+ ```
486
+
487
+ **ESM-only exports** (`package.json`):
488
+
489
+ ```json
490
+ {
491
+ "type": "module",
492
+ "main": "./dist/index.js",
493
+ "types": "./dist/index.d.ts",
494
+ "exports": {
495
+ ".": {
496
+ "bun": "./src/index.ts",
497
+ "import": {
498
+ "types": "./dist/index.d.ts",
499
+ "default": "./dist/index.js"
500
+ }
501
+ },
502
+ "./package.json": "./package.json"
503
+ }
504
+ }
505
+ ```
506
+
507
+ Note: The legacy `"main"` and `"types"` fields still work for ESM-only packages and
508
+ provide fallback resolution for older tooling.
509
+
510
+ **Assessment**: ESM-only is the right default for new Bun-native projects.
511
+ Only add CJS if you have specific consumers that require it.
512
+
513
+ * * *
514
+
515
+ #### Multi-Config Bunup (Array Pattern)
516
+
517
+ **Status**: Recommended for CLI packages
518
+
519
+ **Details**:
520
+
521
+ For packages that produce both a library and a CLI binary, use `defineConfig([...])`
522
+ with separate configurations.
523
+ This provides fine-grained control over each output:
524
+
525
+ ```typescript
526
+ import { defineConfig } from 'bunup';
527
+
528
+ export default defineConfig([
529
+ // Library entry point — consumers import this
530
+ {
531
+ name: 'index',
532
+ entry: ['src/index.ts'],
533
+ format: ['esm'],
534
+ dts: true,
535
+ clean: true,
536
+ sourcemap: 'linked',
537
+ target: 'node',
538
+ },
539
+ // CLI binary — users run this directly
540
+ {
541
+ name: 'bin',
542
+ entry: ['src/bin.ts'],
543
+ format: ['esm'],
544
+ dts: false, // No declarations for CLI entry
545
+ clean: false, // Don't delete library output from first config
546
+ sourcemap: 'linked',
547
+ target: 'node',
548
+ banner: '#!/usr/bin/env bun',
549
+ },
550
+ ]);
551
+ ```
552
+
553
+ **Key details**:
554
+
555
+ - Set `clean: true` only on the first config — subsequent configs must use
556
+ `clean: false` to avoid deleting earlier outputs.
557
+ - CLI binaries typically don’t need `dts` since they aren’t imported by consumers.
558
+ - The `banner` field adds the shebang line for direct execution.
559
+
560
+ **Assessment**: The array pattern is cleaner than a single config with multiple entries
561
+ when the library and CLI have different build requirements (dts, format, banner, etc.).
562
+
563
+ * * *
564
+
565
+ #### Dependency Bundling for CLI Startup
566
+
567
+ **Status**: Recommended for CLI tools
568
+
569
+ **Details**:
570
+
571
+ Bunup’s `noExternal` option can inline specific dependencies into the CLI binary,
572
+ reducing startup time by eliminating require/import resolution at runtime:
573
+
574
+ ```typescript
575
+ {
576
+ name: 'bin',
577
+ entry: ['src/bin.ts'],
578
+ format: ['esm'],
579
+ banner: '#!/usr/bin/env bun',
580
+ noExternal: ['picocolors'], // Bundle into output for faster startup
581
+ }
582
+ ```
583
+
584
+ **When to bundle**:
585
+
586
+ - Small, frequently-imported packages (e.g., `picocolors`, `kleur`)
587
+ - Dependencies used on every CLI invocation (startup-path dependencies)
588
+ - Dependencies with deep `node_modules` resolution chains
589
+
590
+ **When NOT to bundle**:
591
+
592
+ - Large dependencies (increases output size significantly)
593
+ - Dependencies with native bindings
594
+ - Dependencies shared with the library entry point (consumers may already have them)
595
+
596
+ **Cross-platform considerations**: Some dependencies may trigger Bun bundler bugs on
597
+ specific platforms. Use platform guards when needed:
598
+
599
+ ```typescript
600
+ const isWindows = process.platform === 'win32';
601
+
602
+ export default defineConfig([
603
+ {
604
+ name: 'bin',
605
+ entry: ['src/bin.ts'],
606
+ format: ['esm'],
607
+ banner: '#!/usr/bin/env bun',
608
+ // Skip bundling on Windows due to Bun bundler bug with Windows paths
609
+ // See: https://github.com/oven-sh/bun/issues/15007
610
+ noExternal: isWindows ? [] : ['picocolors'],
611
+ },
612
+ ]);
613
+ ```
614
+
615
+ **Assessment**: Dependency bundling is a practical optimization for CLI tools.
616
+ The cross-platform guard pattern ensures builds work everywhere even when Bun’s bundler
617
+ has platform-specific issues.
618
+
619
+ * * *
620
+
431
621
  ### 4. Package Exports & Dual Module Support
432
622
 
433
623
  #### Subpath Exports
@@ -613,6 +803,10 @@ bunx changeset init
613
803
  }
614
804
  ```
615
805
 
806
+ **Note**: For solo or small projects, `"changelog": "@changesets/cli/changelog"` is a
807
+ simpler alternative that uses the built-in changelog generator without requiring the
808
+ `@changesets/changelog-github` dependency.
809
+
616
810
  **Critical workaround scripts**:
617
811
 
618
812
  ```json
@@ -786,6 +980,71 @@ safer choice.
786
980
 
787
981
  * * *
788
982
 
983
+ #### Golden/CLI Testing with tryscript
984
+
985
+ **Status**: Recommended for CLI tools
986
+
987
+ **Details**:
988
+
989
+ For CLI tools, `bun test` covers unit tests but doesn’t validate end-to-end CLI behavior
990
+ (argument parsing, output formatting, exit codes).
991
+ Use [tryscript](https://github.com/jlevy/tryscript) for golden/snapshot testing of CLI
992
+ output.
993
+
994
+ **Setup**:
995
+
996
+ ```bash
997
+ bun add -d tryscript
998
+ ```
999
+
1000
+ **Dual-test pattern** (`package.json`):
1001
+
1002
+ ```json
1003
+ {
1004
+ "scripts": {
1005
+ "test": "bun test && tryscript run tests/cli.tryscript.md",
1006
+ "test:unit": "bun test",
1007
+ "test:golden": "tryscript run tests/*.tryscript.md"
1008
+ }
1009
+ }
1010
+ ```
1011
+
1012
+ This ensures `bun run test` runs both unit tests and golden tests.
1013
+ Developers can also run them separately during development.
1014
+
1015
+ **Example golden test** (`tests/cli.tryscript.md`):
1016
+
1017
+ ````markdown
1018
+ # CLI Golden Tests
1019
+
1020
+ ## Help output
1021
+
1022
+ ```shell
1023
+ $ my-cli --help
1024
+ Usage: my-cli [options] [command]
1025
+ ...
1026
+ ```
1027
+
1028
+ ## Version output
1029
+
1030
+ ```shell
1031
+ $ my-cli --version
1032
+ 0.1.0
1033
+ ```
1034
+ ````
1035
+
1036
+ **Assessment**: Golden tests are especially valuable for CLI tools where output
1037
+ stability matters. They catch regressions in help text, formatting, and argument parsing
1038
+ that unit tests may miss.
1039
+ The `tryscript` tool uses Markdown files as test cases, making them easy to read and
1040
+ maintain.
1041
+
1042
+ **References**:
1043
+
1044
+ - [tryscript](https://github.com/jlevy/tryscript)
1045
+
1046
+ * * *
1047
+
789
1048
  ### 9. Code Formatting & Linting
790
1049
 
791
1050
  #### Biome
@@ -826,15 +1085,29 @@ bun add -d @biomejs/biome
826
1085
 
827
1086
  ```json
828
1087
  {
829
- "$schema": "https://biomejs.dev/schemas/2.3.0/schema.json",
830
- "organizeImports": {
831
- "enabled": true
1088
+ "$schema": "https://biomejs.dev/schemas/2.3.14/schema.json",
1089
+ "vcs": {
1090
+ "enabled": true,
1091
+ "clientKind": "git",
1092
+ "useIgnoreFile": true
832
1093
  },
1094
+ "files": {
1095
+ "ignoreUnknown": true,
1096
+ "includes": [
1097
+ "**",
1098
+ "!**/dist",
1099
+ "!**/node_modules",
1100
+ "!**/*.d.ts",
1101
+ "!**/bun.lock"
1102
+ ]
1103
+ },
1104
+ "assist": { "actions": { "source": { "organizeImports": "on" } } },
833
1105
  "formatter": {
834
1106
  "enabled": true,
835
1107
  "indentStyle": "space",
836
1108
  "indentWidth": 2,
837
- "lineWidth": 100
1109
+ "lineWidth": 100,
1110
+ "lineEnding": "lf"
838
1111
  },
839
1112
  "linter": {
840
1113
  "enabled": true,
@@ -849,16 +1122,27 @@ bun add -d @biomejs/biome
849
1122
  },
850
1123
  "style": {
851
1124
  "useConst": "error",
852
- "noNonNullAssertion": "warn"
1125
+ "noNonNullAssertion": "warn",
1126
+ "useImportType": "error"
853
1127
  }
854
1128
  }
855
- },
856
- "files": {
857
- "ignore": ["dist", "node_modules", ".changeset"]
858
1129
  }
859
1130
  }
860
1131
  ```
861
1132
 
1133
+ **Key configuration notes**:
1134
+
1135
+ - **VCS integration**:
1136
+ `"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }` tells Biome
1137
+ to respect `.gitignore` for file exclusion, reducing config duplication.
1138
+ - **Import organization**: In Biome 2.x, the `"organizeImports"` top-level key is
1139
+ deprecated. Use the new `"assist"` API:
1140
+ `"assist": { "actions": { "source": { "organizeImports": "on" } } }`.
1141
+ - **`ignoreUnknown: true`**: Prevents Biome from erroring on file types it doesn’t
1142
+ support (e.g., images, binaries).
1143
+ - **`useImportType: "error"`**: Enforces `import type` for type-only imports, aligning
1144
+ with `verbatimModuleSyntax` in TypeScript.
1145
+
862
1146
  **Scripts**:
863
1147
 
864
1148
  ```json
@@ -866,17 +1150,17 @@ bun add -d @biomejs/biome
866
1150
  "scripts": {
867
1151
  "format": "biome format --write .",
868
1152
  "format:check": "biome format .",
869
- "lint": "biome lint --write .",
870
- "lint:check": "biome lint .",
1153
+ "lint": "biome check --write . && bun run typecheck",
1154
+ "lint:check": "biome check . && bun run typecheck",
871
1155
  "check": "biome check --write .",
872
- "check:ci": "biome check ."
1156
+ "check:ci": "biome ci ."
873
1157
  }
874
1158
  }
875
1159
  ```
876
1160
 
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.
1161
+ **Biome `check` vs `ci`**: `biome check` runs formatting and linting in a single pass.
1162
+ Use `biome check --write .` for local development (auto-fixes issues) and `biome ci .`
1163
+ for CI (stricter: errors on any issue, cleaner output for CI logs).
880
1164
 
881
1165
  **Comparison with Prettier + ESLint**:
882
1166
 
@@ -921,7 +1205,7 @@ approach (Biome + targeted Prettier/ESLint) is viable.
921
1205
 
922
1206
  **Status**: Recommended
923
1207
 
924
- **`.github/workflows/ci.yml`**:
1208
+ **`.github/workflows/ci.yml`** (basic):
925
1209
 
926
1210
  ```yaml
927
1211
  name: CI
@@ -949,6 +1233,61 @@ jobs:
949
1233
  - run: bun test
950
1234
  ```
951
1235
 
1236
+ **Cross-platform CI with separate lint job** (recommended for CLI tools):
1237
+
1238
+ ```yaml
1239
+ name: CI
1240
+
1241
+ on:
1242
+ pull_request:
1243
+ push:
1244
+ branches: [main]
1245
+
1246
+ permissions:
1247
+ contents: read
1248
+
1249
+ jobs:
1250
+ test:
1251
+ name: Test (${{ matrix.os }})
1252
+ runs-on: ${{ matrix.os }}
1253
+ strategy:
1254
+ fail-fast: false
1255
+ matrix:
1256
+ os: [ubuntu-latest, macos-latest, windows-latest]
1257
+ steps:
1258
+ - uses: actions/checkout@v6
1259
+
1260
+ - uses: oven-sh/setup-bun@v2
1261
+ with:
1262
+ bun-version: latest
1263
+
1264
+ - run: bun install --frozen-lockfile
1265
+ - run: bun run build
1266
+ - run: bun run --filter '*' test
1267
+
1268
+ lint:
1269
+ name: Lint & Typecheck
1270
+ runs-on: ubuntu-latest
1271
+ steps:
1272
+ - uses: actions/checkout@v6
1273
+
1274
+ - uses: oven-sh/setup-bun@v2
1275
+ with:
1276
+ bun-version: latest
1277
+
1278
+ - run: bun install --frozen-lockfile
1279
+ - run: bun run check:ci
1280
+ - run: bun run typecheck
1281
+ - run: bun run build
1282
+ - run: bun run publint
1283
+ ```
1284
+
1285
+ **`biome ci` vs `biome check`**: Use `biome ci .` (via `"check:ci": "biome ci ."`) in CI
1286
+ workflows instead of `biome check .`. The `biome ci` command is stricter — it errors on
1287
+ formatting issues rather than just reporting them, and produces cleaner output for CI
1288
+ logs. Use `biome check --write .` for local development (auto-fixes issues) and
1289
+ `biome ci .` in CI (fails on any issue).
1290
+
952
1291
  **Key differences from pnpm CI**:
953
1292
 
954
1293
  | Aspect | pnpm CI | Bun CI |
@@ -1023,6 +1362,131 @@ Changesets workarounds, but functionally equivalent.
1023
1362
 
1024
1363
  * * *
1025
1364
 
1365
+ #### Alternative: Tag-Triggered OIDC Release Workflow
1366
+
1367
+ **Status**: Recommended (alternative to Changesets Action)
1368
+
1369
+ **Details**:
1370
+
1371
+ Instead of using the `changesets/action` GitHub Action, an alternative approach uses git
1372
+ tags to trigger releases with npm provenance attestation via OIDC. This is simpler for
1373
+ projects that prefer manual version control and want provenance guarantees.
1374
+
1375
+ **`.github/workflows/release.yml`**:
1376
+
1377
+ ```yaml
1378
+ name: Release
1379
+
1380
+ on:
1381
+ push:
1382
+ tags:
1383
+ - 'v*'
1384
+
1385
+ permissions:
1386
+ contents: write
1387
+ id-token: write # Required for npm provenance attestation
1388
+
1389
+ jobs:
1390
+ release:
1391
+ runs-on: ubuntu-latest
1392
+ steps:
1393
+ - uses: actions/checkout@v6
1394
+ with:
1395
+ fetch-depth: 0
1396
+
1397
+ - uses: oven-sh/setup-bun@v2
1398
+ with:
1399
+ bun-version: latest
1400
+
1401
+ # Node.js still needed for npm provenance attestation
1402
+ - uses: actions/setup-node@v6
1403
+ with:
1404
+ node-version: 22
1405
+ registry-url: 'https://registry.npmjs.org'
1406
+
1407
+ - run: bun install --frozen-lockfile
1408
+
1409
+ - run: bun run build
1410
+
1411
+ - run: bun run publint
1412
+
1413
+ - name: Publish to npm
1414
+ run: bun run publish-packages
1415
+ env:
1416
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
1417
+ NPM_CONFIG_PROVENANCE: true
1418
+
1419
+ - name: Extract version from tag
1420
+ id: version
1421
+ run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
1422
+
1423
+ - name: Extract release notes
1424
+ id: changelog
1425
+ run: |
1426
+ VERSION="${{ steps.version.outputs.version }}"
1427
+ PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
1428
+ CHANGELOG=$(awk "/^## ${VERSION//./\\.}/,/^## [0-9]/" packages/*/CHANGELOG.md 2>/dev/null | head -n -1 || true)
1429
+ if [ -z "$CHANGELOG" ]; then
1430
+ if [ -n "$PREV_TAG" ]; then
1431
+ CHANGELOG="See [full commit history](https://github.com/${{ github.repository }}/compare/${PREV_TAG}...v${VERSION}) for details."
1432
+ else
1433
+ CHANGELOG="Release v${VERSION}"
1434
+ fi
1435
+ fi
1436
+ echo "body<<EOF" >> $GITHUB_OUTPUT
1437
+ echo "$CHANGELOG" >> $GITHUB_OUTPUT
1438
+ echo "EOF" >> $GITHUB_OUTPUT
1439
+
1440
+ - name: Create GitHub Release
1441
+ uses: softprops/action-gh-release@v2
1442
+ with:
1443
+ body: ${{ steps.changelog.outputs.body }}
1444
+ prerelease: ${{ contains(steps.version.outputs.version, '-') }}
1445
+ ```
1446
+
1447
+ **Release process with tag-triggered workflow**:
1448
+
1449
+ 1. `bun run changeset` — create changeset
1450
+ 2. Commit the changeset file
1451
+ 3. `bun run changeset version` — bumps versions, updates CHANGELOG
1452
+ 4. Commit the version bump
1453
+ 5. `git tag vX.X.X && git push --tags` — triggers the release workflow
1454
+
1455
+ **Key advantages over Changesets Action**:
1456
+
1457
+ - **npm provenance**: OIDC-based attestation proves packages were published from CI, not
1458
+ a developer machine
1459
+ - **GitHub Releases**: Automatically creates a GitHub Release with extracted CHANGELOG
1460
+ notes
1461
+ - **Pre-release detection**: Versions containing `-` (e.g., `1.0.0-beta.1`) are
1462
+ automatically marked as pre-releases
1463
+ - **Manual control**: The developer decides exactly when to publish by creating a tag
1464
+
1465
+ **Key requirements**:
1466
+
1467
+ - `id-token: write` permission is required for npm provenance
1468
+ - Node.js setup is still needed because npm provenance attestation uses Node.js
1469
+ internally
1470
+ - `NPM_TOKEN` secret must be configured in the repository
1471
+ - `NPM_CONFIG_PROVENANCE: true` enables provenance attestation
1472
+
1473
+ **Comparison with Changesets Action**:
1474
+
1475
+ | Aspect | Changesets Action | Tag-Triggered OIDC |
1476
+ | --- | --- | --- |
1477
+ | Trigger | Push to main | Push of `v*` tag |
1478
+ | PR creation | Automatic “Version Packages” PR | Manual |
1479
+ | npm provenance | Not built-in | Built-in via OIDC |
1480
+ | GitHub Release | Not built-in | Automatic |
1481
+ | Control | More automated | More manual |
1482
+ | Complexity | Simpler workflow file | More steps but more features |
1483
+
1484
+ **Assessment**: Tag-triggered OIDC releases are recommended for projects that want npm
1485
+ provenance attestation, automatic GitHub Releases, and manual control over release
1486
+ timing. The Changesets Action remains simpler for projects that prefer full automation.
1487
+
1488
+ * * *
1489
+
1026
1490
  ### 11. Git Hooks & Local Validation
1027
1491
 
1028
1492
  #### Lefthook with Biome
@@ -1055,6 +1519,16 @@ pre-commit:
1055
1519
  stage_fixed: true
1056
1520
  priority: 1
1057
1521
 
1522
+ # Format markdown with flowmark (~500ms)
1523
+ # Biome does not format Markdown, so flowmark fills this gap
1524
+ # Excludes auto-generated files
1525
+ format-md:
1526
+ glob: '*.md'
1527
+ exclude: '(\.tbd/|\.claude/skills/|AGENTS\.md)'
1528
+ run: uvx flowmark@latest --auto {staged_files}
1529
+ stage_fixed: true
1530
+ priority: 1
1531
+
1058
1532
  # Type check (~2s)
1059
1533
  typecheck:
1060
1534
  glob: '*.{ts,tsx}'
@@ -1091,8 +1565,16 @@ pre-push:
1091
1565
  **Key advantage**: Using `biome check` in pre-commit combines formatting and linting
1092
1566
  into a single command, reducing hook complexity from 2–3 commands to 1.
1093
1567
 
1568
+ **Markdown formatting**: Since Biome does not format Markdown, use
1569
+ [flowmark](https://github.com/jlevy/flowmark) (`uvx flowmark@latest --auto`) in a
1570
+ separate pre-commit command.
1571
+ The `exclude` pattern prevents reformatting auto-generated files.
1572
+ flowmark requires Python (via `uvx`), which is available on most development machines.
1573
+
1094
1574
  **Assessment**: Simpler and faster hooks than the pnpm equivalent thanks to Biome’s
1095
1575
  unified check command.
1576
+ The addition of flowmark provides consistent Markdown formatting that Biome cannot
1577
+ offer.
1096
1578
 
1097
1579
  * * *
1098
1580
 
@@ -1361,8 +1843,8 @@ in core library code if the library needs to work in Node.js environments.
1361
1843
  3. **Use Bunup’s auto-exports** (`exports: true`) to keep `package.json` exports
1362
1844
  synchronized with build output.
1363
1845
 
1364
- 4. **Use Biome for formatting + linting** via a single `biome.json`. Use `biome check`
1365
- for combined format + lint in one pass.
1846
+ 4. **Use Biome for formatting + linting** via a single `biome.json`. Use
1847
+ `biome check --write` locally and `biome ci` in CI for stricter checks.
1366
1848
 
1367
1849
  5. **Add `bun update` after `changeset version`** to fix workspace reference resolution
1368
1850
  in the lockfile.
@@ -1396,6 +1878,21 @@ in core library code if the library needs to work in Node.js environments.
1396
1878
  15. **Use dynamic git-based versioning** for dev builds — the pattern works identically
1397
1879
  with Bun, and `bun` replaces `tsx` for script execution.
1398
1880
 
1881
+ 16. **Use `biome ci`** in CI workflows instead of `biome check` — it’s stricter and
1882
+ produces cleaner output for CI logs.
1883
+
1884
+ 17. **Consider ESM-only output** for Bun-native CLI tools and packages targeting modern
1885
+ Node.js (>=22). Only add CJS if specific consumers require it.
1886
+
1887
+ 18. **Use flowmark** for Markdown formatting in pre-commit hooks — Biome does not format
1888
+ Markdown, and consistent Markdown formatting improves documentation quality.
1889
+
1890
+ 19. **Add golden/CLI tests** with `tryscript` alongside `bun test` for CLI tools — they
1891
+ catch regressions in help text, output formatting, and argument parsing.
1892
+
1893
+ 20. **Consider tag-triggered OIDC releases** as an alternative to the Changesets GitHub
1894
+ Action — they provide npm provenance attestation and automatic GitHub Releases.
1895
+
1399
1896
  * * *
1400
1897
 
1401
1898
  ## Open Research Questions
@@ -1492,7 +1989,7 @@ testing features.
1492
1989
 
1493
1990
  1. **Initialize workspace** with Bun and packages in `packages/`
1494
1991
 
1495
- 2. **Configure Bunup** for dual ESM/CJS output with auto-exports
1992
+ 2. **Configure Bunup** for ESM (or dual ESM/CJS) output with auto-exports
1496
1993
 
1497
1994
  3. **Enable `isolatedDeclarations`** in TypeScript config
1498
1995
 
@@ -1576,6 +2073,56 @@ testing features.
1576
2073
 
1577
2074
  ### Appendix A: Complete Package package.json Example
1578
2075
 
2076
+ **ESM-only** (recommended for Bun-native CLI tools and modern packages):
2077
+
2078
+ ```json
2079
+ {
2080
+ "name": "get-package-name",
2081
+ "version": "0.1.0",
2082
+ "description": "Package description",
2083
+ "license": "MIT",
2084
+ "type": "module",
2085
+ "sideEffects": false,
2086
+ "main": "./dist/index.js",
2087
+ "types": "./dist/index.d.ts",
2088
+ "exports": {
2089
+ ".": {
2090
+ "bun": "./src/index.ts",
2091
+ "import": {
2092
+ "types": "./dist/index.d.ts",
2093
+ "default": "./dist/index.js"
2094
+ }
2095
+ },
2096
+ "./package.json": "./package.json"
2097
+ },
2098
+ "bin": {
2099
+ "package-name": "./dist/bin.js"
2100
+ },
2101
+ "files": ["dist"],
2102
+ "engines": {
2103
+ "node": ">=22"
2104
+ },
2105
+ "scripts": {
2106
+ "prebuild": "cp ../../README.md ./README.md",
2107
+ "build": "bunup",
2108
+ "dev": "bun --watch src/bin.ts",
2109
+ "typecheck": "tsc -p tsconfig.json --noEmit",
2110
+ "test": "bun test",
2111
+ "publint": "publint",
2112
+ "prepack": "bun run build"
2113
+ },
2114
+ "dependencies": {},
2115
+ "devDependencies": {
2116
+ "bun-types": "^1.3.8",
2117
+ "bunup": "^0.16.22",
2118
+ "publint": "^0.3.17",
2119
+ "typescript": "^5.9.3"
2120
+ }
2121
+ }
2122
+ ```
2123
+
2124
+ **Dual ESM/CJS** (for libraries that must support older Node.js or CJS consumers):
2125
+
1579
2126
  ```json
1580
2127
  {
1581
2128
  "name": "@scope/package-name",
@@ -1599,17 +2146,6 @@ testing features.
1599
2146
  "default": "./dist/index.cjs"
1600
2147
  }
1601
2148
  },
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
2149
  "./package.json": "./package.json"
1614
2150
  },
1615
2151
  "bin": {
@@ -1621,7 +2157,7 @@ testing features.
1621
2157
  "dev": "bunup --watch",
1622
2158
  "typecheck": "tsc -p tsconfig.json --noEmit",
1623
2159
  "test": "bun test",
1624
- "publint": "bunx publint",
2160
+ "publint": "publint",
1625
2161
  "prepack": "bun run build"
1626
2162
  },
1627
2163
  "dependencies": {},
@@ -1632,9 +2168,10 @@ testing features.
1632
2168
  "optional-sdk": { "optional": true }
1633
2169
  },
1634
2170
  "devDependencies": {
1635
- "bun-types": "latest",
1636
- "bunup": "^0.16.0",
1637
- "typescript": "^5.9.0"
2171
+ "bun-types": "^1.3.8",
2172
+ "bunup": "^0.16.22",
2173
+ "publint": "^0.3.17",
2174
+ "typescript": "^5.9.3"
1638
2175
  }
1639
2176
  }
1640
2177
  ```
@@ -1645,48 +2182,75 @@ testing features.
1645
2182
  {
1646
2183
  "name": "project-workspace",
1647
2184
  "private": true,
2185
+ "type": "module",
1648
2186
  "workspaces": [
1649
2187
  "packages/*"
1650
2188
  ],
2189
+ "engines": {
2190
+ "node": ">=22"
2191
+ },
1651
2192
  "scripts": {
1652
- "build": "bunup",
1653
- "typecheck": "tsc -b",
1654
- "test": "bun test",
1655
- "publint": "bunx publint",
2193
+ "prepare": "lefthook install",
2194
+ "build": "bun run --filter '*' build",
2195
+ "typecheck": "bun run --filter '*' typecheck",
2196
+ "test": "bun run --filter '*' test",
2197
+ "publint": "bun run --filter '*' publint",
1656
2198
  "format": "biome format --write .",
1657
2199
  "format:check": "biome format .",
1658
- "lint": "biome lint --write .",
1659
- "lint:check": "biome lint .",
2200
+ "lint": "biome check --write . && bun run typecheck",
2201
+ "lint:check": "biome check . && bun run typecheck",
1660
2202
  "check": "biome check --write .",
1661
- "check:ci": "biome check .",
1662
- "prepare": "lefthook install",
2203
+ "check:ci": "biome ci .",
1663
2204
  "changeset": "changeset",
1664
2205
  "version-packages": "changeset version && bun update",
1665
- "release": "bun run build && bunx publint && bun run publish-packages",
2206
+ "release": "bun run build && bun run publint && bun run publish-packages",
1666
2207
  "publish-packages": "for dir in packages/*; do (cd \"$dir\" && bun publish || true); done && changeset tag",
1667
2208
  "upgrade:check": "bunx npm-check-updates --format group",
1668
- "upgrade": "bunx npm-check-updates --target minor -u && bun install && bun test",
2209
+ "upgrade": "bunx npm-check-updates --target minor -u && bun install && bun run test",
1669
2210
  "upgrade:major": "bunx npm-check-updates --target latest --interactive --format group"
1670
2211
  },
1671
2212
  "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"
2213
+ "@biomejs/biome": "^2.3.14",
2214
+ "@changesets/cli": "^2.29.8",
2215
+ "lefthook": "^2.0.15",
2216
+ "npm-check-updates": "^19.0.0",
2217
+ "typescript": "^5.9.3"
1678
2218
  }
1679
2219
  }
1680
2220
  ```
1681
2221
 
2222
+ **Notes**:
2223
+
2224
+ - `"check:ci": "biome ci ."` uses `biome ci` (stricter than `biome check` — errors on
2225
+ formatting issues)
2226
+ - `bun run --filter '*' <script>` delegates to each workspace package
2227
+ - The `@changesets/changelog-github` dependency is optional — use
2228
+ `"changelog": "@changesets/cli/changelog"` in `.changeset/config.json` for a simpler
2229
+ built-in changelog generator
2230
+
1682
2231
  ### Appendix C: Complete biome.json Example
1683
2232
 
1684
2233
  ```json
1685
2234
  {
1686
- "$schema": "https://biomejs.dev/schemas/2.3.0/schema.json",
1687
- "organizeImports": {
1688
- "enabled": true
2235
+ "$schema": "https://biomejs.dev/schemas/2.3.14/schema.json",
2236
+ "vcs": {
2237
+ "enabled": true,
2238
+ "clientKind": "git",
2239
+ "useIgnoreFile": true
2240
+ },
2241
+ "files": {
2242
+ "ignoreUnknown": true,
2243
+ "includes": [
2244
+ "**",
2245
+ "!**/dist",
2246
+ "!**/node_modules",
2247
+ "!**/*.d.ts",
2248
+ "!**/*.d.mts",
2249
+ "!**/bun.lock",
2250
+ "!**/.tbd"
2251
+ ]
1689
2252
  },
2253
+ "assist": { "actions": { "source": { "organizeImports": "on" } } },
1690
2254
  "formatter": {
1691
2255
  "enabled": true,
1692
2256
  "indentStyle": "space",
@@ -1730,7 +2294,7 @@ testing features.
1730
2294
  },
1731
2295
  "overrides": [
1732
2296
  {
1733
- "include": ["**/*.test.ts", "**/*.spec.ts", "**/tests/**/*.ts"],
2297
+ "includes": ["**/*.test.ts", "**/*.spec.ts", "**/tests/**/*.ts"],
1734
2298
  "linter": {
1735
2299
  "rules": {
1736
2300
  "suspicious": {
@@ -1740,7 +2304,7 @@ testing features.
1740
2304
  }
1741
2305
  },
1742
2306
  {
1743
- "include": ["**/scripts/**/*.ts"],
2307
+ "includes": ["**/scripts/**/*.ts"],
1744
2308
  "linter": {
1745
2309
  "rules": {
1746
2310
  "suspicious": {
@@ -1749,19 +2313,19 @@ testing features.
1749
2313
  }
1750
2314
  }
1751
2315
  }
1752
- ],
1753
- "files": {
1754
- "ignore": [
1755
- "dist",
1756
- "node_modules",
1757
- ".changeset",
1758
- "bun.lock",
1759
- "coverage"
1760
- ]
1761
- }
2316
+ ]
1762
2317
  }
1763
2318
  ```
1764
2319
 
2320
+ **Key changes from Biome 1.x to 2.x**:
2321
+
2322
+ - `"organizeImports": { "enabled": true }` →
2323
+ `"assist": { "actions": { "source": { "organizeImports": "on" } } }`
2324
+ - `"vcs"` configuration is new — enables `.gitignore`-based file exclusion
2325
+ - `"files.ignoreUnknown": true` prevents errors on unsupported file types
2326
+ - `"files.includes"` with negation patterns replaces the old `"files.ignore"` array
2327
+ (both still work, but `includes` with negation is more flexible)
2328
+
1765
2329
  ### Appendix D: Complete bunup.config.ts Example
1766
2330
 
1767
2331
  ```typescript
@@ -1804,7 +2368,41 @@ export default defineWorkspace([
1804
2368
  ]);
1805
2369
  ```
1806
2370
 
1807
- **Single-package config** (`packages/core/bunup.config.ts`):
2371
+ **Single-package config with multi-config array** (`packages/cli/bunup.config.ts`):
2372
+
2373
+ ```typescript
2374
+ import { defineConfig } from 'bunup';
2375
+
2376
+ // Platform guard for cross-platform bundler workarounds
2377
+ const isWindows = process.platform === 'win32';
2378
+
2379
+ export default defineConfig([
2380
+ // Library entry point
2381
+ {
2382
+ name: 'index',
2383
+ entry: ['src/index.ts'],
2384
+ format: ['esm'],
2385
+ dts: true,
2386
+ clean: true,
2387
+ sourcemap: 'linked',
2388
+ target: 'node',
2389
+ },
2390
+ // CLI binary - separate config for different build requirements
2391
+ {
2392
+ name: 'bin',
2393
+ entry: ['src/bin.ts'],
2394
+ format: ['esm'],
2395
+ dts: false,
2396
+ clean: false, // Don't delete library output
2397
+ sourcemap: 'linked',
2398
+ target: 'node',
2399
+ banner: '#!/usr/bin/env bun',
2400
+ noExternal: isWindows ? [] : ['picocolors'], // Bundle for startup speed
2401
+ },
2402
+ ]);
2403
+ ```
2404
+
2405
+ **Simple single-package config** (`packages/core/bunup.config.ts`):
1808
2406
 
1809
2407
  ```typescript
1810
2408
  import { defineConfig } from 'bunup';
@@ -1831,7 +2429,7 @@ export default defineConfig({
1831
2429
  ```yaml
1832
2430
  # lefthook.yml
1833
2431
  # Git hooks for code quality (Bun + Biome edition)
1834
- # Pre-commit: Fast checks with auto-fix (target: <1 second)
2432
+ # Pre-commit: Fast checks with auto-fix (target: 2-5 seconds)
1835
2433
  # Pre-push: Full test validation with caching
1836
2434
 
1837
2435
  pre-commit:
@@ -1840,28 +2438,46 @@ pre-commit:
1840
2438
  commands:
1841
2439
  # Format + lint with Biome (~200ms for staged files)
1842
2440
  check:
1843
- glob: '*.{js,ts,tsx,json,css}'
2441
+ glob: '*.{js,ts,tsx,json}'
1844
2442
  run: bunx biome check --write {staged_files}
1845
2443
  stage_fixed: true
1846
2444
  priority: 1
1847
2445
 
2446
+ # Format markdown with flowmark (~500ms)
2447
+ # Biome does not format Markdown, so flowmark fills this gap
2448
+ # Excludes auto-generated files (.tbd/, .claude/skills/, AGENTS.md)
2449
+ format-md:
2450
+ glob: '*.md'
2451
+ exclude: '(\.tbd/|\.claude/skills/|AGENTS\.md)'
2452
+ run: uvx flowmark@latest --auto {staged_files}
2453
+ stage_fixed: true
2454
+ priority: 1
2455
+
1848
2456
  # Type check with incremental mode (~2s)
1849
2457
  typecheck:
1850
2458
  glob: '*.{ts,tsx}'
1851
- run: bunx tsc --noEmit --incremental
2459
+ run: bun run typecheck
1852
2460
  priority: 2
1853
2461
 
1854
2462
  pre-push:
2463
+ parallel: true
2464
+
1855
2465
  commands:
1856
- verify-tests:
2466
+ # Build packages
2467
+ build:
2468
+ run: bun run build
2469
+ priority: 1
2470
+
2471
+ # Tests with caching - skip if already passed for this commit
2472
+ test:
1857
2473
  run: |
1858
2474
  COMMIT_HASH=$(git rev-parse HEAD)
1859
2475
  CACHE_DIR="node_modules/.test-cache"
1860
2476
  CACHE_FILE="$CACHE_DIR/$COMMIT_HASH"
1861
2477
 
1862
- # Check for uncommitted changes
2478
+ # Check for uncommitted changes - always run tests
1863
2479
  if ! git diff --quiet || ! git diff --cached --quiet; then
1864
- bun test
2480
+ bun run test
1865
2481
  exit $?
1866
2482
  fi
1867
2483
 
@@ -1873,7 +2489,7 @@ pre-push:
1873
2489
  fi
1874
2490
 
1875
2491
  # Run tests
1876
- bun test
2492
+ bun run test
1877
2493
 
1878
2494
  # Cache on success
1879
2495
  if [ $? -eq 0 ]; then
@@ -1885,6 +2501,7 @@ pre-push:
1885
2501
  echo "Bypass with: git push --no-verify"
1886
2502
  exit 1
1887
2503
  fi
2504
+ priority: 2
1888
2505
  ```
1889
2506
 
1890
2507
  ### Appendix F: Standalone Executable Build Example
@@ -2094,3 +2711,122 @@ Key takeaways:
2094
2711
  The source-level exports pattern and TypeScript build scripts are directly applicable.
2095
2712
  The hybrid esbuild/Vite approach is more relevant for Electron/full-stack apps than for
2096
2713
  pure library packages where Bunup would be simpler.
2714
+
2715
+ ### Appendix H: Case Study — clam (Published Bun CLI Monorepo)
2716
+
2717
+ **Repository**: [jlevy/clam](https://github.com/jlevy/clam) (MIT, shell-like interface
2718
+ for Claude Code and other agents)
2719
+
2720
+ This appendix documents a real-world, published Bun monorepo that closely follows the
2721
+ patterns in this guide.
2722
+ Unlike Appendix G (craft-agents-oss, a private Electron app), clam is a public npm
2723
+ package that exercises the full publishing pipeline.
2724
+
2725
+ #### Structure Overview
2726
+
2727
+ ```
2728
+ clam/
2729
+ ├── .changeset/
2730
+ │ └── config.json # Changesets with @changesets/cli/changelog
2731
+ ├── .github/
2732
+ │ └── workflows/
2733
+ │ ├── ci.yml # 3-OS matrix + separate lint job
2734
+ │ └── release.yml # Tag-triggered OIDC release
2735
+ ├── packages/
2736
+ │ └── clam/
2737
+ │ ├── src/
2738
+ │ │ ├── bin.ts # CLI entry point
2739
+ │ │ ├── index.ts # Library barrel exports
2740
+ │ │ └── lib/ # Core modules (co-located *.test.ts)
2741
+ │ ├── tests/
2742
+ │ │ └── cli.tryscript.md # Golden CLI tests
2743
+ │ ├── bunup.config.ts # Multi-config: library + CLI binary
2744
+ │ ├── package.json # ESM-only, "bun" export condition
2745
+ │ └── tsconfig.json # extends base, bun-types, isolatedDeclarations: false
2746
+ ├── biome.json # VCS integration, assist API, ignoreUnknown
2747
+ ├── bun.lock # Text JSONC lockfile
2748
+ ├── lefthook.yml # Biome + flowmark pre-commit, cached tests pre-push
2749
+ ├── package.json # Root workspace config
2750
+ └── tsconfig.base.json # ES2024, strict, isolatedDeclarations: true
2751
+ ```
2752
+
2753
+ #### Key Patterns Demonstrated
2754
+
2755
+ **1. ESM-Only Build with Multi-Config Bunup**
2756
+
2757
+ ```typescript
2758
+ // bunup.config.ts
2759
+ export default defineConfig([
2760
+ {
2761
+ name: 'index',
2762
+ entry: ['src/index.ts'],
2763
+ format: ['esm'],
2764
+ dts: true,
2765
+ clean: true,
2766
+ sourcemap: 'linked',
2767
+ target: 'node',
2768
+ },
2769
+ {
2770
+ name: 'bin',
2771
+ entry: ['src/bin.ts'],
2772
+ format: ['esm'],
2773
+ dts: false,
2774
+ clean: false,
2775
+ sourcemap: 'linked',
2776
+ target: 'node',
2777
+ banner: '#!/usr/bin/env bun',
2778
+ noExternal: isWindows ? [] : ['picocolors'],
2779
+ },
2780
+ ]);
2781
+ ```
2782
+
2783
+ Separate configs for library (with DTS) and CLI binary (with shebang, dependency
2784
+ bundling, no DTS). ESM-only output — no CJS.
2785
+
2786
+ **2. Tag-Triggered OIDC Release with npm Provenance**
2787
+
2788
+ Release workflow triggers on `v*` tags with `id-token: write` permission.
2789
+ Uses `NPM_CONFIG_PROVENANCE: true` for npm provenance attestation.
2790
+ Automatically creates GitHub Releases with CHANGELOG extraction via
2791
+ `softprops/action-gh-release@v2`.
2792
+
2793
+ **3. Cross-Platform CI Matrix**
2794
+
2795
+ Tests run on ubuntu, macos, and windows with `fail-fast: false`. Linting, type checking,
2796
+ and publint run in a separate ubuntu-only job for efficiency.
2797
+
2798
+ **4. Dual Testing Strategy**
2799
+
2800
+ Unit tests with `bun test` (co-located `*.test.ts` files) plus golden CLI tests with
2801
+ `tryscript` (Markdown-based test files in `tests/`). Combined via:
2802
+ `"test": "bun test && tryscript run tests/cli.tryscript.md"`.
2803
+
2804
+ **5. Biome with VCS Integration**
2805
+
2806
+ Uses `"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }` and the
2807
+ new Biome 2.x `"assist"` API for import organization.
2808
+ `"check:ci": "biome ci ."` for strict CI checks.
2809
+
2810
+ **6. Intentional `isolatedDeclarations: false` at Package Level**
2811
+
2812
+ The base config enables `isolatedDeclarations: true` (as recommended), but the package
2813
+ overrides it to `false` because its types are complex enough to require the full
2814
+ TypeScript compiler for DTS generation.
2815
+ This is a valid pattern — enable in the base, override in packages that need it.
2816
+
2817
+ #### What This Repo Uses
2818
+
2819
+ | Area | Choice |
2820
+ | --- | --- |
2821
+ | Package manager | Bun workspaces |
2822
+ | Build | Bunup `^0.16.22` (multi-config array) |
2823
+ | Output format | ESM-only |
2824
+ | TypeScript | `^5.9.3`, ES2024, Bundler resolution |
2825
+ | Linting | Biome `^2.3.14` |
2826
+ | Markdown formatting | flowmark (via `uvx`) |
2827
+ | Testing | bun test + tryscript (golden) |
2828
+ | Git hooks | lefthook `^1.11.13` |
2829
+ | Versioning | Changesets `^2.29.8` |
2830
+ | CI | 3-OS matrix + separate lint job |
2831
+ | Release | Tag-triggered OIDC with npm provenance |
2832
+ | Package validation | publint `^0.3.17` |