This commit is contained in:
seb
2026-09-22 20:11:55 +02:00
parent b15ddfadc3
commit 96d2b80b6c
4 changed files with 223 additions and 6 deletions

View File

@@ -22,7 +22,7 @@
import path from 'path';
import chalk from 'chalk';
import { input } from '@inquirer/prompts';
import { editor } from '@inquirer/prompts';
import { LLMOrbiter, OpenRouterProvider } from 'llmorbiter';
@@ -30,6 +30,8 @@ import { API_KEY, MODEL, UNDO_FILE, ROOT_DIR } from './config.js';
import { listDir } from './dir-listing.js';
import { loadUndoLog, getUndoLog } from './undo.js';
import { tools } from './tools/index.js';
import { autocompleteInput } from './autocomplete-input.js';
import { COMMANDS, findCommand } from './slash-commands.js';
if (!API_KEY) {
console.error(chalk.red.bold('\n✖ Missing OPENROUTER_API_KEY — add it to .env\n'));
@@ -135,6 +137,36 @@ function printUsage(u) {
console.log(chalk.dim(` 📊 ${parts.join(' ')}`));
}
function printCommandHelp() {
const width = Math.max(...COMMANDS.map((c) => c.name.length));
console.log('');
console.log(chalk.green.bold(' 📖 Commands'));
printDivider();
for (const c of COMMANDS) {
console.log(' ' + chalk.cyan(c.name.padEnd(width)) + ' ' + chalk.dim(c.description));
}
console.log('');
}
// ─── Multiline input (editor prompt) ─────────────────────────────────────────────
/**
* Open the user's $VISUAL/$EDITOR (falls back to notepad on Windows, vim
* elsewhere) to compose or paste multiline content. Launches immediately —
* no extra keypress — and returns the saved buffer as a string.
*/
async function promptMultiline(message = 'Compose or paste your text, then save & close the editor') {
return editor({
message: chalk.bold.cyan(message),
waitForUserInput: false,
postfix: '.md',
theme: {
prefix: chalk.cyan('›'),
style: { message: chalk.bold.cyan },
},
});
}
// ─── Main loop ─────────────────────────────────────────────────────────────────
async function main() {
@@ -155,14 +187,13 @@ async function main() {
const systemPrompt = buildSystemPrompt();
let messages = [{ role: 'system', content: systemPrompt }];
console.log(chalk.dim(' Type your request below. Press Ctrl+C to exit.\n'));
console.log(chalk.dim(' Type your request below. Type / for commands, /paste for multiline. Press Ctrl+C to exit.\n'));
while (true) {
let userInput;
try {
userInput = await input({
userInput = await autocompleteInput({
message: chalk.bold.cyan('You'),
prefix: chalk.cyan('›'),
theme: {
prefix: chalk.cyan('›'),
style: {
@@ -180,11 +211,38 @@ async function main() {
if (!userInput.trim()) continue;
// Handle special commands
if (userInput.trim() === '/quit' || userInput.trim() === '/exit') {
const command = findCommand(userInput.trim());
if (userInput.trim().startsWith('/') && command === null) {
const name = userInput.trim().split(/\s+/)[0];
console.log(chalk.yellow(`\n Unknown command: ${name}`));
printCommandHelp();
continue;
}
if (command === '/quit') {
console.log(chalk.dim('\n Goodbye! 🐕\n'));
break;
}
if (userInput.trim() === '/undo') {
if (command === '/help') {
printCommandHelp();
continue;
}
if (command === '/paste') {
try {
const pasted = await promptMultiline();
if (!pasted || !pasted.trim()) {
console.log(chalk.yellow('\n Nothing pasted.\n'));
continue;
}
userInput = pasted;
printStatus(`Pasted ${pasted.split('\n').length} line(s), sending…`);
} catch {
console.log(chalk.yellow('\n Paste cancelled.\n'));
continue;
}
}
if (command === '/undo') {
const undoLog = getUndoLog();
if (undoLog.length === 0) {
console.log(chalk.yellow('\n Nothing to undo.\n'));