prior-cli 1.3.11 → 1.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/prior.js CHANGED
@@ -608,7 +608,7 @@ async function startChat(opts = {}) {
608
608
  const chatHistory = [];
609
609
  let currentModel = opts.model || null;
610
610
  let _currentAbortController = null;
611
- let _pendingImageBase64 = null; // set by alt+v clipboard paste
611
+ let _pendingImages = []; // set by alt+v clipboard paste (supports multiple)
612
612
 
613
613
  // ── Live slash-command suggestions ──────────────────────────
614
614
  let clearSuggestions = () => {};
@@ -652,8 +652,12 @@ async function startChat(opts = {}) {
652
652
  let rows = 0;
653
653
 
654
654
  // Image indicator — always first, persists across backspace/typing
655
- if (_pendingImageBase64) {
656
- process.stdout.write(`\x1b[B\r\x1b[2K ${c.brand('◈')} ${c.dim('[Image] attached · alt+v to remove')}`);
655
+ if (_pendingImages.length > 0) {
656
+ const tags = _pendingImages.map((_, i) => c.brand(`[Image ${i + 1}]`)).join(' ');
657
+ const hint = _pendingImages.length > 0
658
+ ? c.dim(' · alt+v to add more · alt+v (empty clipboard) to remove last')
659
+ : '';
660
+ process.stdout.write(`\x1b[B\r\x1b[2K ${c.brand('◈')} ${tags}${hint}`);
657
661
  rows++;
658
662
  }
659
663
 
@@ -690,24 +694,22 @@ async function startChat(opts = {}) {
690
694
  return;
691
695
  }
692
696
 
693
- // Alt+V — grab image from clipboard, or remove if already attached
697
+ // Alt+V — add image from clipboard, or remove last if clipboard is empty
694
698
  if (key.meta && key.name === 'v') {
695
- if (_pendingImageBase64) {
696
- _pendingImageBase64 = null;
697
- renderSubRows(rl.line || '');
698
- } else {
699
- try {
700
- const ps = `Add-Type -AssemblyName System.Windows.Forms; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img) { $ms = New-Object System.IO.MemoryStream; $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); [Convert]::ToBase64String($ms.ToArray()) } else { '' }`;
701
- const b64 = execSync(`powershell -NoProfile -Command "${ps}"`, { timeout: 5000 }).toString().trim();
702
- if (b64) {
703
- _pendingImageBase64 = b64;
704
- renderSubRows(rl.line || '');
705
- } else {
706
- flashSubRow(c.muted('✗ No image found in clipboard'));
707
- }
708
- } catch {
709
- flashSubRow(c.muted('✗ Could not read clipboard'));
699
+ try {
700
+ const ps = `Add-Type -AssemblyName System.Windows.Forms; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img) { $ms = New-Object System.IO.MemoryStream; $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); [Convert]::ToBase64String($ms.ToArray()) } else { '' }`;
701
+ const b64 = execSync(`powershell -NoProfile -Command "${ps}"`, { timeout: 5000 }).toString().trim();
702
+ if (b64) {
703
+ _pendingImages.push(b64);
704
+ renderSubRows(rl.line || '');
705
+ } else if (_pendingImages.length > 0) {
706
+ _pendingImages.pop();
707
+ renderSubRows(rl.line || '');
708
+ } else {
709
+ flashSubRow(c.muted('✗ No image found in clipboard'));
710
710
  }
711
+ } catch {
712
+ flashSubRow(c.muted('✗ Could not read clipboard'));
711
713
  }
712
714
  return;
713
715
  }
@@ -985,11 +987,12 @@ Keep it under 350 words. Write prior.md now.`;
985
987
  console.log('');
986
988
 
987
989
  {
988
- const imageForThisMsg = _pendingImageBase64;
989
- _pendingImageBase64 = null;
990
+ const imagesForThisMsg = [..._pendingImages];
991
+ _pendingImages = [];
990
992
 
991
- if (imageForThisMsg) {
992
- console.log(c.brand('') + c.dim(' image attached'));
993
+ if (imagesForThisMsg.length > 0) {
994
+ const label = imagesForThisMsg.length === 1 ? '1 image' : `${imagesForThisMsg.length} images`;
995
+ console.log(c.brand(' ◈') + c.dim(` ${label} attached`));
993
996
  }
994
997
 
995
998
  let responseText = '';
@@ -1017,7 +1020,7 @@ Keep it under 350 words. Write prior.md now.`;
1017
1020
  model: currentModel,
1018
1021
  cwd: process.cwd(),
1019
1022
  projectContext,
1020
- imageBase64: imageForThisMsg,
1023
+ images: imagesForThisMsg,
1021
1024
  confirm,
1022
1025
  signal: _currentAbortController.signal,
1023
1026
  send: ev => {
package/lib/agent.js CHANGED
@@ -10,11 +10,11 @@ const MAX_ITER = 14;
10
10
 
11
11
  // ── Single inference call (server just runs Ollama + returns) ─
12
12
 
13
- async function infer(messages, model, token, { cwd, uncensored, projectContext, imageBase64 } = {}, signal) {
13
+ async function infer(messages, model, token, { cwd, uncensored, projectContext, images } = {}, signal) {
14
14
  const res = await fetch(`${CLI_BASE}/api/infer`, {
15
15
  method: 'POST',
16
16
  headers: { 'Content-Type': 'application/json' },
17
- body: JSON.stringify({ messages, model, token, cwd, uncensored, projectContext, imageBase64 }),
17
+ body: JSON.stringify({ messages, model, token, cwd, uncensored, projectContext, images }),
18
18
  timeout: 120000,
19
19
  signal,
20
20
  });
@@ -200,13 +200,13 @@ function stripToolTags(text) {
200
200
 
201
201
  const CONFIRM_TOOLS = new Set(['run_command', 'file_delete', 'file_write']);
202
202
 
203
- async function runAgent({ messages, model, uncensored, cwd, projectContext, imageBase64, send, confirm, signal }) {
203
+ async function runAgent({ messages, model, uncensored, cwd, projectContext, images, send, confirm, signal }) {
204
204
  const token = getToken();
205
205
  const history = [...messages];
206
206
 
207
207
  let totalPromptTokens = 0;
208
208
  let totalCompletionTokens = 0;
209
- let pendingImage = imageBase64 || null; // only sent on first iteration
209
+ let pendingImages = (images && images.length) ? images : null; // only sent on first iteration
210
210
 
211
211
  for (let iter = 0; iter < MAX_ITER; iter++) {
212
212
 
@@ -219,10 +219,10 @@ async function runAgent({ messages, model, uncensored, cwd, projectContext, imag
219
219
  send({ type: 'thinking' });
220
220
 
221
221
  let result;
222
- const iterImage = pendingImage;
223
- pendingImage = null; // clear after first use
222
+ const iterImages = pendingImages;
223
+ pendingImages = null; // clear after first use
224
224
  try {
225
- result = await infer(history, model || 'qwen3.5:4b', token, { cwd, uncensored, projectContext, imageBase64: iterImage }, signal);
225
+ result = await infer(history, model || 'qwen3.5:4b', token, { cwd, uncensored, projectContext, images: iterImages }, signal);
226
226
  } catch (err) {
227
227
  await trackTokenUsage(token, totalPromptTokens, totalCompletionTokens);
228
228
  if (err.name === 'AbortError' || signal?.aborted) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prior-cli",
3
- "version": "1.3.11",
3
+ "version": "1.3.12",
4
4
  "description": "Prior Network AI — command-line interface",
5
5
  "bin": {
6
6
  "prior": "bin/prior.js"