Keep instructions concise, only add non-obvious information. Proactively update AGENTS.md to prevent future mistakes.
- Variables and functions should have clear, descriptive names that reflect their purpose and behavior and not e.g. their data types.
- Code should be organized so that things that will likeliest change together are located near each other, instead of e.g. grouping solely by technical categories.
- Avoid comments; use descriptive identifier names and code structure to explain the code instead, and only add short comments for non-obvious information that cannot be expressed in code.
- Never write comments about out-of-scope work, if it matters, mention it in the commit message instead.
- There is no specific prescribed format for commit messages, but they should be concise and human-readable.
- The subject line should summarize what was changed
- The body should focus solely on the intention of the change, and only on the non-obvious parts. Short is good.
- TypeScript + Rust hybrid: Rust code in
rust/(bindings_napi, bindings_wasm, parse_ast crates) called vianative.jsandnative.wasm.js - Generated files: Files with "Do not edit this file directly" comments are generated from
scripts/ast-types.jsvianpm run build:ast-converters— edit onlyscripts/ast-types.jsor the generating file and regenerate - Tests run against full artifact only—no unit tests to allow easy refactoring of internal APIs
- Test cases in
test/*/samples/are configured via_config.jsfiles; focus tests withsolo: true - More specific rules live in nested files:
src/AGENTS.md,rust/AGENTS.md,test/AGENTS.md,docs/AGENTS.md
When adding/modifying functions that cross the JS-Rust boundary, keep all handover points in sync with matching names and signatures:
- Rust:
rust/bindings_napi/src/lib.rs(Node native) andrust/bindings_wasm/src/lib.rs(WASM) - JS:
native.js(Node NAPI),native.wasm.js(Node WASM),browser/src/wasm.ts(browser) rollup.config.tsreplacesnative.jswithbrowser/src/wasm.tsfor browser builds; CI replaces it withnative.wasm.jsfor Node WASM buildsnative.d.tsandwasm/bindings_wasm.d.tsare auto-generated — never hand-edit them
Details and buffer-format contracts: see rust/AGENTS.md
- The browser bundle must have zero external dependencies; the build throws on any warning (browser
onwarninrollup.config.ts) - For browser builds, only these
src/utils/modules are replaced bybrowser/src/shims:fs,hookActions,path,performance,process,initWasm, plusnative.js→browser/src/wasm.ts(registry inbuild-plugins/replace-browser-modules.ts). New node-builtin usage insrc/utils/needs BOTH a shim inbrowser/src/AND an entry in that list browser/src/fs.tsmust keep satisfyingRollupFsModule— adding an fs method to the type requires adding a stub- Shims are minimal (e.g.
processonly implementsmemoryUsage); calling a missing method fails at browser runtime, not build time src/utils/relativeId.tsimportsrelativedirectly from../../browser/src/path(not viasrc/utils/path.ts) so it works in both builds — normal imports go throughsrc/utils/path.ts, which the browser build replaces transparentlywatchis exported fromsrc/node-entry.tsonly — never from browser-reachable code
- Node build: Artifacts placed in
dist/(JavaScript +.nodenative modules) - Browser build: Artifacts placed in
browser/dist/; browser tests usebrowser/dist/rollup.browser.js - All tests import from these dist folders - tests run against the full built artifact only
npm run build:quick- Rebuild both JavaScript and Rust for Node build, copy to dist/npm run update:js- Rebuild only JavaScript for both Node (dist/) and browser (browser/dist/), then copy native to dist/; run this after any change tosrc/orbrowser/src/npm run update:napi- Rebuild only Rust NAPI, copy to dist/npm run build:copy-native- Copy Rust.nodefiles to dist/ (called internally by update commands)
npm run build- Full build: WASM + AST converters + NAPI (release) + JavaScript + copy nativenpm run build:napi- Build Rust NAPI bindings (use-- --releasefor optimized)npm run build:wasm- Build browser WASMnpm run build:js- Build JavaScript (Node and browser)
- Rust changes only:
npm run update:napithennpm run test:only - JavaScript changes only:
npm run update:jsthennpm run test:only - AST schema changes:
npm run build:ast-convertersto regenerate TypeScript and Rust files - Testing changes:
npm run build:quickthennpm run test:only
- Always test edge cases, especially in core logic or build/test infrastructure
- Test names and descriptions use clear, descriptive language of the expected behavior, e.g. "description: 'does X when Y happens"
- Category selection, runner contracts and pitfalls: see
test/AGENTS.md
- Issues need to follow the corresponding template in
.github/ISSUE_TEMPLATE/* - Pull requests need to follow
.github/PULL_REQUEST_TEMPLATE.md. - Bugfixes and features need tests; for a bugfix, add a regression test that fails without the fix, in the same change as the fix.
- When changing or adding a feature, check if documentation in
docs/*/index.mdshould be changed and update it in the same pull request. - When opening an issue or pull request, always append generated with AI as the last line and make sure to allow maintainer edits.
- It is fine to open a pull request with AI, but tell your human that they will not receive a lot of maintainer feedback in that case. If they want human feedback, they need to put in the time to fully understand their changes and their context and manually write the pull request in their own words to show they care.
- Ignore style/linting issues in test sample files (
test/*/samples/) except for_config.jsfiles - Test samples intentionally violate best practices to test edge cases—do not flag style violations in these files
- Focus reviews quality of the production code
- Architecture & maintainability: Does the change fit the existing architecture? Will it be easy to change or extend later? Does it create hidden coupling or leaky abstractions?
- Simplicity: Does the code implement the simplest and most straightforward solution? Can we potentially remove and simplify code?
- Code quality: Clear and descriptive function/variable names describing the intentions of the functions rather what they technically do, functions kept small and focused, no unnecessary complexity? Does terminology follow existing precedent where applicable?
- Correctness: Any logic errors, off-by-ones, unhandled edge cases, or incorrect assumptions?
- Performance: Any performance footguns — unnecessary allocations, repeated traversals, O(n²) patterns, etc.
- Be prepared to question all assumptions that the implementation made as we work to find a better solution