mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
repl: categorize help and centralize LLM checks
- Group `/help` output into Browser, LLM, and Meta commands. - Only show LLM commands in help if an LLM client is configured. - Centralize the check for commands requiring an LLM in the REPL loop. - Add descriptions to meta and LLM commands.
This commit is contained in:
@@ -478,15 +478,16 @@ fn runRepl(self: *Agent) void {
|
||||
},
|
||||
};
|
||||
|
||||
if (cmd.needsLlm() and self.ai_client == null) {
|
||||
self.terminal.printErrorFmt("/{s} requires an LLM. Drop --no-llm and set an API key (ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY).", .{@tagName(std.meta.activeTag(cmd))});
|
||||
continue :repl;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
.comment => continue :repl,
|
||||
.login, .acceptCookies => {
|
||||
if (self.ai_client == null) {
|
||||
self.terminal.printError("/login and /acceptCookies require an LLM. Drop --no-llm and set an API key (ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY).");
|
||||
continue :repl;
|
||||
}
|
||||
const prompt = if (cmd == .login) login_prompt else accept_cookies_prompt;
|
||||
const label: []const u8 = if (cmd == .login) "/login" else "/acceptCookies";
|
||||
const prompt = if (cmd == .login) login_prompt else accept_cookies_prompt;
|
||||
_ = self.runTurn(.{ .prompt = prompt, .record_comment = line, .label = label });
|
||||
},
|
||||
.tool_call => |tc| {
|
||||
@@ -527,21 +528,33 @@ fn handleVerbosity(self: *Agent, rest: []const u8) void {
|
||||
self.terminal.printInfoFmt("verbosity: {s}", .{@tagName(level)});
|
||||
}
|
||||
|
||||
fn helpLessThan(_: void, a: SlashCommand.Help, b: SlashCommand.Help) bool {
|
||||
return std.mem.lessThan(u8, a.name, b.name);
|
||||
}
|
||||
|
||||
fn printHelpSection(term: *Terminal, header: []const u8, rows: []SlashCommand.Help) void {
|
||||
if (rows.len == 0) return;
|
||||
std.sort.pdq(SlashCommand.Help, rows, {}, helpLessThan);
|
||||
term.printInfo(header);
|
||||
for (rows) |r| term.printInfoFmt(" /{s} — {s}", .{ r.name, r.description });
|
||||
}
|
||||
|
||||
fn printSlashHelp(self: *Agent, arena: std.mem.Allocator, target: []const u8) void {
|
||||
if (target.len == 0) {
|
||||
self.terminal.printInfo("Slash commands (no LLM, REPL only):");
|
||||
const all = Schema.all();
|
||||
const sorted = arena.alloc(*const Schema, all.len) catch {
|
||||
for (all) |s| self.terminal.printInfoFmt(" /{s} — {s}", .{ s.tool_name, firstSentence(s.description) });
|
||||
self.terminal.printInfo("Meta: /help [name], /quit, /verbosity <low|medium|high>");
|
||||
return;
|
||||
};
|
||||
for (sorted, all) |*p, *s| p.* = s;
|
||||
std.sort.pdq(*const Schema, sorted, {}, Schema.lessByName);
|
||||
for (sorted) |s| {
|
||||
self.terminal.printInfoFmt(" /{s} — {s}", .{ s.tool_name, firstSentence(s.description) });
|
||||
const browser = arena.alloc(SlashCommand.Help, all.len) catch return;
|
||||
for (all, browser) |*s, *e| e.* = .{ .name = s.tool_name, .description = firstSentence(s.description) };
|
||||
printHelpSection(&self.terminal, "Browser commands:", browser);
|
||||
|
||||
if (self.ai_client != null) {
|
||||
const llm = arena.alloc(SlashCommand.Help, SlashCommand.llm_commands.len) catch return;
|
||||
@memcpy(llm, &SlashCommand.llm_commands);
|
||||
printHelpSection(&self.terminal, "\nLLM commands:", llm);
|
||||
}
|
||||
self.terminal.printInfo("Meta: /help [name], /quit, /verbosity <low|medium|high>");
|
||||
|
||||
const meta = arena.alloc(SlashCommand.Help, SlashCommand.meta_commands.len) catch return;
|
||||
for (SlashCommand.meta_commands, meta) |m, *e| e.* = .{ .name = m.name, .description = m.description };
|
||||
printHelpSection(&self.terminal, "\nMeta commands:", meta);
|
||||
return;
|
||||
}
|
||||
const lookup = if (target[0] == '/') target[1..] else target;
|
||||
|
||||
@@ -23,6 +23,13 @@
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
/// Shared row format for the `/help` listing — `name` is the slash name
|
||||
/// (no `/`), `description` is a single-sentence summary.
|
||||
pub const Help = struct {
|
||||
name: []const u8,
|
||||
description: []const u8,
|
||||
};
|
||||
|
||||
pub const MetaCommand = struct {
|
||||
tag: Tag,
|
||||
name: [:0]const u8,
|
||||
@@ -31,6 +38,9 @@ pub const MetaCommand = struct {
|
||||
hint: []const u8,
|
||||
/// Tab-completion candidates for the first positional arg.
|
||||
values: []const [:0]const u8,
|
||||
/// First-sentence summary for `/help`; longer detail is rendered by
|
||||
/// `Agent.printSlashHelp` for the per-command lookup.
|
||||
description: []const u8,
|
||||
|
||||
/// Dispatched by `Agent.handleMeta` via an exhaustive switch so adding
|
||||
/// a new meta command is a compile error until it's wired up there too.
|
||||
@@ -38,9 +48,17 @@ pub const MetaCommand = struct {
|
||||
};
|
||||
|
||||
pub const meta_commands = [_]MetaCommand{
|
||||
.{ .tag = .help, .name = "help", .hint = "", .values = &.{} },
|
||||
.{ .tag = .quit, .name = "quit", .hint = "", .values = &.{} },
|
||||
.{ .tag = .verbosity, .name = "verbosity", .hint = "<low|medium|high>", .values = &.{ "low", "medium", "high" } },
|
||||
.{ .tag = .help, .name = "help", .hint = "", .values = &.{}, .description = "Show help for a slash command, or list all when no name is given" },
|
||||
.{ .tag = .quit, .name = "quit", .hint = "", .values = &.{}, .description = "Exit the REPL" },
|
||||
.{ .tag = .verbosity, .name = "verbosity", .hint = "<low|medium|high>", .values = &.{ "low", "medium", "high" }, .description = "Set REPL agent verbosity; bare /verbosity prints the current level" },
|
||||
};
|
||||
|
||||
/// LLM-driven slash commands. Parsed via `script.Command.parse` (they're
|
||||
/// variants of the `Command` union) — listed here only so the help
|
||||
/// renderer and completer have a single source of names + descriptions.
|
||||
pub const llm_commands = [_]Help{
|
||||
.{ .name = "login", .description = "Log in to the current site using $LP_* env-var credentials" },
|
||||
.{ .name = "acceptCookies", .description = "Find and dismiss the cookie consent banner" },
|
||||
};
|
||||
|
||||
pub fn findMeta(name: []const u8) ?*const MetaCommand {
|
||||
|
||||
Reference in New Issue
Block a user