clarity-ai 3.0.2 → 3.1.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/agents/loop.js +171 -51
- package/src/main.js +24 -17
- package/src/ui/blocks.js +115 -48
- package/src/ui/input.js +14 -45
package/package.json
CHANGED
package/src/agents/loop.js
CHANGED
|
@@ -1,32 +1,157 @@
|
|
|
1
1
|
import { callProvider } from '../providers/index.js';
|
|
2
2
|
import { buildSystemPrompt } from '../core/context.js';
|
|
3
3
|
import { addMessage } from '../core/history.js';
|
|
4
|
+
import { renderEdit, renderWrite, renderTool } from '../ui/blocks.js';
|
|
5
|
+
import { diffLines } from 'diff';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
4
8
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
9
|
+
export const SYSTEM_PROMPT = 'You are CLARITY, an autonomous AI agent CLI running in Termux on Android.\n\n' +
|
|
10
|
+
'## CRITICAL RULES — NEVER VIOLATE\n\n' +
|
|
11
|
+
'1. NEVER fabricate file contents, directory structures, or command outputs.\n' +
|
|
12
|
+
' - If you have not read a file, say "I have not read that file yet."\n' +
|
|
13
|
+
' - If a command has not run, say "I will run that now."\n' +
|
|
14
|
+
' - NEVER invent results.\n\n' +
|
|
15
|
+
'2. NEVER hallucinate tool calls — only call tools you actually have.\n' +
|
|
16
|
+
' Available tools: bash, read_file, write_file, edit_file, delete_file,\n' +
|
|
17
|
+
' list_directory, search_files, grep, web_search, web_fetch, git,\n' +
|
|
18
|
+
' run_tests, memory, task_planner, code_runner, pkg_manager, env,\n' +
|
|
19
|
+
' diff, compress, screenshot, clipboard, notify, version_check,\n' +
|
|
20
|
+
' context, agent_spawn.\n\n' +
|
|
21
|
+
'3. NEVER confirm an action before doing it — just do it.\n' +
|
|
22
|
+
' Bad: "I will create the workspace folder now." [then waits]\n' +
|
|
23
|
+
' Good: [calls bash tool: mkdir workspace] then "Created workspace/."\n\n' +
|
|
24
|
+
'4. ALWAYS ground responses in actual tool output.\n' +
|
|
25
|
+
' If a tool returns an error, report the actual error. Do not pretend it succeeded.\n\n' +
|
|
26
|
+
'5. SHORT responses. No filler. No "Certainly!" No "Great question!"\n' +
|
|
27
|
+
' Just: action -> result -> next step if needed.\n\n' +
|
|
28
|
+
'6. When editing files, ALWAYS read the file first, then make targeted edits.\n' +
|
|
29
|
+
' Never overwrite a file you have not read.\n\n' +
|
|
30
|
+
'7. For multi-step tasks, plan first with task_planner, then execute step by step.\n' +
|
|
31
|
+
' Show each step as you complete it.\n\n' +
|
|
32
|
+
'## RESPONSE FORMAT\n\n' +
|
|
33
|
+
'- Plain text for explanations. No markdown headers in chat.\n' +
|
|
34
|
+
'- File edits shown as diffs automatically by the UI — do not describe edits in text.\n' +
|
|
35
|
+
'- Code in responses: just write it plainly, no fences needed unless showing a standalone snippet.\n' +
|
|
36
|
+
'- Errors: state the actual error and what you will try next.\n\n' +
|
|
37
|
+
'## IDENTITY\n\n' +
|
|
38
|
+
'You are CLARITY v3.0. You are running on ' + process.platform + '.\n' +
|
|
39
|
+
'Current working directory: ' + process.cwd() + '\n' +
|
|
40
|
+
'Agent mode: ON — you can and should use tools without asking permission.\n' +
|
|
41
|
+
'\nAvailable tools with JSON schema:\n' +
|
|
42
|
+
'- bash { command: string }\n' +
|
|
43
|
+
'- read_file { path: string }\n' +
|
|
44
|
+
'- write_file { path: string, content: string }\n' +
|
|
45
|
+
'- edit_file { path: string, old_str: string, new_str: string }\n' +
|
|
46
|
+
'- delete_file { path: string }\n' +
|
|
47
|
+
'- list_directory { dir: string }\n' +
|
|
48
|
+
'- search_files { pattern: string }\n' +
|
|
49
|
+
'- grep { pattern: string, path: string }\n' +
|
|
50
|
+
'- web_search { query: string }\n' +
|
|
51
|
+
'- web_fetch { url: string }\n' +
|
|
52
|
+
'- git { subcommand: string }\n' +
|
|
53
|
+
'- memory { action: string, key?: string, value?: string }\n' +
|
|
54
|
+
'- code_runner { language: string, code: string }\n' +
|
|
55
|
+
'- pkg_manager { manager: string, action: string, packages: string }\n\n' +
|
|
56
|
+
'Respond with JSON:\n' +
|
|
57
|
+
'{"tool": "tool_name", "args": {"arg1": "val1"}}\n' +
|
|
58
|
+
'or to respond to user:\n' +
|
|
59
|
+
'{"response": "your final answer here"}';
|
|
60
|
+
|
|
61
|
+
function filterContextHunks(hunks, ctx) {
|
|
62
|
+
const changedIdx = hunks
|
|
63
|
+
.map((h, i) => (h.type !== 'context' ? i : -1))
|
|
64
|
+
.filter((i) => i >= 0);
|
|
65
|
+
return hunks.filter((_, i) => {
|
|
66
|
+
if (hunks[i].type !== 'context') return true;
|
|
67
|
+
for (const c of changedIdx) {
|
|
68
|
+
if (Math.abs(i - c) <= ctx) return true;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function executeToolCall(toolName, toolInput) {
|
|
75
|
+
switch (toolName) {
|
|
76
|
+
case 'write_file': {
|
|
77
|
+
const { path, content } = toolInput;
|
|
78
|
+
const lines = content.split('\n').length;
|
|
79
|
+
writeFileSync(path, content, 'utf-8');
|
|
80
|
+
console.log(renderWrite(path, lines));
|
|
81
|
+
return 'Written ' + lines + ' lines to ' + path;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
case 'edit_file': {
|
|
85
|
+
const { path, old_str, new_str } = toolInput;
|
|
86
|
+
if (!existsSync(path)) return 'Error: File not found: ' + path;
|
|
87
|
+
const original = readFileSync(path, 'utf-8');
|
|
88
|
+
const updated = original.replace(old_str, new_str);
|
|
89
|
+
|
|
90
|
+
const changes = diffLines(original, updated);
|
|
91
|
+
const hunks = [];
|
|
92
|
+
let lineNum = 1;
|
|
93
|
+
for (const part of changes) {
|
|
94
|
+
for (const line of part.value.split('\n').slice(0, -1)) {
|
|
95
|
+
if (part.added) {
|
|
96
|
+
hunks.push({ type: 'add', line, lineNum });
|
|
97
|
+
} else if (part.removed) {
|
|
98
|
+
hunks.push({ type: 'remove', line, lineNum });
|
|
99
|
+
} else {
|
|
100
|
+
hunks.push({ type: 'context', line, lineNum });
|
|
101
|
+
}
|
|
102
|
+
lineNum++;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const filtered = filterContextHunks(hunks, 2);
|
|
107
|
+
writeFileSync(path, updated, 'utf-8');
|
|
108
|
+
console.log(renderEdit(path, filtered));
|
|
109
|
+
return 'Edited ' + path;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
case 'bash': {
|
|
113
|
+
const { command } = toolInput;
|
|
114
|
+
try {
|
|
115
|
+
const result = execSync(command, { encoding: 'utf-8', timeout: 30000 });
|
|
116
|
+
const out = result || '(no output)';
|
|
117
|
+
console.log(renderTool('bash', command, out));
|
|
118
|
+
return out;
|
|
119
|
+
} catch (err) {
|
|
120
|
+
const errMsg = err.stderr || err.message;
|
|
121
|
+
console.log(renderTool('bash', command, errMsg));
|
|
122
|
+
return 'Error: ' + errMsg;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
case 'read_file': {
|
|
127
|
+
const { path } = toolInput;
|
|
128
|
+
if (!existsSync(path)) return 'Error: File not found: ' + path;
|
|
129
|
+
const content = readFileSync(path, 'utf-8');
|
|
130
|
+
console.log(renderTool('read_file', path, content.slice(0, 200)));
|
|
131
|
+
return content;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
default: {
|
|
135
|
+
try {
|
|
136
|
+
const mod = await import('../tools/' + toolName + '.js');
|
|
137
|
+
const toolFn = Object.values(mod)[0];
|
|
138
|
+
const result = await toolFn(toolInput);
|
|
139
|
+
console.log(renderTool(toolName, JSON.stringify(toolInput).slice(0, 60), result));
|
|
140
|
+
return result;
|
|
141
|
+
} catch (e) {
|
|
142
|
+
return 'Tool error: ' + e.message;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
25
147
|
|
|
26
148
|
export async function agentLoop(userMessage, config, history) {
|
|
27
149
|
const messages = [
|
|
28
|
-
{ role: 'system', content: buildSystemPrompt(
|
|
29
|
-
...history.slice(-20).map(m => ({
|
|
150
|
+
{ role: 'system', content: buildSystemPrompt(SYSTEM_PROMPT) },
|
|
151
|
+
...history.slice(-20).map((m) => ({
|
|
152
|
+
role: m.role === 'assistant' ? 'assistant' : 'user',
|
|
153
|
+
content: m.content,
|
|
154
|
+
})),
|
|
30
155
|
{ role: 'user', content: userMessage },
|
|
31
156
|
];
|
|
32
157
|
|
|
@@ -41,40 +166,35 @@ export async function agentLoop(userMessage, config, history) {
|
|
|
41
166
|
fullResponse += chunk;
|
|
42
167
|
}
|
|
43
168
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
51
|
-
|
|
52
|
-
if (parsed.response) {
|
|
53
|
-
finalResponse = parsed.response;
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (parsed.tool) {
|
|
58
|
-
toolCalls++;
|
|
59
|
-
const toolName = parsed.tool;
|
|
60
|
-
const args = parsed.args || {};
|
|
61
|
-
|
|
62
|
-
let result;
|
|
63
|
-
try {
|
|
64
|
-
const mod = await import(`../tools/${toolName}.js`);
|
|
65
|
-
const toolFn = Object.values(mod)[0];
|
|
66
|
-
result = await toolFn(args);
|
|
67
|
-
} catch (e) {
|
|
68
|
-
result = `Tool error: ${e.message}`;
|
|
69
|
-
}
|
|
169
|
+
const jsonMatch = fullResponse.match(/\{[^]*\}/);
|
|
170
|
+
if (!jsonMatch) {
|
|
171
|
+
finalResponse = fullResponse;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
70
174
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
175
|
+
let parsed;
|
|
176
|
+
try {
|
|
177
|
+
parsed = JSON.parse(jsonMatch[0]);
|
|
74
178
|
} catch {
|
|
75
179
|
finalResponse = fullResponse;
|
|
76
180
|
break;
|
|
77
181
|
}
|
|
182
|
+
|
|
183
|
+
if (parsed.response) {
|
|
184
|
+
finalResponse = parsed.response;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (parsed.tool) {
|
|
189
|
+
toolCalls++;
|
|
190
|
+
const toolName = parsed.tool;
|
|
191
|
+
const args = parsed.args || {};
|
|
192
|
+
|
|
193
|
+
const result = await executeToolCall(toolName, args);
|
|
194
|
+
|
|
195
|
+
messages.push({ role: 'assistant', content: fullResponse });
|
|
196
|
+
messages.push({ role: 'user', content: 'Tool result: ' + result });
|
|
197
|
+
}
|
|
78
198
|
}
|
|
79
199
|
|
|
80
200
|
if (toolCalls >= maxToolCalls) {
|
package/src/main.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import readline from 'readline';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { renderAI, renderUser, renderInfo, renderError, renderSuccess, renderWarning, divider } from './ui/blocks.js';
|
|
4
4
|
import { dispatchCommand } from './commands/index.js';
|
|
5
5
|
import { callProvider } from './providers/index.js';
|
|
6
6
|
import { loadHistory, saveHistory, addMessage } from './core/history.js';
|
|
@@ -9,23 +9,25 @@ import { agentLoop } from './agents/loop.js';
|
|
|
9
9
|
export async function startChat(config) {
|
|
10
10
|
const history = loadHistory();
|
|
11
11
|
|
|
12
|
-
console.log(
|
|
13
|
-
'CLARITY-AI v' + config.version + ' interactive session started
|
|
14
|
-
'Type /help for commands. Ctrl+C to exit
|
|
15
|
-
'Provider: ' + config.provider + ' | Model: ' + config.model,
|
|
16
|
-
'◆ Ready ── ' + new Date().toLocaleTimeString()
|
|
12
|
+
console.log(renderSuccess(
|
|
13
|
+
'CLARITY-AI v' + config.version + ' interactive session started. ' +
|
|
14
|
+
'Type /help for commands. Ctrl+C to exit.'
|
|
17
15
|
));
|
|
16
|
+
console.log(divider());
|
|
18
17
|
console.log();
|
|
19
18
|
|
|
20
19
|
const rl = readline.createInterface({
|
|
21
20
|
input: process.stdin,
|
|
22
21
|
output: process.stdout,
|
|
23
22
|
terminal: true,
|
|
24
|
-
historySize:
|
|
23
|
+
historySize: 200,
|
|
25
24
|
});
|
|
26
25
|
|
|
27
26
|
function showPrompt() {
|
|
28
|
-
const pre =
|
|
27
|
+
const pre =
|
|
28
|
+
chalk.hex('#00FFFF')('\u276f ') +
|
|
29
|
+
chalk.dim(config.provider + '/' + config.model) +
|
|
30
|
+
chalk.hex('#00FFFF')(' \u276f ');
|
|
29
31
|
rl.setPrompt(pre);
|
|
30
32
|
rl.prompt();
|
|
31
33
|
}
|
|
@@ -50,31 +52,36 @@ export async function startChat(config) {
|
|
|
50
52
|
return;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
console.log(
|
|
55
|
+
console.log();
|
|
56
|
+
console.log(renderUser(input));
|
|
57
|
+
console.log();
|
|
54
58
|
|
|
55
59
|
if (config.agentMode !== false) {
|
|
56
60
|
const result = await agentLoop(input, config, history);
|
|
57
61
|
addMessage(history, 'user', input);
|
|
58
62
|
addMessage(history, 'assistant', result);
|
|
59
63
|
saveHistory(history);
|
|
60
|
-
console.log(
|
|
64
|
+
console.log();
|
|
65
|
+
console.log(renderAI(result));
|
|
66
|
+
console.log();
|
|
61
67
|
} else {
|
|
62
68
|
addMessage(history, 'user', input);
|
|
63
|
-
console.log(
|
|
69
|
+
console.log(chalk.hex('#555555')('\u25c6 Thinking...'));
|
|
64
70
|
|
|
65
71
|
let fullResponse = '';
|
|
66
72
|
const stream = callProvider(config, [
|
|
67
|
-
{ role: 'system', content: 'You are CLARITY-AI, a helpful AI assistant.' },
|
|
73
|
+
{ role: 'system', content: 'You are CLARITY-AI, a helpful AI assistant. Keep responses short and direct.' },
|
|
68
74
|
...history.slice(-10).map(m => ({ role: m.role === 'assistant' ? 'assistant' : 'user', content: m.content })),
|
|
69
75
|
{ role: 'user', content: input },
|
|
70
76
|
]);
|
|
71
77
|
|
|
72
|
-
process.stdout.write(clr.ai('') + ' ');
|
|
73
78
|
for await (const chunk of stream) {
|
|
74
79
|
fullResponse += chunk;
|
|
75
|
-
process.stdout.write(chunk);
|
|
76
80
|
}
|
|
77
|
-
|
|
81
|
+
|
|
82
|
+
console.log();
|
|
83
|
+
console.log(renderAI(fullResponse));
|
|
84
|
+
console.log();
|
|
78
85
|
|
|
79
86
|
addMessage(history, 'assistant', fullResponse);
|
|
80
87
|
saveHistory(history);
|
|
@@ -84,7 +91,7 @@ export async function startChat(config) {
|
|
|
84
91
|
});
|
|
85
92
|
|
|
86
93
|
rl.on('SIGINT', () => {
|
|
87
|
-
console.log('\n' +
|
|
94
|
+
console.log('\n' + renderInfo('Session ended. Goodbye.'));
|
|
88
95
|
process.exit(0);
|
|
89
96
|
});
|
|
90
97
|
}
|
package/src/ui/blocks.js
CHANGED
|
@@ -1,65 +1,132 @@
|
|
|
1
|
-
// src/ui/blocks.js
|
|
2
1
|
import chalk from 'chalk';
|
|
3
2
|
import { clr } from './colors.js';
|
|
4
3
|
import stripAnsi from 'strip-ansi';
|
|
5
4
|
|
|
6
|
-
const W = process.stdout.columns || 80;
|
|
5
|
+
const W = () => process.stdout.columns || 80;
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
export function renderUser(text) {
|
|
8
|
+
const w = Math.min(W(), 80);
|
|
9
|
+
const lines = String(text).split('\n');
|
|
10
|
+
const out = [];
|
|
11
|
+
out.push(chalk.hex('#9B59FF').bold('\u276f YOU ') + chalk.hex('#9B59FF')('\u2500'.repeat(Math.max(0, w - 7))));
|
|
12
|
+
for (const line of lines) {
|
|
13
|
+
const vis = stripAnsi(line).length;
|
|
14
|
+
const pad = Math.max(0, w - vis - 1);
|
|
15
|
+
out.push(chalk.hex('#C39BD3').bgHex('#2D1B4E')(line + ' '.repeat(pad)));
|
|
16
|
+
}
|
|
17
|
+
return out.join('\n');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function renderAI(text) {
|
|
21
|
+
const w = Math.min(W(), 80);
|
|
22
|
+
const lines = String(text).split('\n');
|
|
23
|
+
const out = [];
|
|
24
|
+
out.push(chalk.hex('#9B59FF')('\u25c6 CLARITY ') + chalk.hex('#555555')('\u2500'.repeat(Math.max(0, w - 11))));
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
out.push(chalk.hex('#7B2FFF')('\u2502') + ' ' + chalk.white(line));
|
|
27
|
+
}
|
|
28
|
+
return out.join('\n');
|
|
29
|
+
}
|
|
14
30
|
|
|
15
|
-
function
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
export function renderEdit(filepath, hunks) {
|
|
32
|
+
const out = [];
|
|
33
|
+
const fname = filepath.split('/').pop();
|
|
34
|
+
out.push(
|
|
35
|
+
chalk.bgHex('#1A1A2E').hex('#74B9FF').bold(' \u270e ' + fname + ' ') +
|
|
36
|
+
chalk.dim(' ' + filepath)
|
|
37
|
+
);
|
|
38
|
+
out.push(chalk.hex('#333333')('\u2500'.repeat(Math.min(W(), 80))));
|
|
39
|
+
for (const h of hunks) {
|
|
40
|
+
if (h.type === 'add') {
|
|
41
|
+
out.push(
|
|
42
|
+
chalk.hex('#00FF9F')('+') +
|
|
43
|
+
chalk.dim(String(h.lineNum).padStart(4)) +
|
|
44
|
+
' ' +
|
|
45
|
+
chalk.hex('#00FF9F')(h.line)
|
|
46
|
+
);
|
|
47
|
+
} else if (h.type === 'remove') {
|
|
48
|
+
out.push(
|
|
49
|
+
chalk.hex('#FF4757')('-') +
|
|
50
|
+
chalk.dim(String(h.lineNum).padStart(4)) +
|
|
51
|
+
' ' +
|
|
52
|
+
chalk.hex('#FF4757').strikethrough(h.line)
|
|
53
|
+
);
|
|
54
|
+
} else {
|
|
55
|
+
out.push(
|
|
56
|
+
chalk.dim(' ') +
|
|
57
|
+
chalk.dim(String(h.lineNum).padStart(4)) +
|
|
58
|
+
' ' +
|
|
59
|
+
chalk.hex('#888888')(h.line)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return out.join('\n');
|
|
64
|
+
}
|
|
23
65
|
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
66
|
+
export function renderWrite(filepath, lineCount) {
|
|
67
|
+
const fname = filepath.split('/').pop();
|
|
68
|
+
return [
|
|
69
|
+
chalk.bgHex('#1A1A2E').hex('#00FF9F').bold(' + ' + fname + ' ') +
|
|
70
|
+
chalk.dim(' ' + filepath),
|
|
71
|
+
chalk.hex('#00FF9F')(' ' + lineCount + ' lines written'),
|
|
72
|
+
].join('\n');
|
|
73
|
+
}
|
|
27
74
|
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
75
|
+
export function renderTool(toolName, input, output) {
|
|
76
|
+
const out = [];
|
|
77
|
+
out.push(chalk.hex('#FFD700')('\u2699 ') + chalk.hex('#FFD700').bold(toolName) + chalk.dim(' \u00b7 ' + String(input).slice(0, 60)));
|
|
78
|
+
if (output) {
|
|
79
|
+
const lines = String(output).split('\n').slice(0, 8);
|
|
80
|
+
for (const l of lines) {
|
|
81
|
+
out.push(chalk.dim(' \u2502 ') + chalk.hex('#AAAAAA')(l));
|
|
82
|
+
}
|
|
83
|
+
if (String(output).split('\n').length > 8) {
|
|
84
|
+
out.push(chalk.dim(' \u2502 ...'));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return out.join('\n');
|
|
88
|
+
}
|
|
34
89
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
90
|
+
export function renderInfo(msg) {
|
|
91
|
+
return chalk.hex('#54A0FF')('\u2139 ') + chalk.hex('#54A0FF')(msg);
|
|
92
|
+
}
|
|
38
93
|
|
|
39
|
-
|
|
40
|
-
|
|
94
|
+
export function renderError(msg) {
|
|
95
|
+
return chalk.hex('#FF4757')('\u2716 ') + chalk.hex('#FF4757')(msg);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function renderSuccess(msg) {
|
|
99
|
+
return chalk.hex('#00FF9F')('\u2714 ') + chalk.hex('#00FF9F')(msg);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function renderWarning(msg) {
|
|
103
|
+
return chalk.hex('#FFB800')('\u26a0 ') + chalk.hex('#FFB800')(msg);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function divider() {
|
|
107
|
+
return chalk.hex('#333333')('\u2500'.repeat(Math.min(process.stdout.columns || 80, 80)));
|
|
108
|
+
}
|
|
41
109
|
|
|
42
|
-
|
|
110
|
+
export function renderPromptPrefix(provider, model) {
|
|
111
|
+
return (
|
|
112
|
+
chalk.hex('#00FFFF')('\u276f ') +
|
|
113
|
+
chalk.dim(provider + '/' + model) +
|
|
114
|
+
chalk.hex('#00FFFF')(' \u276f ')
|
|
115
|
+
);
|
|
43
116
|
}
|
|
44
117
|
|
|
45
|
-
// 12 named block types
|
|
46
118
|
export const blocks = {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
error: (msg
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
progress: (pct, label) => {
|
|
59
|
-
const filled = Math.round(pct / 100 * 30);
|
|
60
|
-
const bar = chalk.hex('#00FFFF')('█'.repeat(filled)) + chalk.gray('░'.repeat(30 - filled));
|
|
61
|
-
return `[${bar}] ${chalk.bold(pct + '%')} ${label || ''}`;
|
|
62
|
-
},
|
|
119
|
+
ai: renderAI,
|
|
120
|
+
user: renderUser,
|
|
121
|
+
info: (msg) => renderInfo(msg),
|
|
122
|
+
error: (msg) => renderError(msg),
|
|
123
|
+
success: (msg) => renderSuccess(msg),
|
|
124
|
+
warning: (msg) => renderWarning(msg),
|
|
125
|
+
divider,
|
|
126
|
+
edit: renderEdit,
|
|
127
|
+
write: renderWrite,
|
|
128
|
+
tool: renderTool,
|
|
129
|
+
task: (msg) => chalk.hex('#FFD700')('\u25b6 ') + chalk.hex('#FFD700')(msg),
|
|
63
130
|
};
|
|
64
131
|
|
|
65
132
|
export default blocks;
|
package/src/ui/input.js
CHANGED
|
@@ -1,60 +1,29 @@
|
|
|
1
1
|
import readline from 'readline';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import { clr } from './colors.js';
|
|
4
3
|
|
|
5
|
-
export function
|
|
4
|
+
export function createInput(config) {
|
|
6
5
|
const rl = readline.createInterface({
|
|
7
6
|
input: process.stdin,
|
|
8
7
|
output: process.stdout,
|
|
9
8
|
terminal: true,
|
|
10
|
-
historySize:
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const promptStr = clr.primary('❯ ') + clr.dim(config.provider + '/' + config.model) + clr.primary(' ❯ ');
|
|
14
|
-
|
|
15
|
-
rl.on('SIGINT', () => {
|
|
16
|
-
process.stdout.write('\n');
|
|
17
|
-
process.exit(0);
|
|
9
|
+
historySize: 200,
|
|
18
10
|
});
|
|
19
11
|
|
|
20
12
|
process.stdin.on('keypress', (str, key) => {
|
|
21
|
-
if (key.ctrl && key.name === '
|
|
22
|
-
process.stdout.write('\
|
|
23
|
-
process.
|
|
24
|
-
|
|
25
|
-
if (key.ctrl && key.name === 'l') {
|
|
26
|
-
console.clear();
|
|
27
|
-
return rl._refreshLine();
|
|
28
|
-
}
|
|
29
|
-
if (key.ctrl && key.name === 'u') {
|
|
30
|
-
const pos = rl.cursor;
|
|
31
|
-
rl.line = rl.line.slice(pos);
|
|
32
|
-
rl.cursor = 0;
|
|
33
|
-
return rl._refreshLine();
|
|
34
|
-
}
|
|
35
|
-
if (key.ctrl && key.name === 'a') {
|
|
36
|
-
rl.cursor = 0;
|
|
37
|
-
return rl._refreshLine();
|
|
38
|
-
}
|
|
39
|
-
if (key.ctrl && key.name === 'e') {
|
|
40
|
-
rl.cursor = rl.line.length;
|
|
41
|
-
return rl._refreshLine();
|
|
42
|
-
}
|
|
43
|
-
if (key.ctrl && key.name === 'k') {
|
|
44
|
-
rl.line = rl.line.slice(0, rl.cursor);
|
|
45
|
-
return rl._refreshLine();
|
|
46
|
-
}
|
|
47
|
-
if (key.ctrl && key.name === 'w') {
|
|
48
|
-
const before = rl.line.slice(0, rl.cursor);
|
|
49
|
-
const after = rl.line.slice(rl.cursor);
|
|
50
|
-
const trimmed = before.replace(/\S+\s*$/, '');
|
|
51
|
-
rl.line = trimmed + after;
|
|
52
|
-
rl.cursor = trimmed.length;
|
|
53
|
-
return rl._refreshLine();
|
|
13
|
+
if (key && key.ctrl && key.name === 'l') {
|
|
14
|
+
process.stdout.write('\x1Bc');
|
|
15
|
+
process.stdout.write(chalk.hex('#00FFFF').bold('\n CLARITY v3.0\n\n'));
|
|
16
|
+
rl.prompt();
|
|
54
17
|
}
|
|
55
18
|
});
|
|
56
19
|
|
|
57
|
-
rl.setPrompt(promptStr);
|
|
58
|
-
|
|
59
20
|
return rl;
|
|
60
21
|
}
|
|
22
|
+
|
|
23
|
+
export function getPromptString(provider, model) {
|
|
24
|
+
return (
|
|
25
|
+
chalk.hex('#00FFFF')('\u276f ') +
|
|
26
|
+
chalk.dim(provider + '/' + model) +
|
|
27
|
+
chalk.hex('#00FFFF')(' \u276f ')
|
|
28
|
+
);
|
|
29
|
+
}
|