Skip to content

Conversation

@robobun
Copy link
Collaborator

@robobun robobun commented Dec 16, 2025

Summary

  • Integrates libghostty-vt for full VT100/xterm terminal emulation in Bun.Terminal
  • Adds VT parsing with escape sequence support (CSI, OSC, DCS)
  • Provides screen state tracking with cell access, cursor position, and alternate screen detection

New API

const terminal = new Bun.Terminal({ rows: 24, cols: 80 });

// Feed data to VT parser (string or ArrayBuffer)
terminal.feed("Hello\x1b[32m World\x1b[0m\n");

// Get cell at position
const cell = terminal.at(0, 0); // { char: "H", wide: false, styled: false }

// Get line relative to bottom (0 = bottom line)
const line = terminal.line(0);

// Get full screen text
const text = terminal.text;

// Get cursor state
const cursor = terminal.cursor; // { x: 0, y: 1, visible: true, style: "block" }

// Screen state
terminal.alternateScreen; // false
terminal.scrollbackLines; // 0
terminal.title; // ""

// Control methods
terminal.clear();
terminal.reset();
terminal.close();

Implementation

  • Lazy initialization: VT state is created on first feed() call
  • ghostty integration: Uses ghostty's Terminal/Screen/PageList for state management
  • Unicode support: Pre-generated lookup tables for character width/grapheme detection
  • Build integration: CMake target for libghostty-vt, Zig module integration

Test Plan

  • 86 comprehensive tests covering:
    • Constructor options and validation (8 tests)
    • feed() with various input types (14 tests)
    • at() bounds checking and cell retrieval (12 tests)
    • line() bottom-relative indexing (7 tests)
    • text property (4 tests)
    • cursor property (7 tests)
    • Escape sequence parsing (11 tests)
    • clear() and reset() (6 tests)
    • State after close (2 tests)
    • alternateScreen, scrollbackLines, title properties (7 tests)
    • Line wrapping (2 tests)
    • Integration with spawn (1 test)
    • Resource management (2 tests)
bun bd test test/js/bun/terminal/ghostty.test.ts
# 86 pass, 0 fail

🤖 Generated with Claude Code

Adds libghostty-vt integration to Bun.Terminal for full VT100/xterm
terminal emulation with escape sequence parsing and screen state tracking.

## New API Methods

- `terminal.feed(data)` - Feed string/buffer data to VT parser
- `terminal.at(x, y)` - Get cell info at position (char, wide, styled)
- `terminal.line(n)` - Get line text relative to bottom (0 = bottom)
- `terminal.text` - Get full screen content (getter)
- `terminal.cursor` - Get cursor position and style
- `terminal.clear()` - Clear screen
- `terminal.reset()` - Reset terminal state
- `terminal.alternateScreen` - Check if in alternate screen mode
- `terminal.scrollbackLines` - Get scrollback line count
- `terminal.title` - Get terminal title from OSC sequences

## Implementation Details

- Lazy VT initialization on first `feed()` call
- Uses ghostty's Terminal/Screen/PageList for state management
- Parses CSI, OSC, DCS escape sequences
- Tracks cursor position, style, and visibility
- Supports alternate screen buffer switching
- Cell access with wide character and style detection

## Build Integration

- CMake target for libghostty-vt static library
- Zig build module integration with unicode/symbol tables
- Pre-generated lookup tables for unicode properties

## Test Coverage

86 comprehensive tests covering:
- Constructor options and validation
- feed() with various input types and edge cases
- at() bounds checking and cell retrieval
- line() bottom-relative indexing
- Escape sequence parsing (cursor movement, colors, etc.)
- Error handling and invalid inputs
- Resource management and cleanup

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@robobun
Copy link
Collaborator Author

robobun commented Dec 16, 2025

Updated 1:05 PM PT - Dec 16th, 2025

Your commit 48b027e is building: #33406

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

Walkthrough

This PR integrates the Ghostty Terminal VT emulator into Bun's Terminal API. Changes span build configuration (Zig/CMake), Terminal class JavaScript/Zig bindings, and supporting ghostty modules including symbol tables, build options, and comprehensive tests.

Changes

Cohort / File(s) Summary
Build Configuration
build.zig, cmake/targets/BuildBun.cmake, cmake/targets/BuildGhosttyVt.cmake
Adds Ghostty module wiring into Bun's build system via new addGhosttyModule() helper; registers GhosttyVt CMake dependency; introduces new CMake build script registering ghostty-org/ghostty at v1.1.3 to vendor/ghostty.
Terminal API Implementation
src/bun.js/api/Terminal.classes.ts, src/bun.js/api/bun/Terminal.zig
Adds Terminal VT emulation API with methods feed(), at(), line(), clear(), reset(), resize() and getters for text, cursor, cols, rows, title, alternateScreen, scrollbackLines. Introduces VirtualTerminal wrapper type with full lifecycle management and CellInfo struct for cell queries.
Ghostty Module Support
src/deps/ghostty/symbols_tables.zig, src/deps/ghostty/terminal_options.zig, src/deps/ghostty/build_options.zig
Adds symbol tables generator function, terminal build-time configuration constants (artifact, c_abi, oniguruma, simd, etc.), and SIMD build flag (disabled).
Tests
test/js/bun/terminal/ghostty.test.ts
Introduces comprehensive test suite covering Terminal VT constructor, feed() with multiple input types, cell access via at(), line retrieval, property getters, escape sequence handling, state management, and integration with spawned processes.

Possibly related PRs

  • oven-sh/bun#25415: Adds PTY-backed Terminal/PTY spawn support with complementary Terminal API and JS/Zig bindings modifications.

Suggested reviewers

  • pfgithub
  • dylan-conway
  • taylordotfish

Pre-merge checks

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat(terminal): integrate ghostty-vt for terminal emulation' clearly and concisely summarizes the main change: integrating the ghostty-vt library into Bun's Terminal API for terminal emulation support.
Description check ✅ Passed The PR description comprehensively covers both required sections: 'What does this PR do?' (Summary and Implementation sections) and 'How did you verify your code works?' (Test Plan with 86 passing tests and command provided).

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2071742 and 48b027e.

