From 760afbf471bcb6038ec2f49f56e9744f0d619f7d Mon Sep 17 00:00:00 2001 From: slhx agent Date: Sun, 21 Jun 2026 01:39:52 +0200 Subject: [PATCH] Build Zig skeleton with canonical checks --- AGENTS.md | 9 +++++- build.zig | 37 +++++++++++++++++++++ src/main.zig | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 build.zig create mode 100644 src/main.zig diff --git a/AGENTS.md b/AGENTS.md index f0d0e04..3fd3f50 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,7 +29,14 @@ redgate health Every implementation slice needs carry-their-weight tests: regular tests that prove the intended payoff works, plus adversarial tests for realistic failure, boundary, or regression cases. Prefer protocol/headless/replay/fixture tests over brittle UI snapshots unless rendering behavior is the claim. See req: testing/001 through req: testing/004. -No implementation build/test command exists yet. When the Zig project is created, record the canonical build/test command here. +Canonical implementation checks: + +```sh +zig build +zig build test +zig build run -- --help +``` + ## Work tracking diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..e46f6ab --- /dev/null +++ b/build.zig @@ -0,0 +1,37 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const mod = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + const exe = b.addExecutable(.{ + .name = "mim", + .root_module = mod, + }); + b.installArtifact(exe); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + const run_step = b.step("run", "Run mim"); + run_step.dependOn(&run_cmd.step); + + const tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), + }); + const test_cmd = b.addRunArtifact(tests); + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&test_cmd.step); +} diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..11f68ca --- /dev/null +++ b/src/main.zig @@ -0,0 +1,90 @@ +const std = @import("std"); + +pub const version = "0.1.0-dev"; + +const help_text = + \\mim - mobile-first terminal code editor for SSH sessions + \\ + \\Usage: + \\ mim [--help] + \\ mim [--version] + \\ + \\This skeleton intentionally starts with only the canonical smoke path. + \\Future editor behavior is added through verified product slices. + \\ +; + +const Command = union(enum) { + help, + version, + smoke, + unknown: []const u8, +}; + +fn parseArgs(args: []const []const u8) Command { + if (args.len <= 1) return .smoke; + + const arg = args[1]; + if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) return .help; + if (std.mem.eql(u8, arg, "--version")) return .version; + return .{ .unknown = arg }; +} + +fn versionText() []const u8 { + return "mim " ++ version ++ "\n"; +} + +pub fn main(init: std.process.Init) !u8 { + var args = std.process.Args.Iterator.init(init.minimal.args); + _ = args.skip(); + + const first_arg = args.next(); + const command = if (first_arg) |arg| + parseArgs(&.{ "mim", arg }) + else + parseArgs(&.{"mim"}); + + const stdout = std.Io.File.stdout(); + const stderr = std.Io.File.stderr(); + + switch (command) { + .help => try stdout.writeStreamingAll(init.io, help_text), + .version => try stdout.writeStreamingAll(init.io, versionText()), + .smoke => try stdout.writeStreamingAll(init.io, help_text), + .unknown => |arg| { + try stderr.writeStreamingAll(init.io, "mim: unknown argument '"); + try stderr.writeStreamingAll(init.io, arg); + try stderr.writeStreamingAll(init.io, "'\nTry 'mim --help'.\n"); + return 64; + }, + } + + return 0; +} + +test "regular: help text names the binary and smoke boundary" { + try std.testing.expect(std.mem.indexOf(u8, help_text, "mim - mobile-first terminal code editor") != null); + try std.testing.expect(std.mem.indexOf(u8, help_text, "canonical smoke path") != null); +} + +test "regular: version output is stable enough for smoke checks" { + try std.testing.expectEqualStrings("mim 0.1.0-dev\n", versionText()); +} + +test "adversarial: unknown arguments are rejected instead of ignored" { + const argv = [_][]const u8{ "mim", "--definitely-not-supported" }; + const parsed = parseArgs(&argv); + switch (parsed) { + .unknown => |arg| try std.testing.expectEqualStrings("--definitely-not-supported", arg), + else => return error.ExpectedUnknownArgument, + } +} + +test "adversarial: extra positional arguments are not treated as files yet" { + const argv = [_][]const u8{ "mim", "some-file.zig" }; + const parsed = parseArgs(&argv); + switch (parsed) { + .unknown => |arg| try std.testing.expectEqualStrings("some-file.zig", arg), + else => return error.ExpectedUnknownArgument, + } +}