prior-cli 1.4.5 → 1.5.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 CHANGED
@@ -390,7 +390,55 @@ 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
+ // Use left-border-only style — avoids emoji column-width alignment issues
418
+ const L = ' │ ';
419
+ const hr = ' ├─────────────────────────────────────\n';
420
+
421
+ process.stdout.write(c.muted(' ┌─────────────────────────────────────\n'));
422
+ process.stdout.write(c.muted(L) + c.bold(`📍 ${w.city}${w.country ? ', ' + w.country : ''}`) + '\n');
423
+ process.stdout.write(c.muted(L) + '\n');
424
+ process.stdout.write(c.muted(L) + `${weatherIcon(w.code)} ${w.desc}` + '\n');
425
+ process.stdout.write(c.muted(L) + `🌡 ${c.bold(w.tempC + '°C')} ${c.dim('feels like ' + w.feelsC + '°C')}` + '\n');
426
+ process.stdout.write(c.muted(L) + c.dim(`💧 ${w.humidity}% 💨 ${w.windKmph} km/h`) + '\n');
427
+
428
+ if (w.forecast && w.forecast.length) {
429
+ process.stdout.write(c.muted(hr));
430
+ for (const f of w.forecast) {
431
+ const day = dayLabel(f.date).padEnd(4);
432
+ const icon = weatherIcon(f.code);
433
+ const tmp = `${f.maxC}° / ${f.minC}°`;
434
+ process.stdout.write(c.muted(L) + c.dim(day) + ` ${icon} ` + c.dim(tmp) + '\n');
435
+ }
436
+ }
437
+
438
+ process.stdout.write(c.muted(' └─────────────────────────────────────\n'));
439
+ }
440
+
441
+ function renderToolDone(name, summary, preview, weatherData) {
394
442
  const took = _toolStartTime ? c.dim(` · ${elapsed(Date.now() - _toolStartTime)}`) : '';
395
443
  let display = summary || '';
396
444
  if (/^[a-zA-Z]:[/\\]/.test(display) || display.startsWith('/')) {
@@ -401,6 +449,12 @@ function renderToolDone(name, summary, preview) {
401
449
  }
402
450
  process.stdout.write(` ${c.ok('✓')} ${c.muted(name)} ${display}${took}\n`);
403
451
 
452
+ // Weather card
453
+ if (name === 'get_weather' && weatherData) {
454
+ renderWeatherCard(weatherData);
455
+ return;
456
+ }
457
+
404
458
  // Rich preview for certain tools
405
459
  if (preview && PREVIEW_TOOLS.has(name)) {
406
460
  const lines = String(preview).split('\n').filter(l => l.trim());
@@ -927,7 +981,7 @@ Keep it under 350 words. Write prior.md now.`;
927
981
  renderToolStart(ev.name, ev.args);
928
982
  spinStart('working…');
929
983
  break;
930
- case 'tool_done': spinStop(); renderToolDone(ev.name, ev.summary, ev.preview); break;
984
+ case 'tool_done': spinStop(); renderToolDone(ev.name, ev.summary, ev.preview, ev.weather); break;
931
985
  case 'tool_error': spinStop(); renderToolError(ev.name, ev.error); break;
932
986
  case 'text':
933
987
  spinStop();
@@ -1094,7 +1148,7 @@ Keep it under 350 words. Write prior.md now.`;
1094
1148
 
1095
1149
  case 'tool_done':
1096
1150
  spinStop();
1097
- renderToolDone(ev.name, ev.summary, ev.preview);
1151
+ renderToolDone(ev.name, ev.summary, ev.preview, ev.weather);
1098
1152
  break;
1099
1153
 
1100
1154
  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`, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prior-cli",
3
- "version": "1.4.5",
3
+ "version": "1.5.1",
4
4
  "description": "Prior Network AI — command-line interface",
5
5
  "bin": {
6
6
  "prior": "bin/prior.js"