cli: suggest the closest flag or command on a typo

An unknown --flag now logs the nearest accepted spelling within two
edits as did_you_mean, painted green next to the red typo in the pretty
log format; logfmt stays plain. A bare first argument within two edits
of a command name is rejected with the same hint instead of being
fetched as a url.

The Levenshtein helper moves from SlashCommand into string.zig so the
agent REPL and the CLI share it, with the table widened to fit the
longest flag name.
This commit is contained in:
Adrià Arrufat committed 2026-09-08 16:50:46 +02:00
1 parent 5d732eb8f1
commit 88d133a676
4 files changed
+177 -30

No files matched your search

+39
View File
@@ -346,6 +346,30 @@ pub const KV = struct {
}
};
/// A string the pretty format paints; logfmt writes it plainly.
pub const Colored = struct {
code: []const u8,
text: []const u8,
pub fn logFmt(self: Colored, key: []const u8, writer: LogFormatWriter) !void {
return writer.write(key, self.text);
}
pub fn format(self: Colored, writer: *std.Io.Writer) !void {
try writer.writeAll(self.code);
try writer.writeAll(self.text);
return writer.writeAll("\x1b[0m");
}
};
pub fn red(text: []const u8) Colored {
return .{ .code = "\x1b[0;31m", .text = text };
}
pub fn green(text: []const u8) Colored {
return .{ .code = "\x1b[0;32m", .text = text };
}
const Value = union(enum) {
null,
string: []const u8,
@@ -577,6 +601,21 @@ fn timestamp(comptime clock: std.Io.Clock) u64 {
}
const testing = @import("testing.zig");
test "log: colored" {
opts.format = .logfmt;
defer opts.format = .pretty;
var aw = std.Io.Writer.Allocating.init(testing.allocator);
defer aw.deinit();
try logTo(.app, .err, "test", .{ .arg = red("--wait mss") }, &aw.writer);
try testing.expectEqual("$time=1739795092929 $scope=app $level=error $msg=\"test\" arg=\"--wait mss\"\n", aw.written());
aw.clearRetainingCapacity();
try writeValue(.pretty, green("--wait-ms"), &aw.writer);
try testing.expectEqual("\x1b[0;32m--wait-ms\x1b[0m", aw.written());
}
test "log: data" {
opts.format = .logfmt;
defer opts.format = .pretty;