agent: rename exit to quit and remove command aliases

This commit is contained in:
Adrià Arrufat
2026-04-30 16:06:05 +02:00
parent 44311c20aa
commit ec8d53d684
4 changed files with 24 additions and 25 deletions

View File

@@ -291,7 +291,7 @@ fn runRepl(self: *Self) void {
}
switch (cmd) {
.exit => break :repl,
.quit => break :repl,
.comment => continue :repl,
.login => self.processUserMessage(login_prompt, line) catch |err| {
self.terminal.printErrorFmt("LOGIN failed: {s}", .{@errorName(err)});
@@ -313,7 +313,7 @@ fn runRepl(self: *Self) void {
}
/// Handle a REPL line that started with `/`. Returns `true` if the user asked
/// to exit (`/quit` or `/exit`), `false` otherwise. All errors are printed and
/// to quit (`/quit`), `false` otherwise. All errors are printed and
/// swallowed — the REPL must not die from a malformed slash command.
fn handleSlash(self: *Self, body: []const u8) bool {
const split = SlashCommand.splitNameRest(body) orelse {
@@ -323,7 +323,7 @@ fn handleSlash(self: *Self, body: []const u8) bool {
const name = split.name;
const rest = split.rest;
if (std.mem.eql(u8, name, "quit") or std.mem.eql(u8, name, "exit")) {
if (std.mem.eql(u8, name, "quit")) {
return true;
}
if (std.mem.eql(u8, name, "help")) {
@@ -376,7 +376,7 @@ fn printSlashHelp(self: *Self, target: []const u8) void {
const summary = firstSentence(s.description);
self.terminal.printInfoFmt(" /{s} — {s}", .{ s.tool_name, summary });
}
self.terminal.printInfo("Meta: /help <name>, /quit, /exit");
self.terminal.printInfo("Meta: /help <name>, /quit");
return;
}
const lookup = if (target[0] == '/') target[1..] else target;
@@ -452,8 +452,8 @@ fn runScript(self: *Self, path: []const u8) bool {
while (iter.next()) |entry| {
switch (entry.command) {
.exit => {
self.terminal.printInfo("EXIT — stopping script.");
.quit => {
self.terminal.printInfo("QUIT — stopping script.");
break;
},
.comment => {

View File

@@ -36,13 +36,13 @@ pub const Command = union(enum) {
eval_js: []const u8,
login: void,
accept_cookies: void,
exit: void,
quit: void,
comment: void,
natural_language: []const u8,
pub fn isRecorded(self: Command) bool {
return switch (self) {
.tree, .markdown, .comment, .exit => false,
.tree, .markdown, .comment, .quit => false,
.goto, .click, .type_cmd, .wait, .scroll, .hover, .select, .check, .extract, .eval_js, .login, .accept_cookies => true,
.natural_language => |text| text.len > 0,
};
@@ -100,7 +100,7 @@ pub const Command = union(enum) {
try writer.print("EVAL {f}", .{quote(script)}),
.login => try writer.writeAll("LOGIN"),
.accept_cookies => try writer.writeAll("ACCEPT_COOKIES"),
.exit => try writer.writeAll("EXIT"),
.quit => try writer.writeAll("QUIT"),
.comment => try writer.writeAll("#"),
.natural_language => |text| try writer.writeAll(text),
}
@@ -193,7 +193,7 @@ pub fn parse(line: []const u8) Command {
return .{ .tree = {} };
}
if (std.ascii.eqlIgnoreCase(cmd_word, "MARKDOWN") or std.ascii.eqlIgnoreCase(cmd_word, "MD")) {
if (std.ascii.eqlIgnoreCase(cmd_word, "MARKDOWN")) {
return .{ .markdown = {} };
}
@@ -211,12 +211,12 @@ pub fn parse(line: []const u8) Command {
return .{ .login = {} };
}
if (std.ascii.eqlIgnoreCase(cmd_word, "ACCEPT_COOKIES") or std.ascii.eqlIgnoreCase(cmd_word, "ACCEPT-COOKIES")) {
if (std.ascii.eqlIgnoreCase(cmd_word, "ACCEPT_COOKIES")) {
return .{ .accept_cookies = {} };
}
if (std.ascii.eqlIgnoreCase(cmd_word, "EXIT") or std.ascii.eqlIgnoreCase(cmd_word, "QUIT")) {
return .{ .exit = {} };
if (std.ascii.eqlIgnoreCase(cmd_word, "QUIT")) {
return .{ .quit = {} };
}
return .{ .natural_language = trimmed };
@@ -417,7 +417,7 @@ pub fn noSubstitute(_: std.mem.Allocator, input: []const u8) []const u8 {
/// Map a Command to its (tool_name, JSON args) representation. Returns
/// null for variants without a 1:1 tool mapping (login, accept_cookies,
/// natural_language, comment, exit, extract — extract is rendered as a
/// natural_language, comment, quit, extract — extract is rendered as a
/// custom `eval` script by the caller).
///
/// `substitute` is applied to selector-like fields. The `value` field of
@@ -447,7 +447,7 @@ pub fn toToolCall(arena: std.mem.Allocator, cmd: Command, substitute: Substitute
.tree => .{ .name = @tagName(Action.tree), .args_json = "" },
.markdown => .{ .name = @tagName(Action.markdown), .args_json = "" },
.eval_js => |script| .{ .name = @tagName(Action.eval), .args_json = buildJson(arena, .{ .script = script }) },
.extract, .exit, .natural_language, .comment, .login, .accept_cookies => null,
.extract, .quit, .natural_language, .comment, .login, .accept_cookies => null,
};
}
@@ -684,9 +684,9 @@ test "parse TREE" {
try std.testing.expect(cmd == .tree);
}
test "parse MARKDOWN alias MD" {
test "parse MARKDOWN" {
try std.testing.expect(parse("MARKDOWN") == .markdown);
try std.testing.expect(parse("md") == .markdown);
try std.testing.expect(parse("markdown") == .markdown);
}
test "parse EXTRACT" {
@@ -706,11 +706,12 @@ test "parse LOGIN" {
test "parse ACCEPT_COOKIES" {
try std.testing.expect(parse("ACCEPT_COOKIES") == .accept_cookies);
try std.testing.expect(parse("ACCEPT-COOKIES") == .accept_cookies);
try std.testing.expect(parse("accept_cookies") == .accept_cookies);
}
test "parse EXIT" {
try std.testing.expect(parse("EXIT") == .exit);
test "parse QUIT" {
try std.testing.expect(parse("QUIT") == .quit);
try std.testing.expect(parse("quit") == .quit);
}
test "parse comment" {
@@ -747,7 +748,6 @@ test "isRecorded" {
try std.testing.expect(parse("EVAL \"1+1\"").isRecorded());
try std.testing.expect(!parse("TREE").isRecorded());
try std.testing.expect(!parse("MARKDOWN").isRecorded());
try std.testing.expect(!parse("md").isRecorded());
}
test "ScriptIterator basic commands" {
@@ -1040,7 +1040,7 @@ test "toToolCall: variants without tool mapping return null" {
try std.testing.expect(toToolCall(a, .{ .extract = ".x" }, noSubstitute) == null);
try std.testing.expect(toToolCall(a, .login, noSubstitute) == null);
try std.testing.expect(toToolCall(a, .accept_cookies, noSubstitute) == null);
try std.testing.expect(toToolCall(a, .exit, noSubstitute) == null);
try std.testing.expect(toToolCall(a, .quit, noSubstitute) == null);
try std.testing.expect(toToolCall(a, .comment, noSubstitute) == null);
try std.testing.expect(toToolCall(a, .{ .natural_language = "hi" }, noSubstitute) == null);
}

View File

@@ -28,7 +28,7 @@ pub fn executeWithResult(self: *Self, a: std.mem.Allocator, cmd: Command.Command
if (cmd == .extract) return self.execExtract(a, cmd.extract);
const tc = Command.toToolCall(a, cmd, browser_tools.substituteEnvVars) orelse switch (cmd) {
.exit, .natural_language, .comment, .login, .accept_cookies => unreachable,
.quit, .natural_language, .comment, .login, .accept_cookies => unreachable,
else => return .{ .output = "command has no tool mapping", .failed = true },
};
return self.callTool(a, tc.name, tc.args_json);

View File

@@ -30,12 +30,11 @@ const commands = [_]CommandInfo{
.{ .name = "CHECK", .hint = " '<selector>' [true|false]" },
.{ .name = "TREE", .hint = "" },
.{ .name = "MARKDOWN", .hint = "" },
.{ .name = "MD", .hint = "" },
.{ .name = "EXTRACT", .hint = " '<selector>'" },
.{ .name = "EVAL", .hint = " '<script>'" },
.{ .name = "LOGIN", .hint = "" },
.{ .name = "ACCEPT_COOKIES", .hint = "" },
.{ .name = "EXIT", .hint = "" },
.{ .name = "QUIT", .hint = "" },
};
pub fn init(history_path: ?[:0]const u8) Self {