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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-ai",
3
- "version": "3.0.2",
3
+ "version": "3.1.0",
4
4
  "description": "Autonomous AI Agent CLI for Termux",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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 TOOLS_DESC = `You are CLARITY, an autonomous AI agent. You have access to these tools:
6
- - bash <command>: Execute shell commands
7
- - read_file <path>: Read file contents
8
- - write_file <path> <content>: Create/overwrite files
9
- - edit_file <path> <old> <new>: Replace text in files
10
- - delete_file <path>: Delete files
11
- - list_dir <dir>: List directory contents
12
- - search_files <pattern>: Find files by glob
13
- - grep <pattern> <path>: Search file contents
14
- - web_search <query>: Search the web
15
- - web_fetch <url>: Fetch URL content
16
- - git <subcommand>: Git operations
17
- - memory <read/write/search/clear>: Persistent memory
18
- - pkg_manager <npm/pip> <install> <pkg>: Install packages
19
- - code_runner <lang> <code>: Execute code snippets
20
-
21
- Respond with JSON:
22
- {"tool": "tool_name", "args": {"arg1": "val1"}}
23
- or to respond to user:
24
- {"response": "your final answer here"}`;
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(TOOLS_DESC) },
29
- ...history.slice(-20).map(m => ({ role: m.role === 'assistant' ? 'assistant' : 'user', content: m.content })),
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
- try {
45
- const jsonMatch = fullResponse.match(/\{[^]*\}/);
46
- if (!jsonMatch) {
47
- finalResponse = fullResponse;
48
- break;
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
- messages.push({ role: 'assistant', content: fullResponse });
72
- messages.push({ role: 'user', content: `Tool result: ${result}` });
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 { clr } from './ui/colors.js';
3
- import { blocks } from './ui/blocks.js';
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(blocks.info(
13
- 'CLARITY-AI v' + config.version + ' interactive session started.\n' +
14
- 'Type /help for commands. Ctrl+C to exit.\n' +
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: 100,
23
+ historySize: 200,
25
24
  });
26
25
 
27
26
  function showPrompt() {
28
- const pre = clr.primary('❯ ') + clr.dim(config.provider + '/' + config.model) + clr.primary(' ❯ ');
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('\n' + blocks.user(input) + '\n');
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('\n' + blocks.ai(result) + '\n');
64
+ console.log();
65
+ console.log(renderAI(result));
66
+ console.log();
61
67
  } else {
62
68
  addMessage(history, 'user', input);
63
- console.log(clr.dim(' Thinking...'));
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
- console.log('\n');
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' + blocks.info('Session ended. Goodbye.', '◆ EXIT'));
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
- const BORDERS = {
9
- round: { tl:'╭', tr:'╮', bl:'╰', br:'╯', h:'─', v:'│' },
10
- sharp: { tl:'┌', tr:'┐', bl:'', br:'┘', h:'─', v:'│' },
11
- double: { tl:'╔', tr:'╗', bl:'╚', br:'╝', h:'═', v:'║' },
12
- thick: { tl:'', tr:'▜', bl:'▙', br:'', h:'', v:'█' },
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 box(content, opts = {}) {
16
- const {
17
- title = '',
18
- style = 'round',
19
- color = '#00FFFF',
20
- width = Math.min(W - 2, 78),
21
- padding = 1,
22
- } = opts;
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
- const b = BORDERS[style] || BORDERS.round;
25
- const colorFn = (t) => chalk.hex(color)(t);
26
- const inner = width - 2;
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
- const lines = String(content).split('\n');
29
- const padded = lines.map(l => {
30
- const vis = stripAnsi(l).length;
31
- const pad = Math.max(0, inner - padding * 2 - vis);
32
- return colorFn(b.v) + ' '.repeat(padding) + l + ' '.repeat(pad) + colorFn(b.v);
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
- const titleStr = title
36
- ? colorFn(b.h).repeat(2) + ' ' + chalk.bold(title) + ' ' + colorFn(b.h).repeat(Math.max(0, inner - title.length - 4))
37
- : colorFn(b.h).repeat(inner);
90
+ export function renderInfo(msg) {
91
+ return chalk.hex('#54A0FF')('\u2139 ') + chalk.hex('#54A0FF')(msg);
92
+ }
38
93
 
39
- const top = colorFn(b.tl) + titleStr + colorFn(b.tr);
40
- const bottom = colorFn(b.bl) + colorFn(b.h).repeat(inner) + colorFn(b.br);
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
- return [top, ...padded, bottom].join('\n');
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
- info: (msg, title) => box(clr.info(msg), { title: title || 'ℹ INFO', color: '#54A0FF', style: 'round' }),
48
- success: (msg, title) => box(clr.success(msg), { title: title || '✔ SUCCESS', color: '#00FF9F', style: 'round' }),
49
- warning: (msg, title) => box(clr.warning(msg), { title: title || '⚠ WARNING', color: '#FFB800', style: 'sharp' }),
50
- error: (msg, title) => box(clr.error(msg), { title: title || '✖ ERROR', color: '#FF4757', style: 'sharp' }),
51
- code: (msg, title) => box(chalk.hex('#E8E8E8')(msg), { title: title || '◆ CODE', color: '#9B59FF', style: 'sharp' }),
52
- file: (msg, title) => box(clr.filepath(msg),{ title: title || '📄 FILE', color: '#74B9FF', style: 'round' }),
53
- ai: (msg, title) => box(clr.ai(msg), { title: title || '◆ CLARITY', color: '#9B59FF', style: 'round' }),
54
- user: (msg, title) => box(clr.user(msg), { title: title || '❯ YOU', color: '#00FFFF', style: 'round' }),
55
- tool: (msg, title) => box(clr.tool(msg), { title: title || '⚙ TOOL', color: '#00FF9F', style: 'double' }),
56
- task: (msg, title) => box(clr.gold(msg), { title: title || '▶ TASK', color: '#FFD700', style: 'double' }),
57
- divider: () => clr.muted(''.repeat(Math.min(W, 80))),
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 createPrompt(config) {
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: 100,
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 === 'd') {
22
- process.stdout.write('\n');
23
- process.exit(0);
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
+ }