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 +1 -1
- package/src/commands/index.js +67 -20
- package/src/config/settings.js +2 -2
- package/src/providers/index.js +6 -6
- package/src/ui/chatbox.js +48 -26
- package/src/ui/prompt.js +3 -18
package/package.json
CHANGED
package/src/commands/index.js
CHANGED
|
@@ -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', '/
|
|
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 (
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
|
package/src/config/settings.js
CHANGED
|
@@ -3,7 +3,7 @@ import paths from './paths.js';
|
|
|
3
3
|
|
|
4
4
|
const schema = {
|
|
5
5
|
theme: { type: 'string', default: 'dark' },
|
|
6
|
-
|
|
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/
|
|
20
|
+
defaultModel: 'groq/llama-3.3-70b-versatile',
|
|
21
21
|
stream: true,
|
|
22
22
|
showTokens: true,
|
|
23
23
|
saveHistory: true,
|
package/src/providers/index.js
CHANGED
|
@@ -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: ['
|
|
14
|
-
gemini: { free: true, streaming: true, models: ['gemini-
|
|
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.
|
|
17
|
-
anthropic: { free: false, streaming: true, models: ['claude-3-5-haiku-
|
|
18
|
-
openai: { free: false, streaming: true, models: ['gpt-4o-mini', 'gpt-4o', 'gpt-
|
|
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,
|
|
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
|
-
|
|
34
|
-
|
|
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
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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
|
|
100
|
-
const
|
|
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 =
|
|
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/
|
|
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
|
-
|
|
135
|
-
|
|
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(`
|
|
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
|
-
|
|
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 };
|