@tryfridayai/cli 0.2.1 → 0.2.4

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.
@@ -104,6 +104,11 @@ export function checkPreQueryHint(input) {
104
104
  return ` ${ORANGE}You need a ${intent.keyLabel} key for ${intent.hint}. Try ${BOLD}/keys${RESET}`;
105
105
  }
106
106
 
107
+ // Video intent — warn about cost
108
+ if (intent.capability === 'video') {
109
+ return ` ${ORANGE}⚠ Video generation can be expensive ($0.50–$5+ per video).${RESET}\n ${DIM}Monitor usage at your provider's dashboard (e.g. platform.openai.com/usage).${RESET}`;
110
+ }
111
+
107
112
  // Schedule intent — offer the /schedule command
108
113
  if (intent.capability === 'schedule') {
109
114
  return ` ${DIM}Tip: Use ${BOLD}/schedule${RESET}${DIM} to manage recurring tasks.${RESET}`;
@@ -1,48 +1,23 @@
1
1
  /**
2
2
  * chat/welcomeScreen.js — Branded welcome screen for Friday CLI chat
3
3
  *
4
- * Renders on `ready` message. Reads local config files directly
5
- * (instant, no server round-trip) to show capabilities and plugin status.
4
+ * Renders on `ready` message. Large ASCII art logo with the two
5
+ * vertical bars from the Friday brand, followed by capability
6
+ * indicators and quick-start hints. Inspired by Gemini CLI.
6
7
  */
7
8
 
8
9
  import fs from 'fs';
9
10
  import path from 'path';
10
11
  import os from 'os';
11
- import { createRequire } from 'module';
12
- import {
13
- PURPLE, TEAL, DIM, RESET, BOLD, ORANGE,
14
- drawBox, capabilityIcon, hint,
15
- } from './ui.js';
16
-
17
- const require = createRequire(import.meta.url);
18
-
19
- // ── Resolve paths ────────────────────────────────────────────────────────
20
-
21
- let runtimeDir;
22
- try {
23
- const runtimePkg = require.resolve('friday-runtime/package.json');
24
- runtimeDir = path.dirname(runtimePkg);
25
- } catch {
26
- runtimeDir = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', '..', '..', '..', 'runtime');
27
- }
12
+ import { PURPLE, TEAL, DIM, RESET } from './ui.js';
13
+ import { runtimeDir } from '../../resolveRuntime.js';
28
14
 
29
15
  const CONFIG_DIR = process.env.FRIDAY_CONFIG_DIR || path.join(os.homedir(), '.friday');
30
- const PLUGINS_FILE = path.join(CONFIG_DIR, 'plugins.json');
31
16
  const ENV_FILE = path.join(CONFIG_DIR, '.env');
32
17
 
33
18
  // ── Helpers ──────────────────────────────────────────────────────────────
34
19
 
35
- function readJsonSafe(filePath) {
36
- try {
37
- if (fs.existsSync(filePath)) {
38
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
39
- }
40
- } catch { /* ignore */ }
41
- return null;
42
- }
43
-
44
20
  function envHasKey(keyName) {
45
- // Check process.env first, then ~/.friday/.env
46
21
  if (process.env[keyName]) return true;
47
22
  try {
48
23
  if (fs.existsSync(ENV_FILE)) {
@@ -55,7 +30,6 @@ function envHasKey(keyName) {
55
30
  }
56
31
  }
57
32
  } catch { /* ignore */ }
58
- // Also check the project-level .env
59
33
  try {
60
34
  const projectEnv = path.join(runtimeDir, '..', '.env');
61
35
  if (fs.existsSync(projectEnv)) {
@@ -71,17 +45,25 @@ function envHasKey(keyName) {
71
45
  return false;
72
46
  }
73
47
 
74
- // ── Main render ──────────────────────────────────────────────────────────
48
+ // ── Logo colors ─────────────────────────────────────────────────────────
49
+
50
+ const GRAY_BAR = '\x1b[38;5;245m'; // left bar — gray
51
+ const WHITE_BAR = '\x1b[38;5;255m'; // right bar — bright white
52
+
53
+ // ── Main render ─────────────────────────────────────────────────────────
75
54
 
76
55
  /**
77
- * Render the branded welcome screen.
56
+ * Render the branded welcome screen with ASCII art logo.
78
57
  * @returns {string} The welcome screen string to print
79
58
  */
80
59
  export function renderWelcome() {
81
60
  // Read version from package.json
82
61
  let version = '0.2.0';
83
62
  try {
84
- const cliPkgPath = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', '..', '..', 'package.json');
63
+ const cliPkgPath = path.resolve(
64
+ path.dirname(new URL(import.meta.url).pathname),
65
+ '..', '..', '..', 'package.json',
66
+ );
85
67
  const pkg = JSON.parse(fs.readFileSync(cliPkgPath, 'utf8'));
86
68
  version = pkg.version || version;
87
69
  } catch { /* ignore */ }
@@ -92,70 +74,56 @@ export function renderWelcome() {
92
74
  const hasGoogle = envHasKey('GOOGLE_API_KEY');
93
75
  const hasElevenLabs = envHasKey('ELEVENLABS_API_KEY');
94
76
 
95
- // Determine capabilities
96
77
  const chatOk = hasAnthropic || hasOpenAI || hasGoogle;
97
78
  const imageOk = hasOpenAI || hasGoogle;
98
79
  const voiceOk = hasOpenAI || hasElevenLabs || hasGoogle;
99
80
  const videoOk = hasOpenAI || hasGoogle;
100
81
 
101
- // Read installed plugins
102
- const pluginsData = readJsonSafe(PLUGINS_FILE);
103
- const installedPlugins = pluginsData?.plugins ? Object.keys(pluginsData.plugins) : [];
82
+ // ── ASCII art: two bars (logo) + block-letter FRIDAY ──────────────
83
+ //
84
+ // Layout: ██ ██ FRIDAY (block text)
85
+ //
86
+ // Bars extend 2 rows above and below the text for visual weight.
87
+ // Left bar is gray, right bar is bright white, text is brand purple.
88
+
89
+ const B = (row) => ` ${GRAY_BAR}██${RESET} ${WHITE_BAR}██${RESET}${row}`;
90
+
91
+ const art = [
92
+ B(''),
93
+ B(''),
94
+ B(` ${PURPLE}█████ ████ ███ ████ ███ █ █${RESET}`),
95
+ B(` ${PURPLE}█ █ █ █ █ █ █ █ █ █${RESET}`),
96
+ B(` ${PURPLE}████ ████ █ █ █ █████ █${RESET}`),
97
+ B(` ${PURPLE}█ █ █ █ █ █ █ █ █${RESET}`),
98
+ B(` ${PURPLE}█ █ █ ███ ████ █ █ █${RESET}`),
99
+ B(''),
100
+ B(` ${DIM}v${version}${RESET}`),
101
+ ];
104
102
 
105
- // Read catalog for total count
106
- let totalPlugins = 0;
107
- let installedNames = [];
108
- try {
109
- const catalogPath = path.join(runtimeDir, 'src', 'plugins', 'catalog.json');
110
- const catalog = readJsonSafe(catalogPath);
111
- if (catalog?.plugins) {
112
- totalPlugins = Object.keys(catalog.plugins).length;
113
- installedNames = installedPlugins
114
- .map(id => catalog.plugins[id]?.name || id)
115
- .filter(Boolean);
116
- }
117
- } catch { /* ignore */ }
103
+ // ── Capability indicators ─────────────────────────────────────────
104
+
105
+ const cap = (label, active) => active
106
+ ? `${TEAL}\u25cf${RESET} ${label}`
107
+ : `${DIM}\u25cb ${label}${RESET}`;
118
108
 
119
- // Build capability line
120
109
  const caps = [
121
- capabilityIcon('\ud83d\udcac', 'Chat', chatOk),
122
- capabilityIcon('\ud83c\udfa8', 'Images', imageOk),
123
- capabilityIcon('\ud83d\udd0a', 'Voice', voiceOk),
124
- capabilityIcon('\ud83c\udfac', 'Video', videoOk),
125
- ].join(' ');
126
-
127
- // Build plugin line
128
- let pluginLine;
129
- if (installedNames.length > 0) {
130
- const names = installedNames.slice(0, 4).join(', ');
131
- const suffix = installedNames.length > 4 ? ` +${installedNames.length - 4} more` : '';
132
- pluginLine = ` Plugins: ${TEAL}${names}${suffix}${RESET}`;
133
- pluginLine += `${DIM} ${installedNames.length} of ${totalPlugins} installed${RESET}`;
134
- } else {
135
- pluginLine = ` ${DIM}Plugins: none installed${RESET}${DIM} 0 of ${totalPlugins} available${RESET}`;
136
- }
137
-
138
- // Build missing-key hints
139
- const hints = [];
140
- if (!imageOk || !voiceOk || !videoOk) {
141
- hints.push(` ${DIM}\u2191 ${ORANGE}/keys${DIM} to enable more capabilities${RESET}`);
142
- }
143
-
144
- // Assemble box content
145
- const boxLines = [
110
+ cap('Chat', chatOk),
111
+ cap('Images', imageOk),
112
+ cap('Voice', voiceOk),
113
+ cap('Video', videoOk),
114
+ ].join(' ');
115
+
116
+ // ── Assemble ──────────────────────────────────────────────────────
117
+
118
+ const lines = [
146
119
  '',
147
- ` ${caps}`,
120
+ ...art,
148
121
  '',
149
- pluginLine,
122
+ ` ${caps}`,
123
+ '',
124
+ ` ${DIM}/help${RESET} commands ${DIM}\u00b7${RESET} ${DIM}/keys${RESET} API keys ${DIM}\u00b7${RESET} ${DIM}/model${RESET} configure`,
150
125
  '',
151
- ` Type ${BOLD}/help${RESET} for commands, or just start talking.`,
152
126
  ];
153
- if (hints.length > 0) {
154
- boxLines.push('');
155
- boxLines.push(...hints);
156
- }
157
- boxLines.push('');
158
-
159
- const title = `Friday v${version}`;
160
- return '\n' + drawBox(title, boxLines);
127
+
128
+ return lines.join('\n');
161
129
  }