clarity-ai 1.2.0 → 1.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-ai",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "AI agent CLI for Termux and terminal — chat, code, automate",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -33,6 +33,7 @@ const commandRegistry = {
33
33
 
34
34
  case '/keys': return this.keys(args);
35
35
  case '/model': return this.model(args);
36
+ case '/provider': return this.provider(args);
36
37
  case '/config': return this.config(args);
37
38
  case '/theme': return this.theme(args);
38
39
 
@@ -147,7 +148,7 @@ const commandRegistry = {
147
148
 
148
149
  const categories = {
149
150
  'CHAT & AI': ['/chat', '/ask <q>', '/clear', '/history show|clear|export', '/memory show|add|clear'],
150
- 'CONFIGURATION': ['/init', '/keys set|list|remove|test', '/model set|list', '/config show|reset', '/theme set|list'],
151
+ 'CONFIGURATION': ['/init', '/keys set|list|remove|test', '/provider', '/model', '/config show|reset', '/theme set|list'],
151
152
  'FILES': ['/file create|read|delete|list|edit', '/bash <command>', '/code run|write|explain|fix|review|refactor|test|docs'],
152
153
  'WEB & SEARCH': ['/web <url>', '/search <query>', '/translate <text>', '/summarize <file|url>'],
153
154
  'AGENTS & TOOLS': ['/agent start|stop|list|logs', '/tools list|run'],
@@ -232,26 +233,72 @@ const commandRegistry = {
232
233
 
233
234
  async model(args) {
234
235
  const sub = args[0];
235
- if (!sub) { blocks.warn('Usage', '/model list|set <provider/model>'); return; }
236
+ if (sub === 'set') {
237
+ if (args.length < 2) { blocks.warn('Usage', '/model set <provider/model>'); return; }
238
+ settings.set('defaultModel', args[1]);
239
+ blocks.success('Model Set', `Default model: ${args[1]}`);
240
+ return;
241
+ }
236
242
 
237
- switch (sub) {
238
- case 'list': {
239
- const configured = Object.keys(listKeys());
240
- if (configured.length === 0) { blocks.info('No Keys', 'Configure keys first with /init'); return; }
241
- for (const p of configured) {
242
- const models = listModels(p);
243
- blocks.info(p, models.map(m => `${p}/${m}`).join('\n'));
244
- }
245
- break;
246
- }
247
- case 'set': {
248
- if (args.length < 2) { blocks.warn('Usage', '/model set <provider/model>'); return; }
249
- settings.set('defaultModel', args[1]);
250
- blocks.success('Model Set', `Default model: ${args[1]}`);
251
- break;
252
- }
253
- default:
254
- blocks.warn('Usage', '/model list|set <provider/model>');
243
+ const configured = Object.keys(listKeys());
244
+ if (configured.length === 0) { blocks.info('No Keys', 'Configure keys first with /init'); return; }
245
+
246
+ const { default: inquirer } = await import('inquirer');
247
+ const choices = [];
248
+ for (const p of configured) {
249
+ const models = listModels(p);
250
+ models.forEach(m => {
251
+ choices.push({ name: `${p}/${m}`, value: `${p}/${m}` });
252
+ });
253
+ }
254
+
255
+ if (choices.length === 0) { blocks.warn('No Models', 'No models available for configured providers.'); return; }
256
+
257
+ const current = settings.get('defaultModel') || 'groq/llama-3.3-70b-versatile';
258
+ const { model } = await inquirer.prompt([{
259
+ type: 'list',
260
+ name: 'model',
261
+ message: 'Select AI model:',
262
+ pageSize: 15,
263
+ choices: choices.map(c => ({
264
+ ...c,
265
+ checked: c.value === current,
266
+ })),
267
+ }]);
268
+ settings.set('defaultModel', model);
269
+ blocks.success('Model Set', `Default model: ${model}`);
270
+ },
271
+
272
+ async provider(args) {
273
+ const configured = Object.keys(listKeys());
274
+ if (configured.length === 0) { blocks.info('No Keys', 'Configure keys first with /init'); return; }
275
+
276
+ const { default: inquirer } = await import('inquirer');
277
+ const { provider } = await inquirer.prompt([{
278
+ type: 'list',
279
+ name: 'provider',
280
+ message: 'Select AI provider:',
281
+ choices: configured.map(p => ({
282
+ name: PROVIDER_NAMES[p] || p,
283
+ value: p,
284
+ })),
285
+ }]);
286
+
287
+ const models = listModels(provider);
288
+ if (models.length > 0) {
289
+ const { model } = await inquirer.prompt([{
290
+ type: 'list',
291
+ name: 'model',
292
+ message: `Select ${PROVIDER_NAMES[provider]} model:`,
293
+ choices: models.map(m => ({
294
+ name: `${provider}/${m}`,
295
+ value: `${provider}/${m}`,
296
+ })),
297
+ }]);
298
+ settings.set('defaultModel', model);
299
+ blocks.success('Provider & Model Set', `${PROVIDER_NAMES[provider]} → ${model}`);
300
+ } else {
301
+ blocks.warn('No Models', `No models listed for ${PROVIDER_NAMES[provider]}`);
255
302
  }
256
303
  },
257
304
 
@@ -3,7 +3,7 @@ import paths from './paths.js';
3
3
 
4
4
  const schema = {
5
5
  theme: { type: 'string', default: 'dark' },
6
- defaultModel: { type: 'string', default: 'groq/llama3-70b-8192' },
6
+ defaultModel: { type: 'string', default: 'groq/llama-3.3-70b-versatile' },
7
7
  stream: { type: 'boolean', default: true },
8
8
  showTokens: { type: 'boolean', default: true },
9
9
  saveHistory: { type: 'boolean', default: true },
@@ -17,7 +17,7 @@ const settings = new Conf({
17
17
  cwd: paths.config,
18
18
  defaults: {
19
19
  theme: 'dark',
20
- defaultModel: 'groq/llama3-70b-8192',
20
+ defaultModel: 'groq/llama-3.3-70b-versatile',
21
21
  stream: true,
22
22
  showTokens: true,
23
23
  saveHistory: true,
@@ -10,12 +10,12 @@ const providers = { groq, gemini, deepseek, openrouter, openai, anthropic: claud
10
10
  const PRIORITY = ['groq', 'gemini', 'deepseek', 'openrouter', 'openai', 'anthropic'];
11
11
 
12
12
  const capabilities = {
13
- groq: { free: true, streaming: true, models: ['llama3-70b-8192', 'llama3-8b-8192', 'mixtral-8x7b-32768', 'gemma2-9b-it'], baseURL: 'https://api.groq.com/openai/v1' },
14
- gemini: { free: true, streaming: true, models: ['gemini-1.5-flash', 'gemini-1.5-pro', 'gemini-2.0-flash-exp'], baseURL: 'https://generativelanguage.googleapis.com/v1beta' },
15
- deepseek: { free: false, cheap: true, streaming: true, models: ['deepseek-chat', 'deepseek-coder'], baseURL: 'https://api.deepseek.com/v1' },
16
- openrouter: { free: true, streaming: true, models: ['meta-llama/llama-3.1-8b-instruct:free', 'google/gemma-2-9b-it:free', 'mistralai/mistral-7b-instruct:free'], baseURL: 'https://openrouter.ai/api/v1' },
17
- anthropic: { free: false, streaming: true, models: ['claude-3-5-haiku-20241022', 'claude-3-5-sonnet-20241022'], baseURL: 'https://api.anthropic.com/v1' },
18
- openai: { free: false, streaming: true, models: ['gpt-4o-mini', 'gpt-4o', 'gpt-3.5-turbo'], baseURL: 'https://api.openai.com/v1' },
13
+ groq: { free: true, streaming: true, models: ['llama-3.3-70b-versatile', 'llama-3.1-8b-instant', 'meta-llama/llama-4-scout-17b-16e-instruct', 'meta-llama/llama-4-maverick-17b-128e-instruct', 'mixtral-8x7b-32768', 'gemma2-9b-it', 'qwen/qwen3-32b', 'deepseek-r1-distill-llama-70b', 'moonshotai/kimi-k2-instruct'], baseURL: 'https://api.groq.com/openai/v1' },
14
+ gemini: { free: true, streaming: true, models: ['gemini-2.0-flash', 'gemini-2.5-pro-exp-03-25', 'gemini-1.5-flash', 'gemini-1.5-pro'], baseURL: 'https://generativelanguage.googleapis.com/v1beta' },
15
+ deepseek: { free: false, cheap: true, streaming: true, models: ['deepseek-chat', 'deepseek-coder', 'deepseek-reasoner'], baseURL: 'https://api.deepseek.com/v1' },
16
+ openrouter: { free: true, streaming: true, models: ['meta-llama/llama-3.3-70b-instruct:free', 'google/gemma-2-9b-it:free', 'mistralai/mistral-7b-instruct:free', 'deepseek/deepseek-chat:free', 'qwen/qwen-2.5-72b-instruct:free'], baseURL: 'https://openrouter.ai/api/v1' },
17
+ anthropic: { free: false, streaming: true, models: ['claude-3-5-haiku-latest', 'claude-3-5-sonnet-latest', 'claude-3-haiku'], baseURL: 'https://api.anthropic.com/v1' },
18
+ openai: { free: false, streaming: true, models: ['gpt-4o-mini', 'gpt-4o', 'gpt-4.1', 'o3-mini'], baseURL: 'https://api.openai.com/v1' },
19
19
  };
20
20
 
21
21
  function sendMessage(apiKey, messages, model, stream = true) {
package/src/ui/chatbox.js CHANGED
@@ -4,14 +4,18 @@ import settings from '../config/settings.js';
4
4
  import { hasAnyKeys } from '../config/keys.js';
5
5
  import { getKey, PROVIDER_NAMES } from '../config/keys.js';
6
6
  import { sendMessage } from '../providers/index.js';
7
- import { createPrompt, showPrompt, addToHistory, loadHistory, showSuggestions, ALL_COMMANDS } from './prompt.js';
7
+ import { createPrompt, addToHistory, loadHistory, ALL_COMMANDS } from './prompt.js';
8
8
  import blocks from './blocks.js';
9
9
  import spin from './spinner.js';
10
- import agentManager from '../agents/manager.js';
11
10
  import commandRegistry from '../commands/index.js';
12
11
  import memory from '../memory/store.js';
13
- import { runTool } from '../tools/index.js';
14
12
  import { isTermux } from '../utils/termux.js';
13
+ import readline from 'readline';
14
+ import { readFileSync } from 'fs';
15
+
16
+ const PKG = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url)));
17
+ const VERSION = PKG.version;
18
+ const RESET = '\x1b[0m';
15
19
 
16
20
  let rl = null;
17
21
  let conversation = [];
@@ -30,15 +34,25 @@ Current environment: ${process.platform} ${process.arch}, Node ${process.version
30
34
  Directory: ${process.cwd()}
31
35
  Termux: ${isTermux()}`;
32
36
 
33
- function renderHeader() {
34
- const model = settings.get('defaultModel') || 'groq/llama3-70b-8192';
37
+ const GREY_BG = '\x1b[48;5;236m';
38
+
39
+ function renderPromptBar() {
40
+ const model = settings.get('defaultModel') || 'groq/llama-3.3-70b-versatile';
35
41
  const w = process.stdout.columns || 80;
36
- const left = c.accent('◈');
37
- const mid = c.muted(` ${model} `);
38
- const right = c.muted('/help');
39
- const pad = '.'.repeat(Math.max(0, Math.floor((w - mid.length - right.length - 8) / 2)));
40
- console.log(c.border(` ${left}${c.dim(pad)}${mid}${c.dim(pad)}${right}`));
41
- blocks.divider();
42
+ const line = '━'.repeat(w - 4);
43
+ const leftText = ` ${model} `;
44
+ const rightText = `v${VERSION} /help `;
45
+ const fill = Math.max(0, w - leftText.length - rightText.length - 8);
46
+
47
+ console.log(c.accent(` ┏${line}┓`));
48
+ console.log(c.accent(` ┃${GREY_BG}${' '.repeat(w - 4)}${RESET}${c.accent('┃')}`));
49
+ console.log(c.accent(` ┃${GREY_BG} ${c.muted(leftText)}${' '.repeat(fill)}${c.muted(rightText)}${RESET}${c.accent('┃')}`));
50
+ console.log(c.accent(` ┃${GREY_BG}${' '.repeat(w - 4)}${RESET}${c.accent('┃')}`));
51
+ console.log(c.accent(` ┗${line}┛`));
52
+ }
53
+
54
+ function showPrompt() {
55
+ process.stdout.write(` ${c.accent('◆')} `);
42
56
  }
43
57
 
44
58
  function startChat() {
@@ -50,17 +64,21 @@ function startChat() {
50
64
  loadHistory();
51
65
  console.clear();
52
66
  renderBanner();
53
- renderHeader();
54
67
  console.log();
55
-
56
- rl = createPrompt();
68
+ renderPromptBar();
57
69
  showPrompt();
70
+ rl = createPrompt();
71
+
58
72
  let cmdBuffer = '';
59
73
 
60
74
  process.stdin.on('keypress', (char, key) => {
61
75
  if (key && key.name === 'slash' && !cmdBuffer) {
62
76
  cmdBuffer = '/';
63
- suggestCommands('');
77
+ const input = rl.line || '';
78
+ readline.clearLine(process.stdout, 0);
79
+ readline.cursorTo(process.stdout, 0);
80
+ suggestCommands(input);
81
+ rl._refreshLine();
64
82
  }
65
83
  if (key && key.name === 'escape') {
66
84
  if (cmdBuffer) { cmdBuffer = ''; }
@@ -69,7 +87,7 @@ function startChat() {
69
87
 
70
88
  rl.on('line', async (line) => {
71
89
  const input = line.trim();
72
- if (!input) { showPrompt(); return; }
90
+ if (!input) { renderPromptBar(); showPrompt(); return; }
73
91
  cmdBuffer = '';
74
92
  addToHistory(input);
75
93
 
@@ -80,6 +98,7 @@ function startChat() {
80
98
  conversation.push({ role: 'user', content: input });
81
99
  await handleAIResponse();
82
100
  }
101
+ renderPromptBar();
83
102
  showPrompt();
84
103
  });
85
104
 
@@ -90,28 +109,27 @@ function startChat() {
90
109
 
91
110
  rl.on('SIGINT', () => {
92
111
  console.log(c.muted('\nUse /exit to quit'));
112
+ renderPromptBar();
93
113
  showPrompt();
94
114
  });
95
115
  }
96
116
 
97
117
  function suggestCommands(partial) {
98
118
  const w = process.stdout.columns || 80;
99
- const matches = ALL_COMMANDS.filter(([cmd]) => cmd.startsWith(partial));
100
- const top = matches.slice(0, 6);
119
+ const p = partial.replace('/', '');
120
+ const matches = p ? ALL_COMMANDS.filter(([cmd]) => cmd.startsWith(p)) : ALL_COMMANDS;
121
+ const top = matches.slice(0, 8);
101
122
  if (top.length === 0) return;
102
- console.log();
103
123
  console.log(c.dim(` ${'─'.repeat(w - 4)}`));
104
124
  top.forEach(([cmd, desc], i) => {
105
- const tag = c.badge('/' + cmd, i === 0 ? 'cyan' : 'purple');
125
+ const tag = blocks.badge('/' + cmd, i === 0 ? 'cyan' : 'purple');
106
126
  console.log(` ${tag} ${c.muted(desc)}`);
107
127
  });
108
128
  console.log(c.dim(` ${'─'.repeat(w - 4)}`));
109
- console.log();
110
- showPrompt();
111
129
  }
112
130
 
113
131
  async function handleAIResponse() {
114
- const model = settings.get('defaultModel') || 'groq/llama3-70b-8192';
132
+ const model = settings.get('defaultModel') || 'groq/llama-3.3-70b-versatile';
115
133
  const [provider] = model.split('/');
116
134
  const apiKey = getKey(provider);
117
135
 
@@ -131,8 +149,11 @@ async function handleAIResponse() {
131
149
  if (settings.get('stream')) {
132
150
  spin.stop();
133
151
  const w = process.stdout.columns || 80;
134
- console.log(c.accent(` ╔${''.repeat(w - 4)}╗`));
135
- process.stdout.write(c.accent(' ║ ') + c.ai('CLARITY ') + c.white(''));
152
+ const line = ''.repeat(w - 4);
153
+ const PURPLE_BG = '\x1b[48;5;53m';
154
+ console.log(c.accent(` ┏${line}┓`));
155
+ console.log(c.accent(` ┃${PURPLE_BG}${' '.repeat(w - 4)}${RESET}${c.accent('┃')}`));
156
+ process.stdout.write(c.accent(` ┃${PURPLE_BG} ${c.ai('CLARITY')} ${RESET}${c.white('')}`));
136
157
  let full = '';
137
158
  try {
138
159
  for await (const chunk of stream) {
@@ -144,7 +165,8 @@ async function handleAIResponse() {
144
165
  else throw streamErr;
145
166
  }
146
167
  console.log();
147
- console.log(c.accent(` ╚${''.repeat(w - 4)}╝`));
168
+ console.log(c.accent(` ┃${PURPLE_BG}${' '.repeat(w - 4)}${RESET}${c.accent('┃')}`));
169
+ console.log(c.accent(` ┗${line}┛`));
148
170
  console.log();
149
171
  if (full.trim()) {
150
172
  conversation.push({ role: 'assistant', content: full });
package/src/ui/prompt.js CHANGED
@@ -2,7 +2,6 @@ import readline from 'readline';
2
2
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
3
3
  import { dirname } from 'path';
4
4
  import paths from '../config/paths.js';
5
- import c from './colors.js';
6
5
 
7
6
  const HISTORY_FILE = paths.history;
8
7
  const ALL_COMMANDS = [
@@ -16,6 +15,8 @@ const ALL_COMMANDS = [
16
15
  ['keys list', 'List API keys'],
17
16
  ['keys remove', 'Remove API key'],
18
17
  ['keys test', 'Test API key'],
18
+ ['provider', 'Switch AI provider'],
19
+ ['model', 'Select AI model'],
19
20
  ['model set', 'Set default model'],
20
21
  ['model list', 'List models'],
21
22
  ['config show', 'Show config'],
@@ -73,7 +74,6 @@ const ALL_COMMANDS = [
73
74
 
74
75
  const MAX_HISTORY = 500;
75
76
  let history = [];
76
- let showCmdSuggest = false;
77
77
 
78
78
  function loadHistory() {
79
79
  try {
@@ -99,17 +99,6 @@ function addToHistory(line) {
99
99
  }
100
100
  }
101
101
 
102
- function showSuggestions(rl, partial) {
103
- const p = partial.replace('/', '');
104
- if (!p) {
105
- showCmdSuggest = true;
106
- return ALL_COMMANDS.slice(0, 8);
107
- }
108
- const matches = ALL_COMMANDS.filter(([cmd]) => cmd.startsWith(p));
109
- showCmdSuggest = matches.length > 0;
110
- return matches.slice(0, 8);
111
- }
112
-
113
102
  function createPrompt() {
114
103
  loadHistory();
115
104
  const rl = readline.createInterface({
@@ -123,8 +112,4 @@ function createPrompt() {
123
112
  return rl;
124
113
  }
125
114
 
126
- function showPrompt() {
127
- process.stdout.write(` ${c.primary('◆')} `);
128
- }
129
-
130
- export { createPrompt, showPrompt, addToHistory, loadHistory, history, showSuggestions, ALL_COMMANDS };
115
+ export { createPrompt, addToHistory, loadHistory, history, ALL_COMMANDS };