Skip to content

Latest commit

 

History

History
145 lines (103 loc) · 3.51 KB

File metadata and controls

145 lines (103 loc) · 3.51 KB

AGENTS.md

Zig Development

Always use zigdoc to discover APIs for the Zig standard library and any third-party dependencies.

Examples:

zigdoc std.fs
zigdoc std.posix.getuid
zigdoc ghostty-vt.Terminal
zigdoc vaxis.Window

Pre-Merge Checks

Run task lint before merging. This includes zig fmt --check and ziglint on all handwritten Zig files configured in .ziglint.zon; generated protobuf files under src/proto are excluded. ziglint is required for code quality but should stay out of the default zig build path.

Run the ziglint-only check with:

task lint:zig

Common Zig Patterns

These patterns reflect current Zig APIs and may differ from older documentation.

ArrayList:

var list: std.ArrayList(u32) = .empty;
defer list.deinit(allocator);
try list.append(allocator, 42);

HashMap/StringHashMap (unmanaged):

var map: std.StringHashMapUnmanaged(u32) = .empty;
defer map.deinit(allocator);
try map.put(allocator, "key", 42);

HashMap/StringHashMap (managed):

var map: std.StringHashMap(u32) = std.StringHashMap(u32).init(allocator);
defer map.deinit();
try map.put("key", 42);

stdout/stderr Writer:

var buf: [4096]u8 = undefined;
const writer = std.fs.File.stdout().writer(&buf);
defer writer.flush() catch {};
try writer.print("hello {s}\n", .{"world"});

build.zig executable/test:

b.addExecutable(.{
    .name = "foo",
    .root_module = b.createModule(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    }),
});

Zig Code Style

Naming:

  • camelCase for functions and methods
  • snake_case for variables and parameters
  • PascalCase for types, structs, and enums
  • SCREAMING_SNAKE_CASE for constants

Struct initialization: Prefer explicit type annotation with anonymous literals:

const foo: Type = .{ .field = value };  // Good
const foo = Type{ .field = value };     // Avoid

File structure:

  1. //! doc comment describing the module
  2. const Self = @This(); (for self-referential types)
  3. Imports: stdbuiltin → project modules
  4. const log = std.log.scoped(.module_name);

Functions: Order methods as initdeinit → public API → private helpers

Memory: Pass allocators explicitly, use errdefer for cleanup on error

Documentation: Use /// for public API, // for implementation notes. Always explain why, not just what.

Tests: Inline in the same file, register in src/main.zig test block

Safety Conventions

Inspired by TigerStyle.

Assertions:

  • Add assertions that catch real bugs, not trivially true statements
  • Focus on API boundaries and state transitions where invariants matter
  • Good: bounds checks, null checks before dereference, state machine transitions
  • Avoid: asserting something immediately after setting it, checking internal function arguments

Function size:

  • Soft limit of 70 lines per function
  • Centralize control flow (switch/if) in parent functions
  • Push pure computation to helper functions

Comments:

  • Explain why the code exists, not what it does
  • Document non-obvious thresholds, timing values, protocol details

Diagnostic Message Coloring

When syntax-highlighting code in diagnostic messages:

  • Blue: functions (assert, init)
  • Purple: keywords (and, or, const)
  • Yellow: identifiers, variables
  • Magenta: types
  • Dim: punctuation, backticks