prior-cli 1.4.4 → 1.5.0

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
@@ -390,7 +390,69 @@ function hyperlink(text, url) {
390
390
 
391
391
  const PREVIEW_TOOLS = new Set(['file_read', 'run_command', 'web_search', 'url_fetch']);
392
392
 
393
- function renderToolDone(name, summary, preview) {
393
+ // ── Weather condition code → emoji ────────────────────────────
394
+ function weatherIcon(code) {
395
+ if (code === 113) return '☀️ ';
396
+ if (code === 116) return '⛅ ';
397
+ if (code === 119 || code === 122) return '☁️ ';
398
+ if (code === 143 || code === 248 || code === 260) return '🌫️';
399
+ if (code >= 176 && code <= 182) return '🌦️';
400
+ if (code >= 185 && code <= 227) return '🌨️';
401
+ if (code === 230) return '❄️ ';
402
+ if (code >= 248 && code <= 260) return '🌫️';
403
+ if (code >= 263 && code <= 296) return '🌧️';
404
+ if (code >= 299 && code <= 314) return '🌧️';
405
+ if (code >= 317 && code <= 338) return '🌨️';
406
+ if (code >= 350 && code <= 395) return '⛈️ ';
407
+ return '🌡️';
408
+ }
409
+
410
+ function dayLabel(dateStr) {
411
+ if (!dateStr) return ' ';
412
+ const d = new Date(dateStr);
413
+ return ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][d.getDay()];
414
+ }
415
+
416
+ function renderWeatherCard(w) {
417
+ const W = 42;
418
+ const pad = (str, len) => {
419
+ const plain = str.replace(/\x1b\[[0-9;]*m/g, '');
420
+ return str + ' '.repeat(Math.max(0, len - plain.length));
421
+ };
422
+ const line = (content = '') => {
423
+ process.stdout.write(c.muted(' │ ') + content + c.muted(' │') + '\n');
424
+ };
425
+ const rule = ' ├' + '─'.repeat(W) + '┤';
426
+ const top = ' ╭' + '─'.repeat(W) + '╮';
427
+ const bot = ' ╰' + '─'.repeat(W) + '╯';
428
+
429
+ const location = `📍 ${w.city}${w.country ? ', ' + w.country : ''}`;
430
+ const icon = weatherIcon(w.code);
431
+ const condLine = `${icon} ${w.desc}`;
432
+ const tempLine = `🌡 ${w.tempC}°C ${c.dim(`feels like ${w.feelsC}°C`)}`;
433
+ const infoLine = `💧 ${w.humidity}% 💨 ${w.windKmph} km/h`;
434
+
435
+ process.stdout.write(c.muted(top) + '\n');
436
+ line(pad(c.bold(location), W));
437
+ line();
438
+ line(pad(condLine, W));
439
+ line(pad(tempLine, W + 9)); // +9 for dim escape codes
440
+ line(pad(c.dim(infoLine), W + 9));
441
+
442
+ if (w.forecast && w.forecast.length) {
443
+ process.stdout.write(c.muted(rule) + '\n');
444
+ const days = w.forecast.map(f => pad(c.dim(dayLabel(f.date)), 12)).join(' ');
445
+ const icons = w.forecast.map(f => pad(weatherIcon(f.code), 12)).join(' ');
446
+ const temps = w.forecast.map(f => pad(c.dim(`${f.maxC}° / ${f.minC}°`), 12 + 9)).join(' ');
447
+ line(days);
448
+ line(icons);
449
+ line(temps);
450
+ }
451
+
452
+ process.stdout.write(c.muted(bot) + '\n');
453
+ }
454
+
455
+ function renderToolDone(name, summary, preview, weatherData) {
394
456
  const took = _toolStartTime ? c.dim(` · ${elapsed(Date.now() - _toolStartTime)}`) : '';
395
457
  let display = summary || '';
396
458
  if (/^[a-zA-Z]:[/\\]/.test(display) || display.startsWith('/')) {
@@ -401,6 +463,12 @@ function renderToolDone(name, summary, preview) {
401
463
  }
402
464
  process.stdout.write(` ${c.ok('✓')} ${c.muted(name)} ${display}${took}\n`);
403
465
 
466
+ // Weather card
467
+ if (name === 'get_weather' && weatherData) {
468
+ renderWeatherCard(weatherData);
469
+ return;
470
+ }
471
+
404
472
  // Rich preview for certain tools
405
473
  if (preview && PREVIEW_TOOLS.has(name)) {
406
474
  const lines = String(preview).split('\n').filter(l => l.trim());
@@ -927,7 +995,7 @@ Keep it under 350 words. Write prior.md now.`;
927
995
  renderToolStart(ev.name, ev.args);
928
996
  spinStart('working…');
929
997
  break;
930
- case 'tool_done': spinStop(); renderToolDone(ev.name, ev.summary, ev.preview); break;
998
+ case 'tool_done': spinStop(); renderToolDone(ev.name, ev.summary, ev.preview, ev.weather); break;
931
999
  case 'tool_error': spinStop(); renderToolError(ev.name, ev.error); break;
932
1000
  case 'text':
933
1001
  spinStop();
@@ -1094,7 +1162,7 @@ Keep it under 350 words. Write prior.md now.`;
1094
1162
 
1095
1163
  case 'tool_done':
1096
1164
  spinStop();
1097
- renderToolDone(ev.name, ev.summary, ev.preview);
1165
+ renderToolDone(ev.name, ev.summary, ev.preview, ev.weather);
1098
1166
  break;
1099
1167
 
1100
1168
  case 'tool_skip':
package/lib/agent.js CHANGED
@@ -289,7 +289,7 @@ async function runAgent({ messages, model, uncensored, cwd, projectContext, imag
289
289
  try {
290
290
  const toolResult = await executeTool(call.name, call.args, { cwd, token, send });
291
291
  // Pass output snippet so the CLI can show a rich preview
292
- send({ type: 'tool_done', name: call.name, summary: toolResult.summary, preview: toolResult.output });
292
+ send({ type: 'tool_done', name: call.name, summary: toolResult.summary, preview: toolResult.output, weather: toolResult.weather });
293
293
  resultParts.push(`<tool_result name="${call.name}">\n${toolResult.output}\n</tool_result>`);
294
294
  } catch (err) {
295
295
  send({ type: 'tool_error', name: call.name, error: err.message });
package/lib/tools.js CHANGED
@@ -160,6 +160,45 @@ const TOOLS = {
160
160
  };
161
161
  },
162
162
 
163
+ async get_weather({ location }, {}) {
164
+ if (!location) throw new Error('"location" is required');
165
+ const encoded = encodeURIComponent(location);
166
+ const res = await fetch(`https://wttr.in/${encoded}?format=j1`, {
167
+ headers: { 'User-Agent': 'prior-cli/1.0' },
168
+ timeout: 10000,
169
+ });
170
+ if (!res.ok) throw new Error(`Weather service error: HTTP ${res.status}`);
171
+ const data = await res.json();
172
+
173
+ const cur = data.current_condition?.[0] || {};
174
+ const area = data.nearest_area?.[0] || {};
175
+ const city = area.areaName?.[0]?.value || location;
176
+ const country = area.country?.[0]?.value || '';
177
+
178
+ const tempC = cur.temp_C || '?';
179
+ const feelsC = cur.FeelsLikeC || '?';
180
+ const humidity = cur.humidity || '?';
181
+ const windKmph = cur.windspeedKmph || '?';
182
+ const desc = cur.weatherDesc?.[0]?.value || '?';
183
+ const code = parseInt(cur.weatherCode || '113');
184
+
185
+ const forecast = (data.weather || []).slice(0, 3).map(d => ({
186
+ date: d.date,
187
+ maxC: d.maxtempC,
188
+ minC: d.mintempC,
189
+ desc: d.hourly?.[4]?.weatherDesc?.[0]?.value || '',
190
+ code: parseInt(d.hourly?.[4]?.weatherCode || '113'),
191
+ }));
192
+
193
+ // Build structured output for CLI to render as a card
194
+ const result = { city, country, tempC, feelsC, humidity, windKmph, desc, code, forecast };
195
+ return {
196
+ output: JSON.stringify(result),
197
+ summary: `${city}${country ? ', ' + country : ''} · ${tempC}°C · ${desc}`,
198
+ weather: result,
199
+ };
200
+ },
201
+
163
202
  async web_search({ query }, { token }) {
164
203
  if (!query) throw new Error('"query" is required');
165
204
  const res = await fetch(`${CLI_BASE}/api/web-search`, {
@@ -328,10 +367,10 @@ const TOOLS = {
328
367
  return {
329
368
  output: [
330
369
  `Username : @${u.username || '?'}`,
331
- `Display Name : ${u.display_name || u.username || '?'}`,
370
+ `Display Name : ${u.display_name || u.displayName || u.username || '?'}`,
332
371
  `Bio : ${u.bio || '(none)'}`,
333
- `Posts : ${u.post_count ?? 0}`,
334
- `Friends : ${u.friend_count ?? 0}`,
372
+ `Posts : ${u.post_count ?? u.posts_count ?? 0}`,
373
+ `Friends : ${u.friend_count ?? u.friends_count ?? 0}`,
335
374
  `Joined : ${u.created_at ? new Date(u.created_at).toLocaleDateString() : 'unknown'}`,
336
375
  ].join('\n'),
337
376
  summary: `@${u.username || '?'}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prior-cli",
3
- "version": "1.4.4",
3
+ "version": "1.5.0",
4
4
  "description": "Prior Network AI — command-line interface",
5
5
  "bin": {
6
6
  "prior": "bin/prior.js"