📒 Files selected for processing (3)
  • cmake/targets/BuildGhosttyVt.cmake (1 hunks)
  • src/deps/ghostty/build_options.zig (1 hunks)
  • src/deps/ghostty/terminal_options.zig (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{cpp,zig}

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

src/**/*.{cpp,zig}: Use bun bd or bun run build:debug to build debug versions for C++ and Zig source files; creates debug build at ./build/debug/bun-debug
Run tests using bun bd test <test-file> with the debug build; never use bun test directly as it will not include your changes
Execute files using bun bd <file> <...args>; never use bun <file> directly as it will not include your changes
Enable debug logs for specific scopes using BUN_DEBUG_$(SCOPE)=1 environment variable
Code generation happens automatically as part of the build process; no manual code generation commands are required

Files:

  • src/deps/ghostty/terminal_options.zig
  • src/deps/ghostty/build_options.zig
src/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

Use bun.Output.scoped(.${SCOPE}, .hidden) for creating debug logs in Zig code

Implement core functionality in Zig, typically in its own directory in src/

src/**/*.zig: Private fields in Zig are fully supported using the # prefix: struct { #foo: u32 };
Use decl literals in Zig for declaration initialization: const decl: Decl = .{ .binding = 0, .value = 0 };
Prefer @import at the bottom of the file (auto formatter will move them automatically)

Files:

  • src/deps/ghostty/terminal_options.zig
  • src/deps/ghostty/build_options.zig
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

**/*.zig: Expose generated bindings in Zig structs using pub const js = JSC.Codegen.JS<ClassName> with trait conversion methods: toJS, fromJS, and fromJSDirect
Use consistent parameter name globalObject instead of ctx in Zig constructor and method implementations
Use bun.JSError!JSValue return type for Zig methods and constructors to enable proper error handling and exception propagation
Implement resource cleanup using deinit() method that releases resources, followed by finalize() called by the GC that invokes deinit() and frees the pointer
Use JSC.markBinding(@src()) in finalize methods for debugging purposes before calling deinit()
For methods returning cached properties in Zig, declare external C++ functions using extern fn and callconv(JSC.conv) calling convention
Implement getter functions with naming pattern get<PropertyName> in Zig that accept this and globalObject parameters and return JSC.JSValue
Access JavaScript CallFrame arguments using callFrame.argument(i), check argument count with callFrame.argumentCount(), and get this with callFrame.thisValue()
For reference-counted objects, use .deref() in finalize instead of destroy() to release references to other JS objects

In Zig code, be careful with allocators and use defer for cleanup

Files:

  • src/deps/ghostty/terminal_options.zig
  • src/deps/ghostty/build_options.zig
🧠 Learnings (4)
📚 Learning: 2025-10-15T22:03:50.832Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 23710
File: src/envvars.zig:135-144
Timestamp: 2025-10-15T22:03:50.832Z
Learning: In src/envvars.zig, the boolean feature flag cache uses a single atomic enum and should remain monotonic. Only the string cache (which uses two atomics: ptr and len) requires acquire/release ordering to prevent torn reads.

Applied to files:

  • src/deps/ghostty/terminal_options.zig
📚 Learning: 2025-11-24T18:34:55.173Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-11-24T18:34:55.173Z
Learning: Applies to src/**/*.{cpp,zig} : Use `bun bd` or `bun run build:debug` to build debug versions for C++ and Zig source files; creates debug build at `./build/debug/bun-debug`

Applied to files:

  • src/deps/ghostty/terminal_options.zig
📚 Learning: 2025-11-24T18:36:08.558Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-11-24T18:36:08.558Z
Learning: Applies to **/*.zig : Expose generated bindings in Zig structs using `pub const js = JSC.Codegen.JS<ClassName>` with trait conversion methods: `toJS`, `fromJS`, and `fromJSDirect`

Applied to files:

  • src/deps/ghostty/build_options.zig
📚 Learning: 2025-10-08T13:56:00.875Z
Learnt from: Jarred-Sumner
Repo: oven-sh/bun PR: 23373
File: src/bun.js/api/BunObject.zig:2514-2521
Timestamp: 2025-10-08T13:56:00.875Z
Learning: For Bun codebase: prefer using `bun.path` utilities (e.g., `bun.path.joinAbsStringBuf`, `bun.path.join`) over `std.fs.path` functions for path operations.

Applied to files:

  • cmake/targets/BuildGhosttyVt.cmake
🔇 Additional comments (1)
src/deps/ghostty/terminal_options.zig (1)

1-31: LGTM! Configuration is internally consistent and well-documented.

The build options are clearly documented with rationale for each setting. The dependency chain is correct: kitty_graphics and tmux_control_mode both require oniguruma, and all three are consistently disabled. The intentional hardcoding for Bun's integration is appropriately noted.


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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/bun.js/api/bun/Terminal.zig (1)

637-695: Resize the VT when dimensions change.

The resize() method updates this.cols and this.rows, but doesn't resize the VirtualTerminal if it exists. This causes the VT to have stale dimensions, leading to incorrect rendering and potential out-of-bounds errors when accessing cells or lines.

Apply this diff to resize the VT:

     this.cols = new_cols;
     this.rows = new_rows;
 
+    // Resize virtual terminal if initialized
+    if (this.vt) |vt| {
+        vt.resize(new_cols, new_rows) catch {
+            return globalObject.throw("Failed to resize virtual terminal", .{});
+        };
+    }
+
     return .js_undefined;
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between dd04c57 and e713816.

📒 Files selected for processing (8)
  • build.zig (2 hunks)
  • cmake/targets/BuildBun.cmake (1 hunks)
  • cmake/targets/BuildGhosttyVt.cmake (1 hunks)
  • src/bun.js/api/Terminal.classes.ts (1 hunks)
  • src/bun.js/api/bun/Terminal.zig (3 hunks)
  • src/deps/ghostty/symbols_tables.zig (1 hunks)
  • src/deps/ghostty/terminal_options.zig (1 hunks)
  • test/js/bun/terminal/ghostty.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
src/**/*.{cpp,zig}

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

src/**/*.{cpp,zig}: Use bun bd or bun run build:debug to build debug versions for C++ and Zig source files; creates debug build at ./build/debug/bun-debug
Run tests using bun bd test <test-file> with the debug build; never use bun test directly as it will not include your changes
Execute files using bun bd <file> <...args>; never use bun <file> directly as it will not include your changes
Enable debug logs for specific scopes using BUN_DEBUG_$(SCOPE)=1 environment variable
Code generation happens automatically as part of the build process; no manual code generation commands are required

Files:

  • src/deps/ghostty/terminal_options.zig
  • src/deps/ghostty/symbols_tables.zig
  • src/bun.js/api/bun/Terminal.zig
src/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

Use bun.Output.scoped(.${SCOPE}, .hidden) for creating debug logs in Zig code

Implement core functionality in Zig, typically in its own directory in src/

src/**/*.zig: Private fields in Zig are fully supported using the # prefix: struct { #foo: u32 };
Use decl literals in Zig for declaration initialization: const decl: Decl = .{ .binding = 0, .value = 0 };
Prefer @import at the bottom of the file (auto formatter will move them automatically)

Files:

  • src/deps/ghostty/terminal_options.zig
  • src/deps/ghostty/symbols_tables.zig
  • src/bun.js/api/bun/Terminal.zig
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

**/*.zig: Expose generated bindings in Zig structs using pub const js = JSC.Codegen.JS<ClassName> with trait conversion methods: toJS, fromJS, and fromJSDirect
Use consistent parameter name globalObject instead of ctx in Zig constructor and method implementations
Use bun.JSError!JSValue return type for Zig methods and constructors to enable proper error handling and exception propagation
Implement resource cleanup using deinit() method that releases resources, followed by finalize() called by the GC that invokes deinit() and frees the pointer
Use JSC.markBinding(@src()) in finalize methods for debugging purposes before calling deinit()
For methods returning cached properties in Zig, declare external C++ functions using extern fn and callconv(JSC.conv) calling convention
Implement getter functions with naming pattern get<PropertyName> in Zig that accept this and globalObject parameters and return JSC.JSValue
Access JavaScript CallFrame arguments using callFrame.argument(i), check argument count with callFrame.argumentCount(), and get this with callFrame.thisValue()
For reference-counted objects, use .deref() in finalize instead of destroy() to release references to other JS objects

In Zig code, be careful with allocators and use defer for cleanup

Files:

  • src/deps/ghostty/terminal_options.zig
  • src/deps/ghostty/symbols_tables.zig
  • build.zig
  • src/bun.js/api/bun/Terminal.zig
*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

In Zig, declare external C++ functions and wrap them in public methods using the convention extern fn Bun__ClassName__toJS(...) and pub fn toJS(...)

Files:

  • build.zig
test/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

test/**/*.{js,ts,jsx,tsx}: Write tests as JavaScript and TypeScript files using Jest-style APIs (test, describe, expect) and import from bun:test
Use test.each and data-driven tests to reduce boilerplate when testing multiple similar cases

Files:

  • test/js/bun/terminal/ghostty.test.ts
test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}

