agent terminal: slash commands /help and /clear, tab-completion
intercept any line starting with / before sending it to the agent inbox. two commands today: - /help — render command list locally - /clear — wipe the local terminal view (server-side event history kept, so a page reload restores it) unknown /xxx surfaces an error row instead of being silently sent. tab on a /prefix cycles through matching command names. submit-hint mentions /help so the operator can discover it. scaffolding for the bigger commands (/compact /cancel /model) is in place — adding them later is a switch arm plus harness work.
This commit is contained in:
parent
85e1f1a8f4
commit
8d3df656de
1 changed files with 63 additions and 2 deletions
|
|
@ -155,6 +155,47 @@
|
|||
let lastOutputLen = -1;
|
||||
let pollTimer = null;
|
||||
let termInputRendered = false;
|
||||
// Filled in by the live-event IIFE below. Used by the slash-command
|
||||
// dispatcher to print local-only rows ('help', errors) and to clear
|
||||
// the terminal on `/clear`.
|
||||
let termAPI = null;
|
||||
|
||||
const SLASH_COMMANDS = [
|
||||
{ name: '/help', desc: 'list slash commands' },
|
||||
{ name: '/clear', desc: 'wipe the terminal panel (local-only)' },
|
||||
];
|
||||
|
||||
function handleSlashCommand(line) {
|
||||
if (!termAPI) return false;
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed.startsWith('/')) return false;
|
||||
const [cmd] = trimmed.split(/\s+/);
|
||||
switch (cmd) {
|
||||
case '/help':
|
||||
termAPI.row('note', '· /help');
|
||||
for (const c of SLASH_COMMANDS) {
|
||||
termAPI.row('note', ' ' + c.name.padEnd(10) + ' — ' + c.desc);
|
||||
}
|
||||
return true;
|
||||
case '/clear':
|
||||
termAPI.clear();
|
||||
termAPI.row('note', '· terminal cleared (local view only — server history kept)');
|
||||
return true;
|
||||
default:
|
||||
termAPI.row('turn-end-fail', '✗ unknown slash command: ' + cmd + ' — try /help');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Cycle through commands when operator hits Tab on a `/…` prefix.
|
||||
function completeSlash(prefix) {
|
||||
const matches = SLASH_COMMANDS.filter((c) => c.name.startsWith(prefix));
|
||||
if (!matches.length) return null;
|
||||
// Cycle: when the current prefix already equals a command name,
|
||||
// advance to the next match.
|
||||
const idx = matches.findIndex((c) => c.name === prefix);
|
||||
return matches[(idx + 1) % matches.length].name;
|
||||
}
|
||||
|
||||
function renderTermInput(label, online) {
|
||||
const slot = $('term-input');
|
||||
|
|
@ -178,9 +219,24 @@
|
|||
};
|
||||
ta.addEventListener('input', grow);
|
||||
ta.addEventListener('keydown', (e) => {
|
||||
// Tab-complete slash commands when the buffer starts with `/`.
|
||||
if (e.key === 'Tab' && ta.value.startsWith('/') && !ta.value.includes(' ')) {
|
||||
const next = completeSlash(ta.value);
|
||||
if (next) { e.preventDefault(); ta.value = next; return; }
|
||||
}
|
||||
if (e.key === 'Enter' && !e.shiftKey && !e.isComposing) {
|
||||
e.preventDefault();
|
||||
if (ta.value.trim()) form.requestSubmit();
|
||||
const line = ta.value;
|
||||
if (!line.trim()) return;
|
||||
// Intercept slash commands locally; never send them to the agent.
|
||||
if (line.trim().startsWith('/')) {
|
||||
if (handleSlashCommand(line)) {
|
||||
ta.value = '';
|
||||
grow();
|
||||
return;
|
||||
}
|
||||
}
|
||||
form.requestSubmit();
|
||||
}
|
||||
});
|
||||
// Reset height after async submit clears the value.
|
||||
|
|
@ -188,7 +244,7 @@
|
|||
form.append(
|
||||
el('span', { class: 'prompt' }, 'operator@' + label + ' ▸'),
|
||||
ta,
|
||||
el('span', { class: 'submit-hint' }, '↵ send · ⇧↵ newline'),
|
||||
el('span', { class: 'submit-hint' }, '↵ send · ⇧↵ newline · /help'),
|
||||
);
|
||||
slot.append(form);
|
||||
termInputRendered = true;
|
||||
|
|
@ -270,6 +326,11 @@
|
|||
// Backfill replays mark rows .no-anim so we don't stagger 100 fade-ins
|
||||
// on page load. Set via `currentNoAnim` before the row helpers fire.
|
||||
let currentNoAnim = false;
|
||||
// Expose the panel API for slash commands (`/help`, `/clear`).
|
||||
termAPI = {
|
||||
row: (cls, text) => row(cls, text),
|
||||
clear: () => { log.innerHTML = ''; placeholder = null; },
|
||||
};
|
||||
function row(cls, text) {
|
||||
clearPlaceholder();
|
||||
const e = document.createElement('div');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue