prior-cli 1.5.3 → 1.5.5

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
@@ -669,7 +669,7 @@ async function startChat(opts = {}) {
669
669
  console.log(c.ok(' ◉') + c.muted(' Agent mode ') + c.dim('· file web shell image prior-network'));
670
670
 
671
671
  console.log(DIVIDER);
672
- console.log(c.muted(' /help /clear /censored /uncensored /exit'));
672
+ console.log(c.muted(' /help /clear /compact /censored /uncensored /exit'));
673
673
  console.log(DIVIDER);
674
674
  console.log('');
675
675
 
@@ -686,6 +686,7 @@ async function startChat(opts = {}) {
686
686
  readline.emitKeypressEvents(process.stdin, rl);
687
687
 
688
688
  const SLASH_CMDS = [
689
+ { cmd: '/compact', desc: 'Compact conversation to save context' },
689
690
  { cmd: '/help', desc: 'Show help' },
690
691
  { cmd: '/clear', desc: 'Clear screen' },
691
692
  { cmd: '/censored', desc: 'Load standard model (qwen)' },
@@ -1037,9 +1038,98 @@ Keep it under 350 words. Write prior.md now.`;
1037
1038
  return loop();
1038
1039
  }
1039
1040
 
1041
+ case '/compact': {
1042
+ if (chatHistory.length === 0) {
1043
+ console.log(c.muted(' Nothing to compact yet.\n'));
1044
+ return loop();
1045
+ }
1046
+ const msgsBefore = chatHistory.length;
1047
+ const tokensBefore = chatHistory.reduce((n, m) => n + (m.content?.length || 0), 0);
1048
+
1049
+ // Animation frames while waiting
1050
+ const FRAMES = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
1051
+ const steps = [
1052
+ 'analyzing conversation…',
1053
+ 'identifying key context…',
1054
+ 'building summary…',
1055
+ 'compressing history…',
1056
+ ];
1057
+ let fi = 0; let si = 0;
1058
+ process.stdout.write('\n');
1059
+ const animTimer = setInterval(() => {
1060
+ process.stdout.clearLine(0);
1061
+ process.stdout.cursorTo(0);
1062
+ if (fi % 8 === 0 && si < steps.length - 1) si++;
1063
+ process.stdout.write(` ${c.brand(FRAMES[fi % FRAMES.length])} ${c.dim(steps[si])}`);
1064
+ fi++;
1065
+ }, 100);
1066
+
1067
+ try {
1068
+ const compactPrompt = [
1069
+ ...chatHistory,
1070
+ {
1071
+ role: 'user',
1072
+ content: `Please produce a compact summary of our conversation so far. Structure it as:
1073
+
1074
+ **What we were doing:** (1-2 sentences on the task/goal)
1075
+ **Key decisions made:** (bullet points)
1076
+ **Important context:** (facts, file paths, code details worth remembering)
1077
+ **Current state:** (where things stand right now)
1078
+
1079
+ Be concise but thorough — this summary replaces the full history to save context.`,
1080
+ },
1081
+ ];
1082
+
1083
+ const { infer } = require('./lib/agent');
1084
+ // infer is not exported — call backend directly
1085
+ const token = require('./lib/config').getToken();
1086
+ const res = await fetch('https://prior.ngrok.app/cli-backend/api/infer', {
1087
+ method: 'POST',
1088
+ headers: { 'Content-Type': 'application/json' },
1089
+ body: JSON.stringify({ messages: compactPrompt, model: currentModel, token, cwd: process.cwd() }),
1090
+ timeout: 120000,
1091
+ });
1092
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
1093
+ const data = await res.json();
1094
+ const summary = data.content?.trim() || '';
1095
+
1096
+ clearInterval(animTimer);
1097
+ process.stdout.clearLine(0);
1098
+ process.stdout.cursorTo(0);
1099
+
1100
+ if (!summary) throw new Error('Empty summary returned');
1101
+
1102
+ // Replace history with single compact message
1103
+ chatHistory.length = 0;
1104
+ chatHistory.push({ role: 'user', content: '[Conversation compacted]' });
1105
+ chatHistory.push({ role: 'assistant', content: summary });
1106
+
1107
+ const tokensAfter = summary.length;
1108
+ const saved = Math.round((1 - tokensAfter / tokensBefore) * 100);
1109
+
1110
+ process.stdout.write(` ${c.ok('✓')} ${c.bold('Compacted')} ${c.dim(`${msgsBefore} messages → 1 summary · ~${saved}% smaller`)}\n\n`);
1111
+
1112
+ // Draw summary box
1113
+ const summaryLines = summary.split('\n');
1114
+ process.stdout.write(c.muted(' ┌─────────────────────────────────────\n'));
1115
+ for (const line of summaryLines) {
1116
+ process.stdout.write(c.muted(' │ ') + c.dim(line) + '\n');
1117
+ }
1118
+ process.stdout.write(c.muted(' └─────────────────────────────────────\n\n'));
1119
+
1120
+ } catch (err) {
1121
+ clearInterval(animTimer);
1122
+ process.stdout.clearLine(0);
1123
+ process.stdout.cursorTo(0);
1124
+ console.error(c.err(` ✗ Compact failed: ${err.message}\n`));
1125
+ }
1126
+ return loop();
1127
+ }
1128
+
1040
1129
  case '/help':
1041
1130
  console.log('');
1042
1131
  console.log(c.bold(' Commands'));
1132
+ console.log(c.muted(' /compact ') + 'Compact conversation to save context');
1043
1133
  console.log(c.muted(' /clear ') + 'Clear screen');
1044
1134
  console.log(c.muted(' /censored ') + 'Load standard model (qwen)');
1045
1135
  console.log(c.muted(' /uncensored ') + 'Load uncensored model (dolphin)');
package/lib/tools.js CHANGED
@@ -376,6 +376,46 @@ const TOOLS = {
376
376
  summary: `@${u.username || '?'}`,
377
377
  };
378
378
  },
379
+ async zap_scan({ url, scan_type = 'passive' }, { token }) {
380
+ if (!url) throw new Error('"url" is required');
381
+ const res = await fetch(`${CLI_BASE}/api/zap/scan`, {
382
+ method: 'POST',
383
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
384
+ body: JSON.stringify({ url, scan_type }),
385
+ timeout: 60000,
386
+ });
387
+ if (res.status === 403) throw new Error('ZAP tools are only available to Organization accounts.');
388
+ if (!res.ok) { const e = await res.json().catch(() => ({})); throw new Error(e.error || `HTTP ${res.status}`); }
389
+ const data = await res.json();
390
+ return { output: data.output || JSON.stringify(data), summary: data.summary || `scan started for ${url}` };
391
+ },
392
+
393
+ async zap_alerts({ url }, { token }) {
394
+ const res = await fetch(`${CLI_BASE}/api/zap/alerts`, {
395
+ method: 'POST',
396
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
397
+ body: JSON.stringify({ url }),
398
+ timeout: 15000,
399
+ });
400
+ if (res.status === 403) throw new Error('ZAP tools are only available to Organization accounts.');
401
+ if (!res.ok) { const e = await res.json().catch(() => ({})); throw new Error(e.error || `HTTP ${res.status}`); }
402
+ const data = await res.json();
403
+ return { output: data.output || JSON.stringify(data), summary: data.summary || 'alerts fetched' };
404
+ },
405
+
406
+ async zap_spider({ url }, { token }) {
407
+ if (!url) throw new Error('"url" is required');
408
+ const res = await fetch(`${CLI_BASE}/api/zap/spider`, {
409
+ method: 'POST',
410
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
411
+ body: JSON.stringify({ url }),
412
+ timeout: 60000,
413
+ });
414
+ if (res.status === 403) throw new Error('ZAP tools are only available to Organization accounts.');
415
+ if (!res.ok) { const e = await res.json().catch(() => ({})); throw new Error(e.error || `HTTP ${res.status}`); }
416
+ const data = await res.json();
417
+ return { output: data.output || JSON.stringify(data), summary: data.summary || `spider started for ${url}` };
418
+ },
379
419
  };
380
420
 
381
421
  async function executeTool(name, args, context) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prior-cli",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "Prior Network AI — command-line interface",
5
5
  "bin": {
6
6
  "prior": "bin/prior.js"