prior-cli 1.3.12 → 1.4.1
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 +34 -8
- package/lib/agent.js +11 -19
- package/package.json +1 -1
package/bin/prior.js
CHANGED
|
@@ -388,7 +388,9 @@ function hyperlink(text, url) {
|
|
|
388
388
|
return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`;
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
-
|
|
391
|
+
const PREVIEW_TOOLS = new Set(['file_read', 'run_command', 'web_search', 'url_fetch']);
|
|
392
|
+
|
|
393
|
+
function renderToolDone(name, summary, preview) {
|
|
392
394
|
const took = _toolStartTime ? c.dim(` · ${elapsed(Date.now() - _toolStartTime)}`) : '';
|
|
393
395
|
let display = summary || '';
|
|
394
396
|
if (/^[a-zA-Z]:[/\\]/.test(display) || display.startsWith('/')) {
|
|
@@ -398,6 +400,19 @@ function renderToolDone(name, summary) {
|
|
|
398
400
|
display = c.dim(display);
|
|
399
401
|
}
|
|
400
402
|
process.stdout.write(` ${c.ok('✓')} ${c.muted(name)} ${display}${took}\n`);
|
|
403
|
+
|
|
404
|
+
// Rich preview for certain tools
|
|
405
|
+
if (preview && PREVIEW_TOOLS.has(name)) {
|
|
406
|
+
const lines = String(preview).split('\n').filter(l => l.trim());
|
|
407
|
+
const toShow = lines.slice(0, 5);
|
|
408
|
+
const more = lines.length - toShow.length;
|
|
409
|
+
if (toShow.length > 0) {
|
|
410
|
+
drawBox([
|
|
411
|
+
...toShow.map(l => ({ text: l.slice(0, 80), dim: true })),
|
|
412
|
+
...(more > 0 ? [{ text: `… ${more} more line${more !== 1 ? 's' : ''}`, dim: true }] : []),
|
|
413
|
+
]);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
401
416
|
}
|
|
402
417
|
|
|
403
418
|
function renderToolError(name, error) {
|
|
@@ -750,12 +765,25 @@ async function startChat(opts = {}) {
|
|
|
750
765
|
process.exit(0);
|
|
751
766
|
});
|
|
752
767
|
|
|
753
|
-
const PROMPT
|
|
768
|
+
const PROMPT = () => c.brand(' ❯ ');
|
|
769
|
+
const ML_PROMPT = () => c.brand(' … ');
|
|
770
|
+
|
|
771
|
+
let _mlBuf = []; // multiline accumulation (\ continuation)
|
|
754
772
|
|
|
755
773
|
const loop = () => {
|
|
756
|
-
|
|
774
|
+
const isML = _mlBuf.length > 0;
|
|
775
|
+
rl.question(isML ? ML_PROMPT() : PROMPT(), async raw => {
|
|
757
776
|
clearSuggestions();
|
|
758
|
-
|
|
777
|
+
|
|
778
|
+
// Backslash continuation — collect lines until one doesn't end with \
|
|
779
|
+
if (raw.endsWith('\\')) {
|
|
780
|
+
_mlBuf.push(raw.slice(0, -1));
|
|
781
|
+
return loop();
|
|
782
|
+
}
|
|
783
|
+
_mlBuf.push(raw);
|
|
784
|
+
const input = _mlBuf.join('\n').trim();
|
|
785
|
+
_mlBuf = [];
|
|
786
|
+
|
|
759
787
|
if (!input) return loop();
|
|
760
788
|
|
|
761
789
|
// ── Slash commands ──────────────────────────────────────
|
|
@@ -899,7 +927,7 @@ Keep it under 350 words. Write prior.md now.`;
|
|
|
899
927
|
renderToolStart(ev.name, ev.args);
|
|
900
928
|
spinStart('working…');
|
|
901
929
|
break;
|
|
902
|
-
case 'tool_done': spinStop(); renderToolDone(ev.name, ev.summary); break;
|
|
930
|
+
case 'tool_done': spinStop(); renderToolDone(ev.name, ev.summary, ev.preview); break;
|
|
903
931
|
case 'tool_error': spinStop(); renderToolError(ev.name, ev.error); break;
|
|
904
932
|
case 'text':
|
|
905
933
|
spinStop();
|
|
@@ -1032,7 +1060,6 @@ Keep it under 350 words. Write prior.md now.`;
|
|
|
1032
1060
|
|
|
1033
1061
|
case 'cancelled':
|
|
1034
1062
|
spinStop();
|
|
1035
|
-
process.stdout.write('\n');
|
|
1036
1063
|
console.log(c.muted(' ✗ Cancelled'));
|
|
1037
1064
|
break;
|
|
1038
1065
|
|
|
@@ -1060,7 +1087,7 @@ Keep it under 350 words. Write prior.md now.`;
|
|
|
1060
1087
|
|
|
1061
1088
|
case 'tool_done':
|
|
1062
1089
|
spinStop();
|
|
1063
|
-
renderToolDone(ev.name, ev.summary);
|
|
1090
|
+
renderToolDone(ev.name, ev.summary, ev.preview);
|
|
1064
1091
|
break;
|
|
1065
1092
|
|
|
1066
1093
|
case 'tool_skip':
|
|
@@ -1091,7 +1118,6 @@ Keep it under 350 words. Write prior.md now.`;
|
|
|
1091
1118
|
|
|
1092
1119
|
case 'error':
|
|
1093
1120
|
spinStop();
|
|
1094
|
-
process.stdout.write('\n');
|
|
1095
1121
|
console.error(c.err(` ✗ ${ev.message}`));
|
|
1096
1122
|
break;
|
|
1097
1123
|
}
|
package/lib/agent.js
CHANGED
|
@@ -8,7 +8,7 @@ const CLI_BASE = 'https://prior.ngrok.app/cli-backend';
|
|
|
8
8
|
const PRIOR_BASE = 'https://prior.ngrok.app';
|
|
9
9
|
const MAX_ITER = 14;
|
|
10
10
|
|
|
11
|
-
// ── Single inference call
|
|
11
|
+
// ── Single inference call ─────────────────────────────────────
|
|
12
12
|
|
|
13
13
|
async function infer(messages, model, token, { cwd, uncensored, projectContext, images } = {}, signal) {
|
|
14
14
|
const res = await fetch(`${CLI_BASE}/api/infer`, {
|
|
@@ -22,7 +22,7 @@ async function infer(messages, model, token, { cwd, uncensored, projectContext,
|
|
|
22
22
|
const err = await res.json().catch(() => ({}));
|
|
23
23
|
throw new Error(err.error || `Server error: HTTP ${res.status}`);
|
|
24
24
|
}
|
|
25
|
-
return await res.json();
|
|
25
|
+
return await res.json();
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// ── Token usage tracking ──────────────────────────────────────
|
|
@@ -201,35 +201,28 @@ function stripToolTags(text) {
|
|
|
201
201
|
const CONFIRM_TOOLS = new Set(['run_command', 'file_delete', 'file_write']);
|
|
202
202
|
|
|
203
203
|
async function runAgent({ messages, model, uncensored, cwd, projectContext, images, send, confirm, signal }) {
|
|
204
|
-
const token
|
|
204
|
+
const token = getToken();
|
|
205
205
|
const history = [...messages];
|
|
206
206
|
|
|
207
207
|
let totalPromptTokens = 0;
|
|
208
208
|
let totalCompletionTokens = 0;
|
|
209
|
-
let pendingImages = (images && images.length) ? images : null;
|
|
209
|
+
let pendingImages = (images && images.length) ? images : null;
|
|
210
210
|
|
|
211
211
|
for (let iter = 0; iter < MAX_ITER; iter++) {
|
|
212
212
|
|
|
213
|
-
if (signal?.aborted) {
|
|
214
|
-
send({ type: 'cancelled' });
|
|
215
|
-
send({ type: 'done' });
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
213
|
+
if (signal?.aborted) { send({ type: 'cancelled' }); send({ type: 'done' }); return; }
|
|
218
214
|
|
|
219
215
|
send({ type: 'thinking' });
|
|
220
216
|
|
|
221
|
-
let result;
|
|
222
217
|
const iterImages = pendingImages;
|
|
223
|
-
pendingImages = null;
|
|
218
|
+
pendingImages = null;
|
|
219
|
+
|
|
220
|
+
let result;
|
|
224
221
|
try {
|
|
225
222
|
result = await infer(history, model || 'qwen3.5:4b', token, { cwd, uncensored, projectContext, images: iterImages }, signal);
|
|
226
223
|
} catch (err) {
|
|
227
224
|
await trackTokenUsage(token, totalPromptTokens, totalCompletionTokens);
|
|
228
|
-
if (err.name === 'AbortError' || signal?.aborted) {
|
|
229
|
-
send({ type: 'cancelled' });
|
|
230
|
-
send({ type: 'done' });
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
225
|
+
if (err.name === 'AbortError' || signal?.aborted) { send({ type: 'cancelled' }); send({ type: 'done' }); return; }
|
|
233
226
|
send({ type: 'error', message: err.message });
|
|
234
227
|
send({ type: 'done' });
|
|
235
228
|
return;
|
|
@@ -252,7 +245,6 @@ async function runAgent({ messages, model, uncensored, cwd, projectContext, imag
|
|
|
252
245
|
if (calls.length === 0) {
|
|
253
246
|
const finalText = stripToolTags(cleaned);
|
|
254
247
|
if (!finalText && iter < MAX_ITER - 1) {
|
|
255
|
-
// Model returned blank (all think tags, no actual output) — nudge once
|
|
256
248
|
history.push({ role: 'assistant', content: raw });
|
|
257
249
|
history.push({ role: 'user', content: '(Your response was empty. Please write your reply.)' });
|
|
258
250
|
continue;
|
|
@@ -274,7 +266,6 @@ async function runAgent({ messages, model, uncensored, cwd, projectContext, imag
|
|
|
274
266
|
for (const call of calls) {
|
|
275
267
|
send({ type: 'tool_start', name: call.name, args: call.args });
|
|
276
268
|
|
|
277
|
-
// Confirmation gate for destructive / side-effect tools
|
|
278
269
|
if (confirm && CONFIRM_TOOLS.has(call.name)) {
|
|
279
270
|
const approved = await confirm({ name: call.name, args: call.args });
|
|
280
271
|
if (!approved) {
|
|
@@ -286,7 +277,8 @@ async function runAgent({ messages, model, uncensored, cwd, projectContext, imag
|
|
|
286
277
|
|
|
287
278
|
try {
|
|
288
279
|
const toolResult = await executeTool(call.name, call.args, { cwd, token, send });
|
|
289
|
-
|
|
280
|
+
// Pass output snippet so the CLI can show a rich preview
|
|
281
|
+
send({ type: 'tool_done', name: call.name, summary: toolResult.summary, preview: toolResult.output });
|
|
290
282
|
resultParts.push(`<tool_result name="${call.name}">\n${toolResult.output}\n</tool_result>`);
|
|
291
283
|
} catch (err) {
|
|
292
284
|
send({ type: 'tool_error', name: call.name, error: err.message });
|