📄 CodeRabbit inference engine (test/CLAUDE.md)

test/**/*.test.{ts,js,jsx,tsx,mjs,cjs}: Use bun:test with files that end in *.test.{ts,js,jsx,tsx,mjs,cjs}
Do not write flaky tests. Never wait for time to pass in tests; always wait for the condition to be met instead of using an arbitrary amount of time
Never use hardcoded port numbers in tests. Always use port: 0 to get a random port
Prefer concurrent tests over sequential tests using test.concurrent or describe.concurrent when multiple tests spawn processes or write files, unless it's very difficult to make them concurrent
When spawning Bun processes in tests, use bunExe and bunEnv from harness to ensure the same build of Bun is used and debug logging is silenced
Use -e flag for single-file tests when spawning Bun processes
Use tempDir() from harness to create temporary directories with files for multi-file tests instead of creating files manually
Prefer async/await over callbacks in tests
When callbacks must be used and it's just a single callback, use Promise.withResolvers to create a promise that can be resolved or rejected from a callback
Do not set a timeout on tests. Bun already has timeouts
Use Buffer.alloc(count, fill).toString() instead of 'A'.repeat(count) to create repetitive strings in tests, as ''.repeat is very slow in debug JavaScriptCore builds
Use describe blocks for grouping related tests
Always use await using or using to ensure proper resource cleanup in tests for APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc
Always check exit codes and test error scenarios in error tests
Use describe.each() for parameterized tests
Use toMatchSnapshot() for snapshot testing
Use beforeAll(), afterEach(), beforeEach() for setup/teardown in tests
Track resources (servers, clients) in arrays for cleanup in afterEach()

Files:

  • test/js/bun/terminal/ghostty.test.ts
**/*.test.ts?(x)

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.ts?(x): Never use bun test directly - always use bun bd test to run tests with debug build changes
For single-file tests, prefer -e flag over tempDir
For multi-file tests, prefer tempDir and Bun.spawn over single-file tests
Use normalizeBunSnapshot to normalize snapshot output of tests
Never write tests that check for 'panic', 'uncaught exception', or similar strings in test output
Use tempDir from harness to create temporary directories - do not use tmpdirSync or fs.mkdtempSync
When spawning processes in tests, expect stdout before expecting exit code for more useful error messages on test failure
Do not write flaky tests - do not use setTimeout in tests; instead await the condition to be met
Verify tests fail with USE_SYSTEM_BUN=1 bun test <file> and pass with bun bd test <file> - tests are invalid if they pass with USE_SYSTEM_BUN=1
Test files must end with .test.ts or .test.tsx
Avoid shell commands like find or grep in tests - use Bun's Glob and built-in tools instead

Files:

  • test/js/bun/terminal/ghostty.test.ts
test/**/*.test.ts?(x)

📄 CodeRabbit inference engine (CLAUDE.md)

Always use port: 0 in tests - do not hardcode ports or use custom random port number functions

Files:

  • test/js/bun/terminal/ghostty.test.ts
