prior-cli 1.3.7 → 1.3.9
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 +85 -3
- package/lib/agent.js +7 -4
- package/package.json +1 -1
package/bin/prior.js
CHANGED
|
@@ -7,6 +7,7 @@ const readline = require('readline');
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const os = require('os');
|
|
9
9
|
const fs = require('fs');
|
|
10
|
+
const { execSync } = require('child_process');
|
|
10
11
|
const { version } = require('../package.json');
|
|
11
12
|
|
|
12
13
|
const api = require('../lib/api');
|
|
@@ -607,6 +608,7 @@ async function startChat(opts = {}) {
|
|
|
607
608
|
const chatHistory = [];
|
|
608
609
|
let currentModel = opts.model || null;
|
|
609
610
|
let _currentAbortController = null;
|
|
611
|
+
let _pendingImageBase64 = null; // set by alt+v clipboard paste
|
|
610
612
|
|
|
611
613
|
// ── Live slash-command suggestions ──────────────────────────
|
|
612
614
|
let clearSuggestions = () => {};
|
|
@@ -659,11 +661,36 @@ async function startChat(opts = {}) {
|
|
|
659
661
|
return;
|
|
660
662
|
}
|
|
661
663
|
|
|
664
|
+
// Alt+V — paste image from clipboard
|
|
665
|
+
if (key.meta && key.name === 'v') {
|
|
666
|
+
try {
|
|
667
|
+
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 { '' }`;
|
|
668
|
+
const b64 = execSync(`powershell -NoProfile -Command "${ps}"`, { timeout: 5000 }).toString().trim();
|
|
669
|
+
if (b64) {
|
|
670
|
+
_pendingImageBase64 = b64;
|
|
671
|
+
// Print the indicator below the current input line
|
|
672
|
+
process.stdout.write('\x1b[s');
|
|
673
|
+
process.stdout.write(`\x1b[B\r\x1b[2K ${c.brand('◈')} ${c.dim('image from clipboard attached — type your prompt and press enter')}`);
|
|
674
|
+
process.stdout.write('\x1b[u');
|
|
675
|
+
} else {
|
|
676
|
+
process.stdout.write('\x1b[s');
|
|
677
|
+
process.stdout.write(`\x1b[B\r\x1b[2K ${c.muted('✗ No image found in clipboard')}`);
|
|
678
|
+
process.stdout.write('\x1b[u');
|
|
679
|
+
}
|
|
680
|
+
} catch {
|
|
681
|
+
process.stdout.write('\x1b[s');
|
|
682
|
+
process.stdout.write(`\x1b[B\r\x1b[2K ${c.muted('✗ Could not read clipboard')}`);
|
|
683
|
+
process.stdout.write('\x1b[u');
|
|
684
|
+
}
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
|
|
662
688
|
if (key.name === 'return' || key.name === 'enter' || (key.ctrl && key.name === 'c')) {
|
|
663
|
-
// Clear suggestions
|
|
664
|
-
|
|
689
|
+
// Clear suggestions and image indicator before readline moves cursor
|
|
690
|
+
const clearRows = _suggCount + (_pendingImageBase64 ? 1 : 0);
|
|
691
|
+
if (clearRows > 0) {
|
|
665
692
|
process.stdout.write('\x1b[s');
|
|
666
|
-
for (let i = 0; i <
|
|
693
|
+
for (let i = 0; i < clearRows; i++) process.stdout.write('\x1b[B\r\x1b[2K');
|
|
667
694
|
process.stdout.write('\x1b[u');
|
|
668
695
|
_suggCount = 0;
|
|
669
696
|
}
|
|
@@ -944,6 +971,13 @@ Keep it under 350 words. Write prior.md now.`;
|
|
|
944
971
|
console.log('');
|
|
945
972
|
|
|
946
973
|
{
|
|
974
|
+
const imageForThisMsg = _pendingImageBase64;
|
|
975
|
+
_pendingImageBase64 = null;
|
|
976
|
+
|
|
977
|
+
if (imageForThisMsg) {
|
|
978
|
+
console.log(c.brand(' ◈') + c.dim(' image attached'));
|
|
979
|
+
}
|
|
980
|
+
|
|
947
981
|
let responseText = '';
|
|
948
982
|
let _progressStarted = false;
|
|
949
983
|
const _thinkStart = Date.now();
|
|
@@ -969,6 +1003,7 @@ Keep it under 350 words. Write prior.md now.`;
|
|
|
969
1003
|
model: currentModel,
|
|
970
1004
|
cwd: process.cwd(),
|
|
971
1005
|
projectContext,
|
|
1006
|
+
imageBase64: imageForThisMsg,
|
|
972
1007
|
confirm,
|
|
973
1008
|
signal: _currentAbortController.signal,
|
|
974
1009
|
send: ev => {
|
|
@@ -1259,6 +1294,53 @@ program
|
|
|
1259
1294
|
} catch (err) { console.error(c.err(` ✗ ${err.message}`)); }
|
|
1260
1295
|
});
|
|
1261
1296
|
|
|
1297
|
+
// ── UPDATE ─────────────────────────────────────────────────────
|
|
1298
|
+
program
|
|
1299
|
+
.command('update')
|
|
1300
|
+
.description('Check for updates and install if available')
|
|
1301
|
+
.action(async () => {
|
|
1302
|
+
const { execSync } = require('child_process');
|
|
1303
|
+
console.log('');
|
|
1304
|
+
process.stdout.write(c.dim(' Checking for updates…'));
|
|
1305
|
+
|
|
1306
|
+
const fetch = require('node-fetch');
|
|
1307
|
+
let latest;
|
|
1308
|
+
try {
|
|
1309
|
+
const res = await fetch('https://registry.npmjs.org/prior-cli/latest', { timeout: 8000 });
|
|
1310
|
+
if (!res.ok) throw new Error(`Registry error: HTTP ${res.status}`);
|
|
1311
|
+
const data = await res.json();
|
|
1312
|
+
latest = data.version;
|
|
1313
|
+
} catch (err) {
|
|
1314
|
+
clearLine();
|
|
1315
|
+
console.error(c.err(` ✗ Could not reach npm registry: ${err.message}\n`));
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
clearLine();
|
|
1320
|
+
|
|
1321
|
+
if (latest === version) {
|
|
1322
|
+
console.log(c.ok(' ✓ Already up to date ') + c.muted(`v${version}`));
|
|
1323
|
+
console.log('');
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
console.log(` ${c.muted('Current :')} ${c.white(`v${version}`)}`);
|
|
1328
|
+
console.log(` ${c.muted('Latest :')} ${c.bold(`v${latest}`)}`);
|
|
1329
|
+
console.log('');
|
|
1330
|
+
process.stdout.write(c.dim(' Installing update…'));
|
|
1331
|
+
|
|
1332
|
+
try {
|
|
1333
|
+
execSync('npm install -g prior-cli@latest', { stdio: 'ignore' });
|
|
1334
|
+
clearLine();
|
|
1335
|
+
console.log(c.ok(` ✓ Updated to v${latest} `) + c.muted('restart prior to apply'));
|
|
1336
|
+
} catch (err) {
|
|
1337
|
+
clearLine();
|
|
1338
|
+
console.error(c.err(` ✗ Install failed: ${err.message}`));
|
|
1339
|
+
console.error(c.muted(' Try manually: npm install -g prior-cli@latest'));
|
|
1340
|
+
}
|
|
1341
|
+
console.log('');
|
|
1342
|
+
});
|
|
1343
|
+
|
|
1262
1344
|
// ── Entry point ────────────────────────────────────────────────
|
|
1263
1345
|
program
|
|
1264
1346
|
.name('prior')
|
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 } = {}, signal) {
|
|
13
|
+
async function infer(messages, model, token, { cwd, uncensored, projectContext, imageBase64 } = {}, 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, imageBase64 }),
|
|
18
18
|
timeout: 120000,
|
|
19
19
|
signal,
|
|
20
20
|
});
|
|
@@ -200,12 +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, send, confirm, signal }) {
|
|
203
|
+
async function runAgent({ messages, model, uncensored, cwd, projectContext, imageBase64, 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
210
|
|
|
210
211
|
for (let iter = 0; iter < MAX_ITER; iter++) {
|
|
211
212
|
|
|
@@ -218,8 +219,10 @@ async function runAgent({ messages, model, uncensored, cwd, projectContext, send
|
|
|
218
219
|
send({ type: 'thinking' });
|
|
219
220
|
|
|
220
221
|
let result;
|
|
222
|
+
const iterImage = pendingImage;
|
|
223
|
+
pendingImage = null; // clear after first use
|
|
221
224
|
try {
|
|
222
|
-
result = await infer(history, model || 'qwen3.5:4b', token, { cwd, uncensored, projectContext }, signal);
|
|
225
|
+
result = await infer(history, model || 'qwen3.5:4b', token, { cwd, uncensored, projectContext, imageBase64: iterImage }, signal);
|
|
223
226
|
} catch (err) {
|
|
224
227
|
await trackTokenUsage(token, totalPromptTokens, totalCompletionTokens);
|
|
225
228
|
if (err.name === 'AbortError' || signal?.aborted) {
|