From f72b5793a5c7e7891e4904be73c0bc6bc038fa18 Mon Sep 17 00:00:00 2001 From: T Date: Wed, 27 May 2026 16:34:32 -0600 Subject: system agent definition scaffolding --- build/gen_agent_embed.zig | 126 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 build/gen_agent_embed.zig (limited to 'build/gen_agent_embed.zig') diff --git a/build/gen_agent_embed.zig b/build/gen_agent_embed.zig new file mode 100644 index 0000000..b705863 --- /dev/null +++ b/build/gen_agent_embed.zig @@ -0,0 +1,126 @@ +//! Build-time codegen: walk the in-repo `agent/` tree and emit a Zig +//! module that exposes every file as an `@embedFile` entry. +//! +//! Invoked from `build.zig`: +//! +//! gen-agent-embed +//! +//! `` is the repo's `agent/` directory. `` is +//! the subpath the generated zig file's neighboring `addCopyDirectory` +//! stages the tree under; every `@embedFile` reference uses this +//! prefix so Zig can resolve it relative to the generated module's +//! directory. +//! +//! The generated file looks like: +//! +//! pub const Entry = struct { +//! /// Path relative to the agent tree root (e.g. "tools/read.lua"). +//! path: []const u8, +//! /// File contents. +//! contents: []const u8, +//! }; +//! pub const files: []const Entry = &.{ +//! .{ .path = "tools/read.lua", .contents = @embedFile("...") }, +//! ... +//! }; +//! +//! Bootstrap walks `files` and stages each entry at +//! `$PANTO_HOME/agent/` using the same `writeIfDifferent` +//! discipline as `stageLuaHeaders`. + +const std = @import("std"); + +pub fn main(init: std.process.Init) !void { + const arena = init.arena.allocator(); + const io = init.io; + + var args = init.minimal.args.iterate(); + defer args.deinit(); + _ = args.next(); // skip program name + const embed_prefix = args.next() orelse return error.MissingEmbedPrefix; + const src_dir_arg = args.next() orelse return error.MissingSrcDir; + const out_path_arg = args.next() orelse return error.MissingOutPath; + + var entries: std.array_list.Managed(Entry) = .init(arena); + try collect(arena, io, src_dir_arg, embed_prefix, &entries); + + // Sort for deterministic output across builds. + std.mem.sort(Entry, entries.items, {}, Entry.lessThan); + + var out_file = try std.Io.Dir.cwd().createFile(io, out_path_arg, .{}); + defer out_file.close(io); + var buf: [4096]u8 = undefined; + var writer = out_file.writer(io, &buf); + const w = &writer.interface; + + try w.writeAll( + \\//! Auto-generated. Do not edit. See build/gen_agent_embed.zig. + \\ + \\pub const Entry = struct { + \\ /// Path relative to the agent tree root (e.g. "tools/read.lua"). + \\ path: []const u8, + \\ /// File contents (raw bytes). + \\ contents: []const u8, + \\}; + \\ + \\pub const files: []const Entry = &.{ + \\ + ); + + for (entries.items) |e| { + try w.print( + " .{{ .path = \"{s}\", .contents = @embedFile(\"{s}\") }},\n", + .{ e.rel_path, e.embed_path }, + ); + } + + try w.writeAll("};\n"); + try w.flush(); +} + +const Entry = struct { + /// Path inside the agent tree, forward-slash separated. + rel_path: []const u8, + /// Path the generated `@embedFile` references (under embed prefix). + embed_path: []const u8, + + fn lessThan(_: void, a: Entry, b: Entry) bool { + return std.mem.lessThan(u8, a.rel_path, b.rel_path); + } +}; + +/// Walk `` recursively, collecting every file. Dotfiles +/// (entries whose basename starts with `.`) are skipped at every +/// level so we don't accidentally embed editor backups, `.DS_Store`, +/// or VCS metadata. +fn collect( + arena: std.mem.Allocator, + io: std.Io, + root_abs: []const u8, + embed_prefix: []const u8, + out: *std.array_list.Managed(Entry), +) !void { + var dir = std.Io.Dir.cwd().openDir(io, root_abs, .{ .iterate = true }) catch |err| switch (err) { + error.FileNotFound => return, + else => return err, + }; + defer dir.close(io); + + var walker = try dir.walk(arena); + defer walker.deinit(); + + while (try walker.next(io)) |entry| { + if (entry.kind != .file) continue; + if (entry.basename.len == 0 or entry.basename[0] == '.') continue; + // Skip anything along the path that looks like a dotfile dir. + if (std.mem.indexOf(u8, entry.path, "/.") != null) continue; + + const rel_path = try arena.dupe(u8, entry.path); + const embed_path = try std.fs.path.join(arena, &.{ embed_prefix, entry.path }); + + try out.append(.{ + .rel_path = rel_path, + .embed_path = embed_path, + }); + } +} -- cgit v1.3