diff --git a/README.md b/README.md index 7f55804..f5f9a56 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,9 @@ venice chat -t calculator,weather "What's 25 * 4.5?" # JSON output for scripting venice chat -f json "List 3 colors" | jq '.content' +# Use piped context plus an instruction +cat error.log | venice chat "find the root cause" + # Disable streaming venice chat --no-stream "Quick question" ``` diff --git a/package-lock.json b/package-lock.json index eb4833f..2b075d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "venice-cli", + "name": "veniceai-cli", "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "venice-cli", + "name": "veniceai-cli", "version": "2.0.0", "license": "MIT", "dependencies": { diff --git a/src/commands/chat.test.ts b/src/commands/chat.test.ts new file mode 100644 index 0000000..e34d924 --- /dev/null +++ b/src/commands/chat.test.ts @@ -0,0 +1,23 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { buildChatUserMessage } from './chat.js'; + +test('buildChatUserMessage uses prompt when only args are provided', () => { + const message = buildChatUserMessage('find the root cause'); + assert.deepEqual(message, { role: 'user', content: 'find the root cause' }); +}); + +test('buildChatUserMessage uses stdin when prompt is empty', () => { + const message = buildChatUserMessage('', 'error line 1\nerror line 2'); + assert.deepEqual(message, { role: 'user', content: 'error line 1\nerror line 2' }); +}); + +test('buildChatUserMessage merges stdin and prompt into one message', () => { + const message = buildChatUserMessage('find the root cause', 'stack trace...'); + assert.deepEqual(message, { role: 'user', content: 'stack trace...\n\nfind the root cause' }); +}); + +test('buildChatUserMessage returns null when both inputs are empty', () => { + const message = buildChatUserMessage('', ''); + assert.equal(message, null); +}); diff --git a/src/commands/chat.ts b/src/commands/chat.ts index 3cad169..a81e401 100644 --- a/src/commands/chat.ts +++ b/src/commands/chat.ts @@ -56,15 +56,16 @@ export function registerChatCommand(program: Command): void { return; } - // Get prompt from args or stdin + // Get prompt from args and optionally stdin let prompt = promptParts.join(' '); - - if (!prompt && !process.stdin.isTTY) { - // Read from stdin - prompt = await readStdin(); + let pipedInput = ''; + + if (!process.stdin.isTTY) { + pipedInput = await readStdin(); } - if (!prompt) { + const userMessage = buildChatUserMessage(prompt, pipedInput); + if (!userMessage) { console.error(formatError('No prompt provided. Usage: venice chat "Your message"')); process.exit(1); } @@ -100,8 +101,8 @@ export function registerChatCommand(program: Command): void { } } - // Add user message - messages.push({ role: 'user', content: prompt }); + // Add user message from stdin/args + messages.push(userMessage); // Get tool definitions const toolNames = options.tools?.split(',').map((t: string) => t.trim()) || []; @@ -418,6 +419,20 @@ async function readStdin(): Promise { return Buffer.concat(chunks).toString('utf-8').trim(); } +export function buildChatUserMessage(prompt: string, pipedInput?: string): Message | null { + if (pipedInput && prompt) { + return { role: 'user', content: `${pipedInput}\n\n${prompt}` }; + } + if (pipedInput) { + return { role: 'user', content: pipedInput }; + } + if (prompt) { + return { role: 'user', content: prompt }; + } + + return null; +} + // Character prompts const CHARACTER_PROMPTS: Record = { pirate: 'You are a pirate captain. Respond in pirate speak with nautical terms, "arr"s, and maritime metaphors. Be adventurous and bold.',