mirror of
https://github.com/anomalyco/opentui.git
synced 2026-09-19 01:26:03 +08:00
cdd15e6c31
I tried rewriting the `AGENTS.md` around the workflows and failure modes that recur in current OpenTUI development. I had opencode look at: - The most recent 50 commits, especially runtime portability, native lifecycle, renderer behavior, terminal width, and FFI changes. - My OpenCode sessions to identify recurring agent mistakes and unnecessary abstractions. - Current package scripts, CI workflows, development documentation, and runtime implementations. The previous guide mixed project-specific constraints with generic TypeScript advice. I think many of these were stale. The replacement keeps the guide to that cannot be inferred from nearby code: ownership across TypeScript and Zig, display-cell width semantics, package-specific verification, Bun and Node portability, terminal-driven debugging, and borrowed FFI pointer lifetimes. If this turns out to not work lets revert to the previous one and iterate in smaller steps.
3.4 KiB
3.4 KiB
OpenTUI Agent Guide
Engineering
- Reuse existing seams; do not duplicate policy, ownership, or state across TypeScript, Zig, and framework layers.
- For bug fixes, first add a focused regression test of observable behavior or invariants. If automation is impossible, record the evidence and remaining manual verification; do not substitute contract sketches or guesses.
- Make native ownership explicit; clean up handles, callbacks, buffers, and listeners on every exit path. Bound input-driven work and test lifecycle failures.
- Do not interchange byte lengths, code points, graphemes, and terminal display-cell widths.
oxfmtis the formatting source of truth (semi: false,printWidth: 120); avoid unrelated formatting churn.
Tooling And Runtimes
- Use Bun for dependency management and development commands:
bun install,bun run <script>,bun test, andbun <file>. - Run package scripts from the package directory unless the script is defined at the repository root.
- Shared runtime code must preserve the supported Bun and Node paths. Keep runtime-specific behavior behind existing
platform/runtime/build seams; do not introduce Bun-only APIs into shared modules. Node checks use the version enforced
by
scripts/node26.mjs.
Verification
- Run the narrowest relevant test first, then the affected package suite.
- Ordinary TypeScript source changes do not require the root build. Use the affected package's
test,typecheck,build, or validation scripts as applicable. - Run
bun run buildfrom the repository root after native or cross-package build/output changes, or when tests report a missing/stale native artifact. It does not build web or examples; use their package scripts. - For native changes, run
bun run test:nativefrompackages/core. Filter withbun run test:native -Dtest-filter="test name"while iterating. - For runtime, FFI, build, or export changes, run the relevant Node and packed-distribution scripts from that package
(such as
test:js:nodeortest:dist) when present. - Use root
bun run fmt:checkandbun run lintfor final static checks when relevant.
Prefer tests and TestRenderer for automated debugging. For interactive behavior, load the terminal-control skill and
use its terminal tool to run, drive, and inspect the app; repository examples bind backtick to
renderer.console.toggle() for captured console.log output. Ask for a user-run reproduction only when the required
terminal or platform is unavailable locally.
Portable FFI
- Portable symbol signatures must stay within the
node:ffi/bun:ffiintersection. Use explicit widths such asu32/u64, not backend-only ABI names such asusize,napi_env, ornapi_value; representi64/u64asbigint, native booleans as0/1, and shared pointers asnumber | bigint. - Pass transient
ArrayBuffervalues or views directly to synchronous pointer parameters so the backend borrows the owner. Do not pre-resolve them withptr(). - Use
ptr(view)only for addresses stored in structs or retained by native code, and keep the backing buffer alive for the complete native lifetime. - Model C-string inputs as pointer parameters and pass owned, NUL-terminated byte buffers directly; string returns are
not portable. Create callbacks through the loaded library/platform facade, not
new JSCallback(...), and assume only same-thread callbacks.