clarity-ai 1.1.1 → 1.2.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 +2 -13
- package/scripts/postinstall.js +18 -17
- package/src/providers/groq.js +8 -4
- package/src/ui/blocks.js +73 -53
- package/src/ui/chatbox.js +66 -15
- package/src/ui/prompt.js +80 -3
- package/src/utils/version-check.js +29 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clarity-ai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "AI agent CLI for Termux and terminal — chat, code, automate",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -17,25 +17,14 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"chalk": "^5.3.0",
|
|
19
19
|
"inquirer": "^9.2.12",
|
|
20
|
-
"node-fetch": "^3.3.2",
|
|
21
20
|
"conf": "^12.0.0",
|
|
22
21
|
"marked": "^12.0.0",
|
|
23
22
|
"marked-terminal": "^7.1.0",
|
|
24
|
-
"ora": "^8.0.1",
|
|
25
|
-
"boxen": "^7.1.1",
|
|
26
23
|
"figlet": "^1.7.0",
|
|
27
24
|
"gradient-string": "^2.0.2",
|
|
28
25
|
"cli-table3": "^0.6.3",
|
|
29
|
-
"commander": "^12.0.0",
|
|
30
|
-
"dotenv": "^16.4.5",
|
|
31
|
-
"axios": "^1.6.8",
|
|
32
|
-
"fs-extra": "^11.2.0",
|
|
33
|
-
"glob": "^10.3.12",
|
|
34
|
-
"open": "^10.1.0",
|
|
35
|
-
"update-notifier": "^7.0.0",
|
|
36
26
|
"semver": "^7.6.0",
|
|
37
|
-
"
|
|
38
|
-
"wrap-ansi": "^9.0.0"
|
|
27
|
+
"glob": "^10.3.12"
|
|
39
28
|
},
|
|
40
29
|
"keywords": [
|
|
41
30
|
"ai",
|
package/scripts/postinstall.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import figlet from 'figlet';
|
|
2
2
|
import gradient from 'gradient-string';
|
|
3
|
-
import boxen from 'boxen';
|
|
4
3
|
import chalk from 'chalk';
|
|
5
4
|
|
|
6
5
|
const GRADIENT = gradient(['#00d2ff', '#7b2ff7']);
|
|
@@ -14,40 +13,42 @@ try {
|
|
|
14
13
|
|
|
15
14
|
const nodeVersion = process.versions.node;
|
|
16
15
|
const [major] = nodeVersion.split('.').map(Number);
|
|
16
|
+
const W = process.stdout.columns || 80;
|
|
17
|
+
const line = '═'.repeat(W - 4);
|
|
17
18
|
|
|
18
19
|
if (major < 18) {
|
|
19
|
-
console.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
));
|
|
20
|
+
console.error();
|
|
21
|
+
console.error(chalk.hex('#ff4466')(` ╔${line}╗`));
|
|
22
|
+
console.error(chalk.hex('#ff4466')(` ║ ✗ Node.js 18+ required. You have v${nodeVersion}`));
|
|
23
|
+
console.error(chalk.hex('#ffcc00')(` ║ Termux: pkg install nodejs-lts`));
|
|
24
|
+
console.error(chalk.hex('#ff4466')(` ╚${line}╝`));
|
|
25
|
+
console.error();
|
|
24
26
|
process.exit(1);
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
const isTermux = process.env.PREFIX?.includes('com.termux');
|
|
28
30
|
|
|
29
|
-
console.log(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
));
|
|
34
|
-
|
|
35
|
-
console.log(chalk.hex('#666888')("Run 'clarity init' to configure your AI keys and get started."));
|
|
31
|
+
console.log();
|
|
32
|
+
console.log(chalk.hex('#00ff88')(` ╔${line}╗`));
|
|
33
|
+
console.log(chalk.hex('#00ff88')(` ║ ✓ CLARITY installed successfully!`));
|
|
34
|
+
console.log(chalk.hex('#00d2ff')(` ║ Run: clarity init`));
|
|
35
|
+
console.log(chalk.hex('#00ff88')(` ╚${line}╝`));
|
|
36
|
+
console.log();
|
|
37
|
+
console.log(chalk.hex('#666888')(" Run 'clarity init' to configure your AI keys and get started."));
|
|
36
38
|
|
|
37
39
|
const binDir = process.env.npm_config_prefix ? `${process.env.npm_config_prefix}/bin` : null;
|
|
38
40
|
if (isTermux && binDir) {
|
|
39
|
-
const { existsSync, writeFileSync, chmodSync, unlinkSync } = await import('fs');
|
|
41
|
+
const { existsSync, writeFileSync, chmodSync, unlinkSync, lstatSync } = await import('fs');
|
|
40
42
|
const { resolve } = await import('path');
|
|
41
43
|
const wrapperPath = resolve(binDir, 'clarity');
|
|
42
|
-
const targetPath = resolve(process.argv[1] || '.', '..', 'bin', 'clarity.js');
|
|
43
44
|
|
|
44
45
|
try {
|
|
45
|
-
const stat =
|
|
46
|
+
const stat = lstatSync(wrapperPath);
|
|
46
47
|
if (stat.isSymbolicLink()) {
|
|
47
48
|
unlinkSync(wrapperPath);
|
|
48
49
|
writeFileSync(wrapperPath, `#!/data/data/com.termux/files/usr/bin/bash\nexec node "${resolve(process.env.PREFIX || '/usr', 'lib/node_modules/clarity-ai/bin/clarity.js')}" "$@"\n`);
|
|
49
50
|
chmodSync(wrapperPath, '755');
|
|
50
|
-
console.log(chalk.hex('#00ff88')('✓ Termux wrapper created at:'), chalk.hex('#00d2ff')(wrapperPath));
|
|
51
|
+
console.log(chalk.hex('#00ff88')(' ✓ Termux wrapper created at:'), chalk.hex('#00d2ff')(wrapperPath));
|
|
51
52
|
}
|
|
52
53
|
} catch {}
|
|
53
54
|
}
|
package/src/providers/groq.js
CHANGED
|
@@ -8,15 +8,19 @@ const PROVIDER = {
|
|
|
8
8
|
|
|
9
9
|
async function* sendMessage(apiKey, messages, model = 'llama3-70b-8192', stream = true) {
|
|
10
10
|
const url = `${PROVIDER.baseURL}/chat/completions`;
|
|
11
|
-
const body = { model, messages, stream };
|
|
12
|
-
|
|
11
|
+
const body = { model, messages, stream, max_tokens: 4096 };
|
|
12
|
+
|
|
13
13
|
const res = await fetch(url, {
|
|
14
14
|
method: 'POST',
|
|
15
15
|
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
16
16
|
body: JSON.stringify(body),
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
if (!res.ok)
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
let detail = '';
|
|
21
|
+
try { const e = await res.json(); detail = e.error?.message || JSON.stringify(e); } catch { detail = res.statusText; }
|
|
22
|
+
throw new Error(`Groq API error: ${res.status} — ${detail}`);
|
|
23
|
+
}
|
|
20
24
|
|
|
21
25
|
if (!stream) {
|
|
22
26
|
const data = await res.json();
|
|
@@ -34,7 +38,7 @@ async function* sendMessage(apiKey, messages, model = 'llama3-70b-8192', stream
|
|
|
34
38
|
buffer += decoder.decode(value, { stream: true });
|
|
35
39
|
const lines = buffer.split('\n');
|
|
36
40
|
buffer = lines.pop() || '';
|
|
37
|
-
|
|
41
|
+
|
|
38
42
|
for (const line of lines) {
|
|
39
43
|
if (line.startsWith('data: ')) {
|
|
40
44
|
const data = line.slice(6).trim();
|
package/src/ui/blocks.js
CHANGED
|
@@ -1,74 +1,82 @@
|
|
|
1
1
|
import cliTable3 from 'cli-table3';
|
|
2
2
|
import c from './colors.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
const w = process.stdout.columns || 80;
|
|
6
|
-
console.log(color(char.repeat(w)));
|
|
7
|
-
}
|
|
4
|
+
const W = () => process.stdout.columns || 80;
|
|
8
5
|
|
|
9
|
-
function
|
|
10
|
-
|
|
6
|
+
function mid(text) {
|
|
7
|
+
const w = W();
|
|
8
|
+
const pad = Math.max(0, Math.floor((w - text.length) / 2));
|
|
9
|
+
return ' '.repeat(pad) + text;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
const FILL = '░';
|
|
13
|
+
const FILL_BG = '\x1b[48;5;17m';
|
|
14
|
+
const FILL_BG_PURPLE = '\x1b[48;5;53m';
|
|
15
|
+
const FILL_BG_RED = '\x1b[48;5;52m';
|
|
16
|
+
const FILL_BG_GREEN = '\x1b[48;5;22m';
|
|
17
|
+
const FILL_BG_YELLOW = '\x1b[48;5;58m';
|
|
18
|
+
const FILL_BG_BLUE = '\x1b[48;5;19m';
|
|
19
|
+
const RESET = '\x1b[0m';
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
console.log(c.warning(` └${'─'.repeat((process.stdout.columns || 80) - 4)}┘`));
|
|
35
|
-
gap();
|
|
36
|
-
},
|
|
21
|
+
function shadeBox(color, title, msg) {
|
|
22
|
+
const w = W();
|
|
23
|
+
const line = '━'.repeat(w - 4);
|
|
24
|
+
console.log();
|
|
25
|
+
console.log(color(` ┏${line}┓`));
|
|
26
|
+
console.log(color(` ┃${FILL_BG}${' '.repeat(w - 4)}${RESET}${color('┃')}`));
|
|
27
|
+
if (title) console.log(color(` ┃${FILL_BG} ${title}${' '.repeat(Math.max(0, w - 8 - title.length))}${RESET}${color('┃')}`));
|
|
28
|
+
msg.split('\n').forEach(l => console.log(color(` ┃${FILL_BG} ${c.white(l)}${' '.repeat(Math.max(0, w - 6 - l.length))}${RESET}${color('┃')}`)));
|
|
29
|
+
console.log(color(` ┃${FILL_BG}${' '.repeat(w - 4)}${RESET}${color('┃')}`));
|
|
30
|
+
console.log(color(` ┗${line}┛`));
|
|
31
|
+
console.log();
|
|
32
|
+
}
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
gap();
|
|
44
|
-
},
|
|
34
|
+
const blocks = {
|
|
35
|
+
info(title, msg) { shadeBox(c.info, `ℹ ${title}`, msg); },
|
|
36
|
+
success(title, msg) { shadeBox(c.success, `✓ ${title}`, msg); },
|
|
37
|
+
warn(title, msg) { shadeBox(c.warning, `⚠ ${title}`, msg); },
|
|
38
|
+
error(title, msg) { shadeBox(c.error, `✗ ${title}`, msg); },
|
|
45
39
|
|
|
46
40
|
code(lang, code) {
|
|
47
|
-
|
|
41
|
+
const w = W();
|
|
42
|
+
console.log(c.dim(` ┌─ ${lang} ${'─'.repeat(Math.max(0, w - lang.length - 8))}`));
|
|
48
43
|
code.split('\n').forEach(l => console.log(c.code(` │ ${l}`)));
|
|
49
|
-
console.log(c.dim(` └${'─'.repeat(
|
|
44
|
+
console.log(c.dim(` └${'─'.repeat(w - 4)}`));
|
|
50
45
|
},
|
|
51
46
|
|
|
52
47
|
file(path, content) {
|
|
48
|
+
const w = W();
|
|
53
49
|
console.log(c.filename(` ┌─ ${path}`));
|
|
54
50
|
content.split('\n').forEach(l => console.log(c.white(` │ ${l}`)));
|
|
55
|
-
console.log(c.dim(` └${'─'.repeat(
|
|
51
|
+
console.log(c.dim(` └${'─'.repeat(w - 4)}`));
|
|
56
52
|
},
|
|
57
53
|
|
|
58
|
-
ai(msg) {
|
|
59
|
-
const w =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
console.log(c.accent(`
|
|
63
|
-
|
|
54
|
+
ai(msg, title = 'CLARITY') {
|
|
55
|
+
const w = W();
|
|
56
|
+
const line = '═'.repeat(w - 4);
|
|
57
|
+
console.log();
|
|
58
|
+
console.log(c.accent(` ╔${line}╗`));
|
|
59
|
+
console.log(c.accent(` ║${FILL_BG_PURPLE}${' '.repeat(w - 4)}${RESET}${c.accent('║')}`));
|
|
60
|
+
if (title) console.log(c.accent(` ║${FILL_BG_PURPLE} ${c.ai(title)}${' '.repeat(Math.max(0, w - 8 - title.length))}${RESET}${c.accent('║')}`));
|
|
61
|
+
msg.split('\n').forEach(l => {
|
|
62
|
+
const clean = l.replace(/\x1b\[[0-9;]*m/g, '');
|
|
63
|
+
console.log(c.accent(` ║${FILL_BG_PURPLE} ${c.white(l)}${' '.repeat(Math.max(0, w - 6 - clean.length))}${RESET}${c.accent('║')}`));
|
|
64
|
+
});
|
|
65
|
+
console.log(c.accent(` ║${FILL_BG_PURPLE}${' '.repeat(w - 4)}${RESET}${c.accent('║')}`));
|
|
66
|
+
console.log(c.accent(` ╚${line}╝`));
|
|
67
|
+
console.log();
|
|
64
68
|
},
|
|
65
69
|
|
|
66
70
|
user(msg) {
|
|
67
|
-
const w =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
console.log(c.primary(`
|
|
71
|
-
|
|
71
|
+
const w = W();
|
|
72
|
+
const line = '─'.repeat(w - 4);
|
|
73
|
+
console.log();
|
|
74
|
+
console.log(c.primary(` ┌${line}┐`));
|
|
75
|
+
console.log(c.primary(` │${FILL_BG}${' '.repeat(w - 4)}${RESET}${c.primary('│')}`));
|
|
76
|
+
msg.split('\n').forEach(l => console.log(c.primary(` │${FILL_BG} ${c.user(l)}${' '.repeat(Math.max(0, w - 6 - l.length))}${RESET}${c.primary('│')}`)));
|
|
77
|
+
console.log(c.primary(` │${FILL_BG}${' '.repeat(w - 4)}${RESET}${c.primary('│')}`));
|
|
78
|
+
console.log(c.primary(` └${line}┘`));
|
|
79
|
+
console.log();
|
|
72
80
|
},
|
|
73
81
|
|
|
74
82
|
table(headers, rows) {
|
|
@@ -85,12 +93,12 @@ const blocks = {
|
|
|
85
93
|
},
|
|
86
94
|
|
|
87
95
|
divider(label) {
|
|
88
|
-
const w =
|
|
96
|
+
const w = W();
|
|
89
97
|
if (label) {
|
|
90
|
-
const side = Math.floor((w - label.length - 4) / 2);
|
|
98
|
+
const side = Math.max(0, Math.floor((w - label.length - 4) / 2));
|
|
91
99
|
console.log(c.dim(` ${'─'.repeat(side)} ${label} ${'─'.repeat(side)}`));
|
|
92
100
|
} else {
|
|
93
|
-
|
|
101
|
+
console.log(c.dim('─'.repeat(w)));
|
|
94
102
|
}
|
|
95
103
|
},
|
|
96
104
|
|
|
@@ -104,6 +112,18 @@ const blocks = {
|
|
|
104
112
|
const filled = Math.round(w * pct / 100);
|
|
105
113
|
const bar = c.primary('█'.repeat(filled)) + c.dim('░'.repeat(w - filled));
|
|
106
114
|
console.log(` ${c.muted(label)} ${bar} ${c.white(String(pct))}%`);
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
cmdsuggest(cmds) {
|
|
118
|
+
const w = W();
|
|
119
|
+
console.log();
|
|
120
|
+
console.log(c.dim(` ${'─'.repeat(w - 4)}`));
|
|
121
|
+
cmds.forEach(([cmd, desc], i) => {
|
|
122
|
+
const tag = c.badge(cmd, i === 0 ? 'cyan' : 'purple');
|
|
123
|
+
console.log(` ${tag} ${c.muted(desc)}`);
|
|
124
|
+
});
|
|
125
|
+
console.log(c.dim(` ${'─'.repeat(w - 4)}`));
|
|
126
|
+
console.log();
|
|
107
127
|
}
|
|
108
128
|
};
|
|
109
129
|
|
package/src/ui/chatbox.js
CHANGED
|
@@ -4,23 +4,39 @@ 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 } from './prompt.js';
|
|
7
|
+
import { createPrompt, showPrompt, addToHistory, loadHistory, showSuggestions, 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';
|
|
10
11
|
import commandRegistry from '../commands/index.js';
|
|
11
12
|
import memory from '../memory/store.js';
|
|
13
|
+
import { runTool } from '../tools/index.js';
|
|
14
|
+
import { isTermux } from '../utils/termux.js';
|
|
12
15
|
|
|
13
16
|
let rl = null;
|
|
14
17
|
let conversation = [];
|
|
15
18
|
|
|
19
|
+
const SYSTEM_PROMPT = `You are CLARITY, an autonomous AI agent running in the user's terminal (Termux on Android).
|
|
20
|
+
|
|
21
|
+
You have FULL access to tools: bash execution, file operations, web search, code execution, git, and package management.
|
|
22
|
+
When the user asks something that requires action — run commands, create/edit files, search the web, execute code — DO IT AUTOMATICALLY using your tools. Do not ask for permission.
|
|
23
|
+
|
|
24
|
+
Be proactive. If the user asks to do a task, break it down and execute each step.
|
|
25
|
+
Keep responses concise and terminal-friendly. Use markdown for code blocks.
|
|
26
|
+
|
|
27
|
+
Available tools: bash, files (create/read/delete/list), web fetch, web search, code run (js/python/bash), git, pkg (npm/pip/pkg), system info
|
|
28
|
+
|
|
29
|
+
Current environment: ${process.platform} ${process.arch}, Node ${process.versions.node}
|
|
30
|
+
Directory: ${process.cwd()}
|
|
31
|
+
Termux: ${isTermux()}`;
|
|
32
|
+
|
|
16
33
|
function renderHeader() {
|
|
17
34
|
const model = settings.get('defaultModel') || 'groq/llama3-70b-8192';
|
|
18
35
|
const w = process.stdout.columns || 80;
|
|
19
36
|
const left = c.accent('◈');
|
|
20
37
|
const mid = c.muted(` ${model} `);
|
|
21
38
|
const right = c.muted('/help');
|
|
22
|
-
const
|
|
23
|
-
const pad = '.'.repeat(Math.max(0, w - total - 4));
|
|
39
|
+
const pad = '.'.repeat(Math.max(0, Math.floor((w - mid.length - right.length - 8) / 2)));
|
|
24
40
|
console.log(c.border(` ${left}${c.dim(pad)}${mid}${c.dim(pad)}${right}`));
|
|
25
41
|
blocks.divider();
|
|
26
42
|
}
|
|
@@ -39,11 +55,22 @@ function startChat() {
|
|
|
39
55
|
|
|
40
56
|
rl = createPrompt();
|
|
41
57
|
showPrompt();
|
|
58
|
+
let cmdBuffer = '';
|
|
59
|
+
|
|
60
|
+
process.stdin.on('keypress', (char, key) => {
|
|
61
|
+
if (key && key.name === 'slash' && !cmdBuffer) {
|
|
62
|
+
cmdBuffer = '/';
|
|
63
|
+
suggestCommands('');
|
|
64
|
+
}
|
|
65
|
+
if (key && key.name === 'escape') {
|
|
66
|
+
if (cmdBuffer) { cmdBuffer = ''; }
|
|
67
|
+
}
|
|
68
|
+
});
|
|
42
69
|
|
|
43
70
|
rl.on('line', async (line) => {
|
|
44
71
|
const input = line.trim();
|
|
45
72
|
if (!input) { showPrompt(); return; }
|
|
46
|
-
|
|
73
|
+
cmdBuffer = '';
|
|
47
74
|
addToHistory(input);
|
|
48
75
|
|
|
49
76
|
if (input.startsWith('/')) {
|
|
@@ -51,7 +78,6 @@ function startChat() {
|
|
|
51
78
|
if (result?.exit) { closeChat(); return; }
|
|
52
79
|
} else {
|
|
53
80
|
conversation.push({ role: 'user', content: input });
|
|
54
|
-
blocks.user(input);
|
|
55
81
|
await handleAIResponse();
|
|
56
82
|
}
|
|
57
83
|
showPrompt();
|
|
@@ -68,6 +94,22 @@ function startChat() {
|
|
|
68
94
|
});
|
|
69
95
|
}
|
|
70
96
|
|
|
97
|
+
function suggestCommands(partial) {
|
|
98
|
+
const w = process.stdout.columns || 80;
|
|
99
|
+
const matches = ALL_COMMANDS.filter(([cmd]) => cmd.startsWith(partial));
|
|
100
|
+
const top = matches.slice(0, 6);
|
|
101
|
+
if (top.length === 0) return;
|
|
102
|
+
console.log();
|
|
103
|
+
console.log(c.dim(` ${'─'.repeat(w - 4)}`));
|
|
104
|
+
top.forEach(([cmd, desc], i) => {
|
|
105
|
+
const tag = c.badge('/' + cmd, i === 0 ? 'cyan' : 'purple');
|
|
106
|
+
console.log(` ${tag} ${c.muted(desc)}`);
|
|
107
|
+
});
|
|
108
|
+
console.log(c.dim(` ${'─'.repeat(w - 4)}`));
|
|
109
|
+
console.log();
|
|
110
|
+
showPrompt();
|
|
111
|
+
}
|
|
112
|
+
|
|
71
113
|
async function handleAIResponse() {
|
|
72
114
|
const model = settings.get('defaultModel') || 'groq/llama3-70b-8192';
|
|
73
115
|
const [provider] = model.split('/');
|
|
@@ -78,8 +120,8 @@ async function handleAIResponse() {
|
|
|
78
120
|
return;
|
|
79
121
|
}
|
|
80
122
|
|
|
81
|
-
const
|
|
82
|
-
const messages = [
|
|
123
|
+
const systemMsg = { role: 'system', content: SYSTEM_PROMPT + '\n\nUser memory: ' + (memory.show().filter(m => m.role === 'system').map(m => m.content).join('; ') || 'none') };
|
|
124
|
+
const messages = [systemMsg, ...conversation];
|
|
83
125
|
|
|
84
126
|
spin.start('CLARITY is thinking...');
|
|
85
127
|
|
|
@@ -92,24 +134,33 @@ async function handleAIResponse() {
|
|
|
92
134
|
console.log(c.accent(` ╔${'═'.repeat(w - 4)}╗`));
|
|
93
135
|
process.stdout.write(c.accent(' ║ ') + c.ai('CLARITY ') + c.white(''));
|
|
94
136
|
let full = '';
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
137
|
+
try {
|
|
138
|
+
for await (const chunk of stream) {
|
|
139
|
+
full += chunk;
|
|
140
|
+
process.stdout.write(c.white(chunk));
|
|
141
|
+
}
|
|
142
|
+
} catch (streamErr) {
|
|
143
|
+
if (full) process.stdout.write(c.warning('\n\n[stream interrupted]'));
|
|
144
|
+
else throw streamErr;
|
|
98
145
|
}
|
|
99
146
|
console.log();
|
|
100
147
|
console.log(c.accent(` ╚${'═'.repeat(w - 4)}╝`));
|
|
101
148
|
console.log();
|
|
102
|
-
|
|
103
|
-
|
|
149
|
+
if (full.trim()) {
|
|
150
|
+
conversation.push({ role: 'assistant', content: full });
|
|
151
|
+
memory.add(conversation);
|
|
152
|
+
}
|
|
104
153
|
} else {
|
|
105
154
|
spin.stop();
|
|
106
155
|
let full = '';
|
|
107
156
|
for await (const chunk of stream) {
|
|
108
157
|
full += chunk;
|
|
109
158
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
159
|
+
if (full.trim()) {
|
|
160
|
+
blocks.ai(full);
|
|
161
|
+
conversation.push({ role: 'assistant', content: full });
|
|
162
|
+
memory.add(conversation);
|
|
163
|
+
}
|
|
113
164
|
}
|
|
114
165
|
|
|
115
166
|
if (settings.get('showTokens')) {
|
package/src/ui/prompt.js
CHANGED
|
@@ -5,8 +5,75 @@ import paths from '../config/paths.js';
|
|
|
5
5
|
import c from './colors.js';
|
|
6
6
|
|
|
7
7
|
const HISTORY_FILE = paths.history;
|
|
8
|
+
const ALL_COMMANDS = [
|
|
9
|
+
['help', 'Show all commands'],
|
|
10
|
+
['init', 'Setup wizard'],
|
|
11
|
+
['chat', 'Start chat session'],
|
|
12
|
+
['ask', 'One-shot question'],
|
|
13
|
+
['clear', 'Clear screen'],
|
|
14
|
+
['exit', 'Exit CLARITY'],
|
|
15
|
+
['keys set', 'Set API key'],
|
|
16
|
+
['keys list', 'List API keys'],
|
|
17
|
+
['keys remove', 'Remove API key'],
|
|
18
|
+
['keys test', 'Test API key'],
|
|
19
|
+
['model set', 'Set default model'],
|
|
20
|
+
['model list', 'List models'],
|
|
21
|
+
['config show', 'Show config'],
|
|
22
|
+
['config reset', 'Reset config'],
|
|
23
|
+
['theme set', 'Set theme'],
|
|
24
|
+
['theme list', 'List themes'],
|
|
25
|
+
['file create', 'Create file'],
|
|
26
|
+
['file read', 'Read file'],
|
|
27
|
+
['file delete', 'Delete file'],
|
|
28
|
+
['file list', 'List directory'],
|
|
29
|
+
['file edit', 'Edit file'],
|
|
30
|
+
['bash', 'Run command'],
|
|
31
|
+
['code run', 'Run code file'],
|
|
32
|
+
['code write', 'AI write code'],
|
|
33
|
+
['code explain', 'Explain code'],
|
|
34
|
+
['code fix', 'Fix code'],
|
|
35
|
+
['code review', 'Review code'],
|
|
36
|
+
['code refactor', 'Refactor code'],
|
|
37
|
+
['code test', 'Generate tests'],
|
|
38
|
+
['code docs', 'Generate docs'],
|
|
39
|
+
['web', 'Fetch URL'],
|
|
40
|
+
['search', 'Web search'],
|
|
41
|
+
['git', 'Git operations'],
|
|
42
|
+
['pkg install', 'Install package'],
|
|
43
|
+
['pkg remove', 'Remove package'],
|
|
44
|
+
['agent start', 'Start agent'],
|
|
45
|
+
['agent stop', 'Stop agent'],
|
|
46
|
+
['agent list', 'List agents'],
|
|
47
|
+
['agent logs', 'Show agent logs'],
|
|
48
|
+
['tools list', 'List tools'],
|
|
49
|
+
['tools run', 'Run tool'],
|
|
50
|
+
['memory show', 'Show memory'],
|
|
51
|
+
['memory add', 'Add memory'],
|
|
52
|
+
['memory clear', 'Clear memory'],
|
|
53
|
+
['history show', 'Show history'],
|
|
54
|
+
['history clear', 'Clear history'],
|
|
55
|
+
['history export', 'Export history'],
|
|
56
|
+
['env show', 'Show env vars'],
|
|
57
|
+
['env set', 'Set env var'],
|
|
58
|
+
['status', 'System status'],
|
|
59
|
+
['version', 'Version info'],
|
|
60
|
+
['update', 'Check update'],
|
|
61
|
+
['alias create', 'Create alias'],
|
|
62
|
+
['alias list', 'List aliases'],
|
|
63
|
+
['export', 'Export chat'],
|
|
64
|
+
['import', 'Import chat'],
|
|
65
|
+
['summarize', 'Summarize content'],
|
|
66
|
+
['translate', 'Translate text'],
|
|
67
|
+
['generate', 'Generate content'],
|
|
68
|
+
['deploy', 'Deploy project'],
|
|
69
|
+
['debug', 'Debug file'],
|
|
70
|
+
['explain', 'Explain code'],
|
|
71
|
+
['run', 'Run command'],
|
|
72
|
+
];
|
|
73
|
+
|
|
8
74
|
const MAX_HISTORY = 500;
|
|
9
75
|
let history = [];
|
|
76
|
+
let showCmdSuggest = false;
|
|
10
77
|
|
|
11
78
|
function loadHistory() {
|
|
12
79
|
try {
|
|
@@ -32,6 +99,17 @@ function addToHistory(line) {
|
|
|
32
99
|
}
|
|
33
100
|
}
|
|
34
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
|
+
|
|
35
113
|
function createPrompt() {
|
|
36
114
|
loadHistory();
|
|
37
115
|
const rl = readline.createInterface({
|
|
@@ -46,8 +124,7 @@ function createPrompt() {
|
|
|
46
124
|
}
|
|
47
125
|
|
|
48
126
|
function showPrompt() {
|
|
49
|
-
|
|
50
|
-
process.stdout.write(` ${prefix} `);
|
|
127
|
+
process.stdout.write(` ${c.primary('◆')} `);
|
|
51
128
|
}
|
|
52
129
|
|
|
53
|
-
export { createPrompt, showPrompt, addToHistory, loadHistory, history };
|
|
130
|
+
export { createPrompt, showPrompt, addToHistory, loadHistory, history, showSuggestions, ALL_COMMANDS };
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import boxen from 'boxen';
|
|
3
2
|
import semver from 'semver';
|
|
4
3
|
import { isTermux } from './termux.js';
|
|
5
4
|
|
|
6
5
|
const MIN_NODE = '18.0.0';
|
|
6
|
+
const W = () => process.stdout.columns || 80;
|
|
7
|
+
const line = '═'.repeat(W() - 4);
|
|
8
|
+
|
|
9
|
+
function simpleBox(colorFn, title, msg) {
|
|
10
|
+
console.error();
|
|
11
|
+
console.error(colorFn(` ╔${line}╗`));
|
|
12
|
+
if (title) console.error(colorFn(` ║ ${title}`));
|
|
13
|
+
msg.split('\n').forEach(l => console.error(colorFn(` ║ ${l}`)));
|
|
14
|
+
console.error(colorFn(` ╚${line}╝`));
|
|
15
|
+
console.error();
|
|
16
|
+
}
|
|
7
17
|
|
|
8
18
|
function checkNodeVersion() {
|
|
9
19
|
const current = process.versions.node;
|
|
10
20
|
if (!semver.satisfies(current, `>=${MIN_NODE}`)) {
|
|
11
|
-
|
|
12
|
-
chalk.hex('#ff4466')
|
|
13
|
-
|
|
14
|
-
{
|
|
15
|
-
)
|
|
21
|
+
simpleBox(
|
|
22
|
+
chalk.hex('#ff4466'),
|
|
23
|
+
`✗ Node.js ${MIN_NODE}+ required`,
|
|
24
|
+
`You have v${current}\n${isTermux() ? 'Termux: pkg install nodejs-lts' : 'Install Node.js 18+ from https://nodejs.org'}`
|
|
25
|
+
);
|
|
16
26
|
process.exit(1);
|
|
17
27
|
}
|
|
18
28
|
}
|
|
@@ -22,11 +32,11 @@ async function checkDependencies(pkgDir) {
|
|
|
22
32
|
const { resolve } = await import('path');
|
|
23
33
|
const pkgPath = resolve(pkgDir, 'package.json');
|
|
24
34
|
if (!existsSync(pkgPath)) return true;
|
|
25
|
-
|
|
35
|
+
|
|
26
36
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
27
37
|
const deps = { ...pkg.dependencies, ...pkg.peerDependencies };
|
|
28
38
|
const missing = [];
|
|
29
|
-
|
|
39
|
+
|
|
30
40
|
for (const dep of Object.keys(deps || {})) {
|
|
31
41
|
try {
|
|
32
42
|
await import(dep);
|
|
@@ -34,13 +44,13 @@ async function checkDependencies(pkgDir) {
|
|
|
34
44
|
missing.push(dep);
|
|
35
45
|
}
|
|
36
46
|
}
|
|
37
|
-
|
|
47
|
+
|
|
38
48
|
if (missing.length > 0) {
|
|
39
|
-
|
|
40
|
-
chalk.hex('#ffcc00')
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
)
|
|
49
|
+
simpleBox(
|
|
50
|
+
chalk.hex('#ffcc00'),
|
|
51
|
+
'⚠ Missing dependencies',
|
|
52
|
+
missing.join(', ')
|
|
53
|
+
);
|
|
44
54
|
const { execSync } = await import('child_process');
|
|
45
55
|
execSync('npm install', { cwd: pkgDir, stdio: 'inherit' });
|
|
46
56
|
}
|
|
@@ -54,11 +64,11 @@ async function checkUpdates() {
|
|
|
54
64
|
const res = await fetch('https://registry.npmjs.org/clarity-ai/latest');
|
|
55
65
|
const data = await res.json();
|
|
56
66
|
if (data.version && semver.gt(data.version, current)) {
|
|
57
|
-
|
|
58
|
-
chalk.hex('#00d2ff')
|
|
59
|
-
|
|
60
|
-
{
|
|
61
|
-
)
|
|
67
|
+
simpleBox(
|
|
68
|
+
chalk.hex('#00d2ff'),
|
|
69
|
+
'Update Available',
|
|
70
|
+
`clarity-ai v${current} → v${data.version}\nRun: npm install -g clarity-ai`
|
|
71
|
+
);
|
|
62
72
|
}
|
|
63
73
|
} catch {}
|
|
64
74
|
}
|