**/*.classes.ts

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

**/*.classes.ts: Define JavaScript API using declarative .classes.ts files with properties: name, constructor, JSType, finalize, and proto object containing method/property definitions
Use WriteBarrier caching (cache: true) in .classes.ts property definitions to enable JSC's garbage-collected value storage for computed or lazily-created properties

Files:

  • src/bun.js/api/Terminal.classes.ts
🧠 Learnings (26)
📚 Learning: 2025-11-20T19:51:32.288Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 24880
File: packages/bun-vscode/package.json:382-385
Timestamp: 2025-11-20T19:51:32.288Z
Learning: In the Bun repository, dependencies may be explicitly added to package.json files (even when not directly imported in code) to force version upgrades on transitive dependencies, particularly as part of Aikido security scanner remediation to ensure vulnerable transitive dependencies resolve to patched versions.

Applied to files:

  • cmake/targets/BuildBun.cmake
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/V8*.h : Add BUN_EXPORT visibility attribute to all public V8 API functions to ensure proper symbol export across platforms

Applied to files:

  • cmake/targets/BuildBun.cmake
📚 Learning: 2025-11-24T18:34:55.173Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-11-24T18:34:55.173Z
Learning: Applies to src/**/*.{cpp,zig} : Use `bun bd` or `bun run build:debug` to build debug versions for C++ and Zig source files; creates debug build at `./build/debug/bun-debug`

Applied to files:

  • cmake/targets/BuildBun.cmake
  • src/deps/ghostty/terminal_options.zig
  • build.zig
📚 Learning: 2025-11-24T18:36:08.558Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-11-24T18:36:08.558Z
Learning: Applies to src/bun.js/bindings/generated_classes_list.zig : Include new class bindings in `src/bun.js/bindings/generated_classes_list.zig` to register them with the code generator

Applied to files:

  • cmake/targets/BuildBun.cmake
  • build.zig
📚 Learning: 2025-10-15T22:03:50.832Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 23710
File: src/envvars.zig:135-144
Timestamp: 2025-10-15T22:03:50.832Z
Learning: In src/envvars.zig, the boolean feature flag cache uses a single atomic enum and should remain monotonic. Only the string cache (which uses two atomics: ptr and len) requires acquire/release ordering to prevent torn reads.

Applied to files:

  • src/deps/ghostty/terminal_options.zig
📚 Learning: 2025-10-16T02:17:35.237Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 23710
File: src/analytics.zig:15-21
Timestamp: 2025-10-16T02:17:35.237Z
Learning: In src/analytics.zig and similar files using bun.EnvVar boolean environment variables: the new EnvVar API for boolean flags (e.g., bun.EnvVar.do_not_track.get(), bun.EnvVar.ci.get()) is designed to parse and return boolean values from environment variables, not just check for their presence. This is an intentional design change from the previous presence-based checks using bun.getenvZ().

Applied to files:

  • src/deps/ghostty/terminal_options.zig
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Applies to src/**/index.zig : Create an `index.zig` entry point in your module directory that exports all module functionality

Applied to files:

  • src/deps/ghostty/terminal_options.zig
📚 Learning: 2025-12-16T00:21:32.163Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T00:21:32.163Z
Learning: Code generation happens automatically during the build process - bundled modules can be reloaded without rebuilding Zig by running `bun run build`

Applied to files:

  • src/deps/ghostty/terminal_options.zig
  • build.zig
