prior-cli 1.3.10 → 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 +19 -11
- package/lib/agent.js +7 -7
- package/package.json +1 -1
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
|
|
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 (
|
|
656
|
-
|
|
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,14 +694,17 @@ async function startChat(opts = {}) {
|
|
|
690
694
|
return;
|
|
691
695
|
}
|
|
692
696
|
|
|
693
|
-
// Alt+V —
|
|
697
|
+
// Alt+V — add image from clipboard, or remove last if clipboard is empty
|
|
694
698
|
if (key.meta && key.name === 'v') {
|
|
695
699
|
try {
|
|
696
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 { '' }`;
|
|
697
701
|
const b64 = execSync(`powershell -NoProfile -Command "${ps}"`, { timeout: 5000 }).toString().trim();
|
|
698
702
|
if (b64) {
|
|
699
|
-
|
|
700
|
-
renderSubRows(rl.line || '');
|
|
703
|
+
_pendingImages.push(b64);
|
|
704
|
+
renderSubRows(rl.line || '');
|
|
705
|
+
} else if (_pendingImages.length > 0) {
|
|
706
|
+
_pendingImages.pop();
|
|
707
|
+
renderSubRows(rl.line || '');
|
|
701
708
|
} else {
|
|
702
709
|
flashSubRow(c.muted('✗ No image found in clipboard'));
|
|
703
710
|
}
|
|
@@ -980,11 +987,12 @@ Keep it under 350 words. Write prior.md now.`;
|
|
|
980
987
|
console.log('');
|
|
981
988
|
|
|
982
989
|
{
|
|
983
|
-
const
|
|
984
|
-
|
|
990
|
+
const imagesForThisMsg = [..._pendingImages];
|
|
991
|
+
_pendingImages = [];
|
|
985
992
|
|
|
986
|
-
if (
|
|
987
|
-
|
|
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`));
|
|
988
996
|
}
|
|
989
997
|
|
|
990
998
|
let responseText = '';
|
|
@@ -1012,7 +1020,7 @@ Keep it under 350 words. Write prior.md now.`;
|
|
|
1012
1020
|
model: currentModel,
|
|
1013
1021
|
cwd: process.cwd(),
|
|
1014
1022
|
projectContext,
|
|
1015
|
-
|
|
1023
|
+
images: imagesForThisMsg,
|
|
1016
1024
|
confirm,
|
|
1017
1025
|
signal: _currentAbortController.signal,
|
|
1018
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
223
|
-
|
|
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,
|
|
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) {
|