From 9ff2e702fe1cd33fb22387850ababb9035f27b75 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 11 Mar 2026 12:04:26 +0000 Subject: [PATCH 1/3] Support chat stdin plus prompt arguments Co-authored-by: Joshua Mo --- README.md | 3 +++ package-lock.json | 4 ++-- src/commands/chat.test.ts | 26 ++++++++++++++++++++++++++ src/commands/chat.ts | 30 ++++++++++++++++++++++-------- 4 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 src/commands/chat.test.ts 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..5187eaf --- /dev/null +++ b/src/commands/chat.test.ts @@ -0,0 +1,26 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { buildChatUserMessages } from './chat.js'; + +test('buildChatUserMessages uses prompt when only args are provided', () => { + const messages = buildChatUserMessages('find the root cause'); + assert.deepEqual(messages, [{ role: 'user', content: 'find the root cause' }]); +}); + +test('buildChatUserMessages uses stdin when prompt is empty', () => { + const messages = buildChatUserMessages('', 'error line 1\nerror line 2'); + assert.deepEqual(messages, [{ role: 'user', content: 'error line 1\nerror line 2' }]); +}); + +test('buildChatUserMessages merges stdin and prompt in order', () => { + const messages = buildChatUserMessages('find the root cause', 'stack trace...'); + assert.deepEqual(messages, [ + { role: 'user', content: 'stack trace...' }, + { role: 'user', content: 'find the root cause' }, + ]); +}); + +test('buildChatUserMessages returns empty array when both inputs are empty', () => { + const messages = buildChatUserMessages('', ''); + assert.deepEqual(messages, []); +}); diff --git a/src/commands/chat.ts b/src/commands/chat.ts index 3cad169..68c7f8a 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 userMessages = buildChatUserMessages(prompt, pipedInput); + if (userMessages.length === 0) { 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(s) from stdin/args + messages.push(...userMessages); // Get tool definitions const toolNames = options.tools?.split(',').map((t: string) => t.trim()) || []; @@ -418,6 +419,19 @@ async function readStdin(): Promise { return Buffer.concat(chunks).toString('utf-8').trim(); } +export function buildChatUserMessages(prompt: string, pipedInput?: string): Message[] { + const userMessages: Message[] = []; + + if (pipedInput) { + userMessages.push({ role: 'user', content: pipedInput }); + } + if (prompt) { + userMessages.push({ role: 'user', content: prompt }); + } + + return userMessages; +} + // 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.', From 575b42b748fb998f2114b16efb6ce3922183c1a1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 11 Mar 2026 12:11:02 +0000 Subject: [PATCH 2/3] Combine piped chat input into single user message Co-authored-by: Joshua Mo --- src/commands/chat.test.ts | 7 ++----- src/commands/chat.ts | 11 ++++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/commands/chat.test.ts b/src/commands/chat.test.ts index 5187eaf..c1ff56c 100644 --- a/src/commands/chat.test.ts +++ b/src/commands/chat.test.ts @@ -12,12 +12,9 @@ test('buildChatUserMessages uses stdin when prompt is empty', () => { assert.deepEqual(messages, [{ role: 'user', content: 'error line 1\nerror line 2' }]); }); -test('buildChatUserMessages merges stdin and prompt in order', () => { +test('buildChatUserMessages merges stdin and prompt into one message', () => { const messages = buildChatUserMessages('find the root cause', 'stack trace...'); - assert.deepEqual(messages, [ - { role: 'user', content: 'stack trace...' }, - { role: 'user', content: 'find the root cause' }, - ]); + assert.deepEqual(messages, [{ role: 'user', content: 'stack trace...\n\nfind the root cause' }]); }); test('buildChatUserMessages returns empty array when both inputs are empty', () => { diff --git a/src/commands/chat.ts b/src/commands/chat.ts index 68c7f8a..f6f935f 100644 --- a/src/commands/chat.ts +++ b/src/commands/chat.ts @@ -420,16 +420,17 @@ async function readStdin(): Promise { } export function buildChatUserMessages(prompt: string, pipedInput?: string): Message[] { - const userMessages: Message[] = []; - + if (pipedInput && prompt) { + return [{ role: 'user', content: `${pipedInput}\n\n${prompt}` }]; + } if (pipedInput) { - userMessages.push({ role: 'user', content: pipedInput }); + return [{ role: 'user', content: pipedInput }]; } if (prompt) { - userMessages.push({ role: 'user', content: prompt }); + return [{ role: 'user', content: prompt }]; } - return userMessages; + return []; } // Character prompts From d3270e0aebd0089d4ddb10846a68e0ba829ff776 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 11 Mar 2026 12:12:41 +0000 Subject: [PATCH 3/3] Return single chat user message or null Co-authored-by: Joshua Mo --- src/commands/chat.test.ts | 26 +++++++++++++------------- src/commands/chat.ts | 18 +++++++++--------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/commands/chat.test.ts b/src/commands/chat.test.ts index c1ff56c..e34d924 100644 --- a/src/commands/chat.test.ts +++ b/src/commands/chat.test.ts @@ -1,23 +1,23 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { buildChatUserMessages } from './chat.js'; +import { buildChatUserMessage } from './chat.js'; -test('buildChatUserMessages uses prompt when only args are provided', () => { - const messages = buildChatUserMessages('find the root cause'); - assert.deepEqual(messages, [{ role: 'user', content: 'find the root cause' }]); +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('buildChatUserMessages uses stdin when prompt is empty', () => { - const messages = buildChatUserMessages('', 'error line 1\nerror line 2'); - assert.deepEqual(messages, [{ role: 'user', content: 'error line 1\nerror line 2' }]); +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('buildChatUserMessages merges stdin and prompt into one message', () => { - const messages = buildChatUserMessages('find the root cause', 'stack trace...'); - assert.deepEqual(messages, [{ role: 'user', content: 'stack trace...\n\nfind the root cause' }]); +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('buildChatUserMessages returns empty array when both inputs are empty', () => { - const messages = buildChatUserMessages('', ''); - assert.deepEqual(messages, []); +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 f6f935f..a81e401 100644 --- a/src/commands/chat.ts +++ b/src/commands/chat.ts @@ -64,8 +64,8 @@ export function registerChatCommand(program: Command): void { pipedInput = await readStdin(); } - const userMessages = buildChatUserMessages(prompt, pipedInput); - if (userMessages.length === 0) { + const userMessage = buildChatUserMessage(prompt, pipedInput); + if (!userMessage) { console.error(formatError('No prompt provided. Usage: venice chat "Your message"')); process.exit(1); } @@ -101,8 +101,8 @@ export function registerChatCommand(program: Command): void { } } - // Add user message(s) from stdin/args - messages.push(...userMessages); + // Add user message from stdin/args + messages.push(userMessage); // Get tool definitions const toolNames = options.tools?.split(',').map((t: string) => t.trim()) || []; @@ -419,18 +419,18 @@ async function readStdin(): Promise { return Buffer.concat(chunks).toString('utf-8').trim(); } -export function buildChatUserMessages(prompt: string, pipedInput?: string): Message[] { +export function buildChatUserMessage(prompt: string, pipedInput?: string): Message | null { if (pipedInput && prompt) { - return [{ role: 'user', content: `${pipedInput}\n\n${prompt}` }]; + return { role: 'user', content: `${pipedInput}\n\n${prompt}` }; } if (pipedInput) { - return [{ role: 'user', content: pipedInput }]; + return { role: 'user', content: pipedInput }; } if (prompt) { - return [{ role: 'user', content: prompt }]; + return { role: 'user', content: prompt }; } - return []; + return null; } // Character prompts