📚 Learning: 2025-11-24T18:36:08.558Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-11-24T18:36:08.558Z
Learning: Applies to **/*.zig : Expose generated bindings in Zig structs using `pub const js = JSC.Codegen.JS<ClassName>` with trait conversion methods: `toJS`, `fromJS`, and `fromJSDirect`

Applied to files:

  • src/deps/ghostty/symbols_tables.zig
📚 Learning: 2025-08-30T09:09:18.384Z
Learnt from: Jarred-Sumner
Repo: oven-sh/bun PR: 22231
File: src/bundler/bundle_v2.zig:48-48
Timestamp: 2025-08-30T09:09:18.384Z
Learning: In Zig, when a module exports a top-level struct, import("./Module.zig") directly returns that struct type and can be used as a type alias without needing to access a field within the module. This is a common pattern in the Bun codebase.

Applied to files:

  • build.zig
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/src/napi/napi.zig : For each new V8 C++ method, add both GCC/Clang and MSVC mangled symbol names to the V8API struct in src/napi/napi.zig using extern fn declarations

Applied to files:

  • build.zig
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/test/v8/v8.test.ts : Add corresponding test cases to test/v8/v8.test.ts using checkSameOutput() function to compare Node.js and Bun output

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-11-24T18:35:50.422Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: Applies to test/**/*.{js,ts,jsx,tsx} : Write tests as JavaScript and TypeScript files using Jest-style APIs (`test`, `describe`, `expect`) and import from `bun:test`

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-11-24T18:35:50.422Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: See `test/harness.ts` for common test utilities and helpers

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : When spawning Bun processes in tests, use `bunExe` and `bunEnv` from `harness` to ensure the same build of Bun is used and debug logging is silenced

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-11-24T18:35:08.612Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:08.612Z
Learning: Applies to test/bake/dev/css.test.ts : Organize CSS tests in css.test.ts for tests concerning bundling bugs with CSS files

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-10-26T01:32:04.844Z
Learnt from: Jarred-Sumner
Repo: oven-sh/bun PR: 24082
File: test/cli/test/coverage.test.ts:60-112
Timestamp: 2025-10-26T01:32:04.844Z
Learning: In the Bun repository test files (test/cli/test/*.test.ts), when spawning Bun CLI commands with Bun.spawnSync for testing, prefer using stdio: ["inherit", "inherit", "inherit"] to inherit stdio streams rather than piping them.

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-11-24T18:35:08.612Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:08.612Z
Learning: Applies to test/bake/dev/bundle.test.ts : Organize bundle tests in bundle.test.ts for tests concerning bundling bugs that only occur in DevServer

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-10-19T02:44:46.354Z
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: packages/bun-otel/context-propagation.test.ts:1-1
Timestamp: 2025-10-19T02:44:46.354Z
Learning: In the Bun repository, standalone packages under packages/ (e.g., bun-vscode, bun-inspector-protocol, bun-plugin-yaml, bun-plugin-svelte, bun-debug-adapter-protocol, bun-otel) co-locate their tests with package source code using *.test.ts files. This follows standard npm/monorepo patterns. The test/ directory hierarchy (test/js/bun/, test/cli/, test/js/node/) is reserved for testing Bun's core runtime APIs and built-in functionality, not standalone packages.

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*-fixture.ts : Test files that spawn Bun processes should end in `*-fixture.ts` to identify them as test fixtures and not tests themselves

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-11-24T18:35:50.422Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: Applies to test/cli/**/*.{js,ts,jsx,tsx} : When testing Bun as a CLI, use the `spawn` API from `bun` with the `bunExe()` and `bunEnv` from `harness` to execute Bun commands and validate exit codes, stdout, and stderr

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-11-06T00:58:23.965Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 24417
File: test/js/bun/spawn/spawn.test.ts:903-918
Timestamp: 2025-11-06T00:58:23.965Z
Learning: In Bun test files, `await using` with spawn() is appropriate for long-running processes that need guaranteed cleanup on scope exit or when explicitly testing disposal behavior. For short-lived processes that exit naturally (e.g., console.log scripts), the pattern `const proc = spawn(...); await proc.exited;` is standard and more common, as evidenced by 24 instances vs 4 `await using` instances in test/js/bun/spawn/spawn.test.ts.

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : Always use `await using` or `using` to ensure proper resource cleanup in tests for APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-11-24T18:37:30.259Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Unit tests for specific features are organized by module (e.g., `/test/js/bun/`, `/test/js/node/`)

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Add tests for new Bun runtime functionality

Applied to files:

  • test/js/bun/terminal/ghostty.test.ts
📚 Learning: 2025-09-02T06:10:17.252Z
Learnt from: Jarred-Sumner
Repo: oven-sh/bun PR: 22323
File: test/js/web/websocket/websocket-subprotocol-strict.test.ts:80-91
Timestamp: 2025-09-02T06:10:17.252Z
Learning: Bun's WebSocket implementation includes a terminate() method, which is available even though it's not part of the standard WebSocket API. This method can be used in Bun test files and applications for immediate WebSocket closure.

Applied to files:

  • src/bun.js/api/bun/Terminal.zig
🧬 Code graph analysis (1)
test/js/bun/terminal/ghostty.test.ts (1)
test/harness.ts (1)
  • bunExe (102-105)
🔇 Additional comments (6)
cmake/targets/BuildBun.cmake (1)

65-65: LGTM!

The GhosttyVt dependency is correctly added to the BUN_DEPENDENCIES list. This will trigger the inclusion of BuildGhosttyVt.cmake via the foreach loop at line 1251.

src/deps/ghostty/symbols_tables.zig (1)

1-1808: LGTM - Auto-generated lookup tables.

This file contains pre-generated multi-stage lookup tables for symbol/unicode property detection. The comptime-parameterized Tables function allows the element type to be specified at compile time. Since this is auto-generated data, no manual review of the table contents is needed.

src/deps/ghostty/terminal_options.zig (1)

1-28: LGTM - Minimal build configuration for initial integration.

The options are appropriately set for a minimal ghostty-vt integration:

  • Building as a library (.lib) is correct for embedding
  • Disabling C ABI, oniguruma, and SIMD keeps the build simple
  • Feature dependencies are correctly handled (kitty_graphics and tmux_control_mode require oniguruma)

This conservative approach is good for initial integration. SIMD could be enabled later if performance optimization is needed for large terminal buffers.

cmake/targets/BuildGhosttyVt.cmake (1)

12-19: The ghostty tag v1.1.3 is valid and cryptographically verified.

The v1.1.3 tag exists and is signed with the committer's verified GPG signature, created by the project maintainer. The repository registration follows the same pattern used for other vendored dependencies and the version pinning ensures reproducible builds. The documentation clearly explains the integration approach where the ghostty source is cloned to the vendor path for the Zig build system.

build.zig (1)

846-876: Build system integration for ghostty module is correct.

The referenced source files are properly accounted for. The three Bun customization files (terminal_options.zig, unicode_tables.zig, symbols_tables.zig) exist in src/deps/ghostty/. The vendored ghostty files (lib_vt.zig and unicode/props.zig) will be cloned by CMake to vendor/ghostty/ as part of the standard build process—BuildGhosttyVt.cmake registers the ghostty-org/ghostty repository and is included via the dependency loop in BuildBun.cmake.

src/bun.js/api/bun/Terminal.zig (1)

72-77: Verify ArrayList initialization pattern.

The vt_title field is initialized with .empty, but std.ArrayList typically requires initialization with an allocator via .init(allocator) or = .{}. While .empty may be a Bun-specific pattern, the deinit() call at line 965 passes bun.default_allocator, suggesting the ArrayList should have been initialized with that allocator.

Run the following script to check if .empty is used elsewhere in the codebase for ArrayList initialization:

#!/bin/bash
# Search for ArrayList initialization patterns in Zig files
echo "=== Searching for ArrayList.empty pattern ==="
rg -n "ArrayList.*=.*\.empty" --type=zig -C2

echo ""
echo "=== Searching for proper ArrayList initialization ==="
rg -n "ArrayList.*=.*\.init\(" --type=zig -C1

Comment on lines 161 to 166
test("handles very long string", () => {
const terminal = new Bun.Terminal({ rows: 10, cols: 40 });
const longString = "A".repeat(10000);
expect(() => terminal.feed(longString)).not.toThrow();
terminal.close();
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Use Buffer.alloc() for repetitive strings in tests.

The coding guidelines recommend using Buffer.alloc(count, fill).toString() instead of ''.repeat() for creating repetitive strings in tests, as ''.repeat() is very slow in debug JavaScriptCore builds.

Apply this diff:

     test("handles very long string", () => {
       const terminal = new Bun.Terminal({ rows: 10, cols: 40 });
-      const longString = "A".repeat(10000);
+      const longString = Buffer.alloc(10000, "A").toString();
       expect(() => terminal.feed(longString)).not.toThrow();
       terminal.close();
     });

As per coding guidelines for test files.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test("handles very long string", () => {
const terminal = new Bun.Terminal({ rows: 10, cols: 40 });
const longString = "A".repeat(10000);
expect(() => terminal.feed(longString)).not.toThrow();
terminal.close();
});
test("handles very long string", () => {
const terminal = new Bun.Terminal({ rows: 10, cols: 40 });
const longString = Buffer.alloc(10000, "A").toString();
expect(() => terminal.feed(longString)).not.toThrow();
terminal.close();
});
🤖 Prompt for AI Agents
In test/js/bun/terminal/ghostty.test.ts around lines 161 to 166, replace the
slow `"A".repeat(10000)` with a Buffer-based construction per guidelines: create
the longString using Buffer.alloc(10000, "A").toString() so the test builds the
repetitive string efficiently; keep the rest of the test (Terminal creation,
feed call, not.toThrow assertion, and terminal.close()) unchanged.

Claude Bot and others added 3 commits December 16, 2025 12:12
- Remove unused opts parameter from addGhosttyModule function
- Add TODO comment explaining SIMD is disabled for initial integration stability
- Replace string.repeat() with Buffer.alloc() for better performance in tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
SIMD remains disabled because ghostty's SIMD implementation uses C++
code (vt.cpp) that would need to be built and linked separately.
The scalar fallback paths in ghostty provide correct functionality.

Added build_options.zig to provide the simd flag that ghostty's
simd/vt.zig expects from @import("build_options").

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Build ghostty's C++ SIMD implementation (vt.cpp) using highway
- Configure simdutf via webkit wrapper header
- Add utfcpp dependency for UTF-8 error handling with replacement chars
- Enable exceptions for vt.cpp only (required by utfcpp)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c0e1f30 and 2071742.

📒 Files selected for processing (4)
  • cmake/targets/BuildGhosttyVt.cmake (1 hunks)
  • src/deps/ghostty/build_options.zig (1 hunks)
  • src/deps/ghostty/simdutf.h (1 hunks)
  • src/deps/ghostty/terminal_options.zig (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{cpp,zig}

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

src/**/*.{cpp,zig}: Use bun bd or bun run build:debug to build debug versions for C++ and Zig source files; creates debug build at ./build/debug/bun-debug
Run tests using bun bd test <test-file> with the debug build; never use bun test directly as it will not include your changes
Execute files using bun bd <file> <...args>; never use bun <file> directly as it will not include your changes
Enable debug logs for specific scopes using BUN_DEBUG_$(SCOPE)=1 environment variable
Code generation happens automatically as part of the build process; no manual code generation commands are required

Files:

  • src/deps/ghostty/build_options.zig
  • src/deps/ghostty/terminal_options.zig
src/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

Use bun.Output.scoped(.${SCOPE}, .hidden) for creating debug logs in Zig code

Implement core functionality in Zig, typically in its own directory in src/

src/**/*.zig: Private fields in Zig are fully supported using the # prefix: struct { #foo: u32 };
Use decl literals in Zig for declaration initialization: const decl: Decl = .{ .binding = 0, .value = 0 };
Prefer @import at the bottom of the file (auto formatter will move them automatically)

Files:

  • src/deps/ghostty/build_options.zig
  • src/deps/ghostty/terminal_options.zig
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

**/*.zig: Expose generated bindings in Zig structs using pub const js = JSC.Codegen.JS<ClassName> with trait conversion methods: toJS, fromJS, and fromJSDirect
Use consistent parameter name globalObject instead of ctx in Zig constructor and method implementations
Use bun.JSError!JSValue return type for Zig methods and constructors to enable proper error handling and exception propagation
Implement resource cleanup using deinit() method that releases resources, followed by finalize() called by the GC that invokes deinit() and frees the pointer
Use JSC.markBinding(@src()) in finalize methods for debugging purposes before calling deinit()
For methods returning cached properties in Zig, declare external C++ functions using extern fn and callconv(JSC.conv) calling convention
Implement getter functions with naming pattern get<PropertyName> in Zig that accept this and globalObject parameters and return JSC.JSValue
Access JavaScript CallFrame arguments using callFrame.argument(i), check argument count with callFrame.argumentCount(), and get this with callFrame.thisValue()
For reference-counted objects, use .deref() in finalize instead of destroy() to release references to other JS objects

In Zig code, be careful with allocators and use defer for cleanup

Files:

  • src/deps/ghostty/build_options.zig
  • src/deps/ghostty/terminal_options.zig
🧠 Learnings (16)
📓 Common learnings
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/js/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:11.466Z
Learning: Write JS builtins for Bun's Node.js compatibility and APIs, and run `bun bd` after changes
Learnt from: theshadow27
Repo: oven-sh/bun PR: 23798
File: packages/bun-otel/context-propagation.test.ts:1-1
Timestamp: 2025-10-19T02:44:46.354Z
Learning: In the Bun repository, standalone packages under packages/ (e.g., bun-vscode, bun-inspector-protocol, bun-plugin-yaml, bun-plugin-svelte, bun-debug-adapter-protocol, bun-otel) co-locate their tests with package source code using *.test.ts files. This follows standard npm/monorepo patterns. The test/ directory hierarchy (test/js/bun/, test/cli/, test/js/node/) is reserved for testing Bun's core runtime APIs and built-in functionality, not standalone packages.
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: test/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:37:30.259Z
Learning: Applies to test/**/*.test.{ts,js,jsx,tsx,mjs,cjs} : When spawning Bun processes in tests, use `bunExe` and `bunEnv` from `harness` to ensure the same build of Bun is used and debug logging is silenced
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-11-24T18:35:50.422Z
Learning: Applies to test/cli/**/*.{js,ts,jsx,tsx} : When testing Bun as a CLI, use the `spawn` API from `bun` with the `bunExe()` and `bunEnv` from `harness` to execute Bun commands and validate exit codes, stdout, and stderr
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-11-24T18:34:55.173Z
Learning: Applies to src/**/*.{cpp,zig} : Run tests using `bun bd test <test-file>` with the debug build; never use `bun test` directly as it will not include your changes
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Add tests for new Bun runtime functionality
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/test/v8/v8.test.ts : Add corresponding test cases to test/v8/v8.test.ts using checkSameOutput() function to compare Node.js and Bun output
📚 Learning: 2025-10-01T21:48:38.278Z
Learnt from: taylordotfish
Repo: oven-sh/bun PR: 23169
File: src/bun.js/bindings/Bindgen/IDLTypes.h:1-3
Timestamp: 2025-10-01T21:48:38.278Z
Learning: In the Bun codebase, for `BunIDL*` and `Bindgen*` headers (e.g., BunIDLTypes.h, Bindgen/IDLTypes.h), it's acceptable to rely on transitive includes for standard library headers like <type_traits> and <utility> rather than including them explicitly.

Applied to files:

  • src/deps/ghostty/simdutf.h
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/V8*.h : Create V8 class headers with .h extension following the pattern V8ClassName.h that include pragma once, v8.h, V8Local.h, V8Isolate.h, and declare classes extending from Data with BUN_EXPORT static methods

Applied to files:

  • src/deps/ghostty/simdutf.h
📚 Learning: 2025-11-24T18:36:59.706Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: src/bun.js/bindings/v8/CLAUDE.md:0-0
Timestamp: 2025-11-24T18:36:59.706Z
Learning: Applies to src/bun.js/bindings/v8/V8*.h : Add BUN_EXPORT visibility attribute to all public V8 API functions to ensure proper symbol export across platforms

Applied to files:

  • src/deps/ghostty/simdutf.h
📚 Learning: 2025-09-20T05:35:57.318Z
Learnt from: pfgithub
Repo: oven-sh/bun PR: 22534
File: src/bun.js/bindings/headers.h:729-731
Timestamp: 2025-09-20T05:35:57.318Z
Learning: symbols.txt in the Bun codebase is specifically for V8 API mangled symbols (without leading underscore), not for general Bun host functions declared with BUN_DECLARE_HOST_FUNCTION. Host functions are handled through different build mechanisms.

Applied to files:

  • src/deps/ghostty/simdutf.h
📚 Learning: 2025-10-15T20:19:38.580Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 23680
File: cmake/targets/BuildBun.cmake:822-822
Timestamp: 2025-10-15T20:19:38.580Z
Learning: In the Bun codebase, FFI is compiled with tcc (TinyCC), which barely supports C99. The headers `src/bun.js/api/FFI.h` and `src/bun.js/api/ffi-stdbool.h` are only used for FFI compilation with tcc, not for the main Bun target. Therefore, C23 compatibility concerns (such as bool/true/false keyword conflicts) do not apply to these FFI headers.

Applied to files:

  • src/deps/ghostty/simdutf.h
📚 Learning: 2025-11-24T18:36:08.558Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-11-24T18:36:08.558Z
Learning: Applies to **/*.zig : Expose generated bindings in Zig structs using `pub const js = JSC.Codegen.JS<ClassName>` with trait conversion methods: `toJS`, `fromJS`, and `fromJSDirect`

Applied to files:

  • src/deps/ghostty/build_options.zig
📚 Learning: 2025-11-24T18:34:55.173Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-11-24T18:34:55.173Z
Learning: Applies to src/**/*.{cpp,zig} : Code generation happens automatically as part of the build process; no manual code generation commands are required

Applied to files:

  • src/deps/ghostty/build_options.zig
📚 Learning: 2025-11-24T18:34:55.173Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-11-24T18:34:55.173Z
Learning: Applies to src/**/*.{cpp,zig} : Use `bun bd` or `bun run build:debug` to build debug versions for C++ and Zig source files; creates debug build at `./build/debug/bun-debug`

Applied to files:

  • cmake/targets/BuildGhosttyVt.cmake
  • src/deps/ghostty/terminal_options.zig
📚 Learning: 2025-11-24T18:36:08.558Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-11-24T18:36:08.558Z
Learning: Applies to src/bun.js/bindings/generated_classes_list.zig : Include new class bindings in `src/bun.js/bindings/generated_classes_list.zig` to register them with the code generator

Applied to files:

  • cmake/targets/BuildGhosttyVt.cmake
📚 Learning: 2025-11-24T18:34:55.173Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-11-24T18:34:55.173Z
Learning: Applies to src/**/*.{cpp,zig} : Run tests using `bun bd test <test-file>` with the debug build; never use `bun test` directly as it will not include your changes

Applied to files:

  • cmake/targets/BuildGhosttyVt.cmake
📚 Learning: 2025-12-16T00:21:32.163Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T00:21:32.163Z
Learning: Run `bun run zig:check-all` to compile Zig code on all platforms when making platform-specific changes

Applied to files:

  • cmake/targets/BuildGhosttyVt.cmake
📚 Learning: 2025-11-24T18:34:55.173Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/building-bun.mdc:0-0
Timestamp: 2025-11-24T18:34:55.173Z
Learning: Applies to src/**/*.{cpp,zig} : Execute files using `bun bd <file> <...args>`; never use `bun <file>` directly as it will not include your changes

Applied to files:

  • cmake/targets/BuildGhosttyVt.cmake
📚 Learning: 2025-12-16T00:21:32.163Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T00:21:32.163Z
Learning: Code generation happens automatically during the build process - bundled modules can be reloaded without rebuilding Zig by running `bun run build`

Applied to files:

  • cmake/targets/BuildGhosttyVt.cmake
  • src/deps/ghostty/terminal_options.zig
📚 Learning: 2025-11-24T18:35:39.205Z
Learnt from: CR
Repo: oven-sh/bun PR: 0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-11-24T18:35:39.205Z
Learning: Applies to src/**/index.zig : Create an `index.zig` entry point in your module directory that exports all module functionality

Applied to files:

  • src/deps/ghostty/terminal_options.zig
📚 Learning: 2025-10-15T22:03:50.832Z
Learnt from: markovejnovic
Repo: oven-sh/bun PR: 23710
File: src/envvars.zig:135-144
Timestamp: 2025-10-15T22:03:50.832Z
Learning: In src/envvars.zig, the boolean feature flag cache uses a single atomic enum and should remain monotonic. Only the string cache (which uses two atomics: ptr and len) requires acquire/release ordering to prevent torn reads.

Applied to files:

  • src/deps/ghostty/terminal_options.zig
🔇 Additional comments (4)
cmake/targets/BuildGhosttyVt.cmake (1)

34-63: LGTM! Well-structured SIMD library configuration.

The selective exception enablement for the SIMD source file is appropriate, and the include directory configuration correctly references all necessary dependencies. The use of HWY_STATIC_DEFINE aligns with the static linkage pattern.

src/deps/ghostty/simdutf.h (1)

1-5: LGTM! Clean compatibility wrapper.

The header correctly redirects ghostty's expected <simdutf.h> include to Bun's WebKit-provided SIMDUTF implementation. The approach is clean and well-documented.

src/deps/ghostty/terminal_options.zig (2)

1-11: LGTM! Clean artifact configuration.

The enum and artifact constant are well-defined for library-mode integration. The .lib artifact type correctly reflects Bun's usage pattern.


12-29: Configuration choices are appropriate for Bun integration.

The build options correctly balance functionality and simplicity:

  • SIMD enabled for performance (addresses previous review feedback)
  • Optional features (oniguruma, kitty_graphics, tmux_control_mode) appropriately disabled for minimal build
  • Dependency constraints are satisfied (graphics and control mode require oniguruma, both disabled)
  • Runtime safety checks disabled for production

Comment on lines 12 to 29
register_repository(
NAME
ghostty
REPOSITORY
ghostty-org/ghostty
TAG
v1.1.3
)

# utfcpp - header-only UTF-8 library for error handling in SIMD code
register_repository(
NAME
utfcpp
REPOSITORY
nemtrif/utfcpp
TAG
v4.0.5
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Verify utfcpp release information and check for CVE advisories
curl -s "https://api.github.com/repos/nemtrif/utfcpp/releases" | jq -r '.[] | "\(.tag_name): \(.published_at)"' | head -20

Repository: oven-sh/bun

Length of output: 630


Update ghostty and utfcpp to newer versions.

Ghostty v1.2.3 (released October 23, 2025) is available; the current pin to v1.1.3 is outdated. Ghostty v1.2.0 (September 15, 2025) introduced a security fix where Ghostty now unconditionally asks for permission prior to executing any script or executable, which is not present in v1.1.3.

For utfcpp, v4.0.8 (released September 2025) is the latest available; the current pin to v4.0.5 (released December 2023) is nearly two years old with multiple newer patch releases (v4.0.6, v4.0.7, v4.0.8) available.

🤖 Prompt for AI Agents
In cmake/targets/BuildGhosttyVt.cmake around lines 12 to 29 the repository TAG
pins are outdated; update the ghostty TAG from v1.1.3 to v1.2.3 and update the
utfcpp TAG from v4.0.5 to v4.0.8 to pick up the security and patch fixes, then
run a configure/build and tests to ensure compatibility after the version bumps.

Comment on lines 65 to 75
# Ensure dependencies are built first
add_dependencies(ghostty-simd clone-ghostty clone-utfcpp)
if(TARGET highway)
add_dependencies(ghostty-simd highway)
endif()

# Link ghostty-simd into bun
target_link_libraries(${bun} PRIVATE ghostty-simd)

# Link highway library
target_link_libraries(${bun} PRIVATE ${BUILD_PATH}/highway/libhwy.a)
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider linking against the highway target instead of hardcoded path.

Line 75 uses a hardcoded path to libhwy.a. If the highway target is available (checked on line 67), linking against the target would be more robust and portable.

Apply this diff to use target-based linking:

 # Link ghostty-simd into bun
 target_link_libraries(${bun} PRIVATE ghostty-simd)
 
-# Link highway library
-target_link_libraries(${bun} PRIVATE ${BUILD_PATH}/highway/libhwy.a)
+# Link highway library (if available as a target, use it; otherwise fall back to path)
+if(TARGET highway)
+  target_link_libraries(${bun} PRIVATE highway)
+else()
+  target_link_libraries(${bun} PRIVATE ${BUILD_PATH}/highway/libhwy.a)
+endif()
🤖 Prompt for AI Agents
In cmake/targets/BuildGhosttyVt.cmake around lines 65 to 75, replace the
hardcoded link to ${BUILD_PATH}/highway/libhwy.a with target-based linking when
the highway target exists: modify the target_link_libraries call to link PRIVATE
highway if TARGET highway is true (e.g. use if(TARGET highway)
target_link_libraries(${bun} PRIVATE highway) else target_link_libraries(${bun}
PRIVATE ${BUILD_PATH}/highway/libhwy.a) endif()), preserving the existing
add_dependencies so the highway target is built first and falling back to the
file path only when the target is not available.

Comment on lines 1 to 7
//! Build options for ghostty-vt module.
//! This provides the build_options that ghostty's SIMD code expects.

/// SIMD acceleration for optimized UTF-8 and escape sequence parsing.
/// Uses highway SIMD library with simdutf for fast UTF-8 decoding.
/// Note: Keep in sync with terminal_options.simd
pub const simd = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider consolidating SIMD configuration to avoid sync issues.

Line 6 notes "Keep in sync with terminal_options.simd", which introduces a manual synchronization requirement. This duplication could lead to configuration drift.

Consider one of these approaches:

  • Have one file import from the other: pub const simd = @import("terminal_options").simd;
  • Generate both from a single source of truth during the build process
  • Document why both are needed if the separation is intentional (e.g., different build phases)

This would eliminate the manual sync requirement and reduce maintenance burden.

This reverts commit 2071742.

SIMD cannot be enabled with official ghostty v1.1.3 due to Zig compatibility
issues. Ghostty uses Zig syntax that's incompatible with Bun's Zig 0.15.2
(duplicate struct member name errors from usingnamespace patterns).

SIMD will need to be re-enabled once a compatible ghostty version is available.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@remorses
Copy link

remorses commented Dec 24, 2025

Working on something similar in opentui: sst/opentui#440

I also export stateless functions to convert ANSI to text without a pty and also a way to render the first N lines cheaply without parsing the full terminal output (to show the collapsed bash tool outputs in opencode fast)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants