clarity-ai 3.0.1 → 3.0.2

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.
Files changed (115) hide show
  1. package/bin/clarity.js +23 -1
  2. package/package.json +21 -15
  3. package/scripts/postinstall.js +13 -8
  4. package/src/agents/code-agent.js +18 -0
  5. package/src/agents/file-agent.js +24 -0
  6. package/src/agents/git-agent.js +27 -0
  7. package/src/agents/loop.js +85 -0
  8. package/src/agents/monitor-agent.js +22 -0
  9. package/src/agents/planner.js +21 -0
  10. package/src/agents/shell-agent.js +11 -0
  11. package/src/agents/web-agent.js +16 -0
  12. package/src/commands/agent.js +14 -0
  13. package/src/commands/chat.js +6 -102
  14. package/src/commands/clear.js +5 -0
  15. package/src/commands/config.js +15 -142
  16. package/src/commands/diff.js +12 -0
  17. package/src/commands/exit.js +5 -0
  18. package/src/commands/export.js +12 -0
  19. package/src/commands/fetch.js +16 -0
  20. package/src/commands/git.js +12 -0
  21. package/src/commands/help.js +32 -0
  22. package/src/commands/history.js +13 -0
  23. package/src/commands/index.js +51 -134
  24. package/src/commands/keys.js +30 -0
  25. package/src/commands/memory.js +23 -0
  26. package/src/commands/model.js +10 -92
  27. package/src/commands/provider.js +26 -0
  28. package/src/commands/run.js +16 -0
  29. package/src/commands/search.js +13 -0
  30. package/src/commands/task.js +24 -0
  31. package/src/commands/tools.js +43 -0
  32. package/src/commands/undo.js +10 -0
  33. package/src/config/settings.js +31 -24
  34. package/src/core/context.js +39 -0
  35. package/src/core/history.js +27 -0
  36. package/src/core/memory.js +40 -0
  37. package/src/core/setup.js +87 -0
  38. package/src/main.js +67 -98
  39. package/src/providers/index.js +29 -13
  40. package/src/tools/agent-spawn.js +13 -0
  41. package/src/tools/bash.js +9 -0
  42. package/src/tools/clipboard-tool.js +14 -0
  43. package/src/tools/code-runner.js +17 -0
  44. package/src/tools/compress-tool.js +21 -0
  45. package/src/tools/context-tool.js +16 -0
  46. package/src/tools/delete-file.js +11 -0
  47. package/src/tools/diff-tool.js +19 -0
  48. package/src/tools/edit-file.js +13 -0
  49. package/src/tools/env-tool.js +19 -0
  50. package/src/tools/git-tool.js +9 -0
  51. package/src/tools/grep.js +13 -0
  52. package/src/tools/list-dir.js +20 -0
  53. package/src/tools/memory-tool.js +18 -0
  54. package/src/tools/notify-tool.js +9 -0
  55. package/src/tools/pkg-manager.js +19 -0
  56. package/src/tools/read-file.js +9 -0
  57. package/src/tools/run-tests.js +10 -0
  58. package/src/tools/screenshot-tool.js +9 -0
  59. package/src/tools/search-files.js +10 -0
  60. package/src/tools/task-planner.js +11 -0
  61. package/src/tools/version-check.js +12 -0
  62. package/src/tools/web-fetch.js +15 -0
  63. package/src/tools/web-search.js +18 -0
  64. package/src/tools/write-file.js +12 -0
  65. package/src/ui/banner.js +45 -0
  66. package/src/ui/blocks.js +65 -0
  67. package/src/ui/colors.js +41 -0
  68. package/src/ui/input.js +60 -0
  69. package/src/ui/spinner.js +23 -47
  70. package/src/agent/intent.js +0 -9
  71. package/src/agent/loop.js +0 -105
  72. package/src/agent/planner.js +0 -37
  73. package/src/agent/runner.js +0 -143
  74. package/src/agent/subagent.js +0 -55
  75. package/src/agent/tools.js +0 -132
  76. package/src/agents/BaseAgent.js +0 -54
  77. package/src/agents/CodeAgent.js +0 -224
  78. package/src/agents/FileAgent.js +0 -85
  79. package/src/agents/MonitorAgent.js +0 -104
  80. package/src/agents/ShellAgent.js +0 -91
  81. package/src/agents/WebAgent.js +0 -89
  82. package/src/agents/manager.js +0 -127
  83. package/src/app.js +0 -88
  84. package/src/banner.js +0 -14
  85. package/src/blocks.js +0 -100
  86. package/src/colors.js +0 -31
  87. package/src/commands/files.js +0 -106
  88. package/src/commands/system.js +0 -131
  89. package/src/commands/utility.js +0 -332
  90. package/src/commands/web.js +0 -82
  91. package/src/components/AIMessage.js +0 -21
  92. package/src/components/AgentTree.js +0 -42
  93. package/src/components/Banner.js +0 -15
  94. package/src/components/InputBar.js +0 -40
  95. package/src/components/MessageList.js +0 -23
  96. package/src/components/Spinner.js +0 -13
  97. package/src/components/StatusBar.js +0 -24
  98. package/src/components/ThoughtBox.js +0 -23
  99. package/src/components/TodoTree.js +0 -31
  100. package/src/components/ToolCall.js +0 -69
  101. package/src/components/UserMessage.js +0 -9
  102. package/src/config/keys.js +0 -104
  103. package/src/config/paths.js +0 -22
  104. package/src/config.js +0 -29
  105. package/src/history.js +0 -54
  106. package/src/index.js +0 -43
  107. package/src/input.js +0 -36
  108. package/src/memory/context.js +0 -38
  109. package/src/memory/store.js +0 -54
  110. package/src/setup.js +0 -137
  111. package/src/store.js +0 -68
  112. package/src/utils/logger.js +0 -40
  113. package/src/utils/markdown.js +0 -25
  114. package/src/utils/termux.js +0 -38
  115. package/src/utils/version-check.js +0 -76
package/bin/clarity.js CHANGED
@@ -1,2 +1,24 @@
1
1
  #!/usr/bin/env node
2
- import '../src/index.js';
2
+ // bin/clarity.js
3
+ import { showBanner } from '../src/ui/banner.js';
4
+ import { isFirstRun, loadConfig } from '../src/config/settings.js';
5
+ import { runSetupWizard } from '../src/core/setup.js';
6
+ import { startChat } from '../src/main.js';
7
+
8
+ async function main() {
9
+ const firstRun = isFirstRun();
10
+ let config = firstRun ? {} : loadConfig();
11
+
12
+ await showBanner(config.version, config.provider, config.model);
13
+
14
+ if (firstRun) {
15
+ config = await runSetupWizard();
16
+ }
17
+
18
+ await startChat(config);
19
+ }
20
+
21
+ main().catch(err => {
22
+ console.error('\x1b[31mFatal error:\x1b[0m', err.message);
23
+ process.exit(1);
24
+ });
package/package.json CHANGED
@@ -1,25 +1,31 @@
1
1
  {
2
2
  "name": "clarity-ai",
3
- "version": "3.0.1",
4
- "description": "Autonomous AI Agent Terminal — OpenCode-style TUI for Termux",
3
+ "version": "3.0.2",
4
+ "description": "Autonomous AI Agent CLI for Termux",
5
5
  "type": "module",
6
- "bin": { "clarity": "./bin/clarity.js" },
6
+ "bin": {
7
+ "clarity": "bin/clarity.js"
8
+ },
7
9
  "scripts": {
8
10
  "start": "node bin/clarity.js",
9
11
  "dev": "node --watch bin/clarity.js"
10
12
  },
13
+ "engines": {
14
+ "node": ">=18.0.0"
15
+ },
11
16
  "dependencies": {
12
- "ink": "^4.4.1",
13
- "react": "^18.2.0",
14
17
  "chalk": "^5.3.0",
15
- "figures": "^6.1.0",
16
- "ora": "^8.0.1",
17
- "dotenv": "^16.4.5",
18
- "node-fetch": "^3.3.2",
19
- "strip-ansi": "^7.1.0",
20
- "wrap-ansi": "^9.0.0",
21
- "cli-truncate": "^4.0.0",
22
- "ink-text-input": "^5.0.1"
23
- },
24
- "engines": { "node": ">=18.0.0" }
18
+ "figlet": "^1.7.0",
19
+ "gradient-string": "^2.0.2",
20
+ "ora": "^8.1.0",
21
+ "inquirer": "^9.2.12",
22
+ "cli-table3": "^0.6.3",
23
+ "glob": "^10.3.10",
24
+ "chokidar": "^3.5.3",
25
+ "marked": "^9.1.6",
26
+ "marked-terminal": "^6.1.0",
27
+ "diff": "^5.1.0",
28
+ "archiver": "^6.0.1",
29
+ "strip-ansi": "^7.1.0"
30
+ }
25
31
  }
@@ -40,15 +40,20 @@ const binDir = process.env.npm_config_prefix ? `${process.env.npm_config_prefix}
40
40
  if (isTermux && binDir) {
41
41
  const { existsSync, writeFileSync, chmodSync, unlinkSync, lstatSync } = await import('fs');
42
42
  const { resolve } = await import('path');
43
- const wrapperPath = resolve(binDir, 'clarity');
43
+ const symlinkPath = resolve(binDir, 'clarity');
44
44
 
45
45
  try {
46
- const stat = lstatSync(wrapperPath);
47
- if (stat.isSymbolicLink()) {
48
- unlinkSync(wrapperPath);
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`);
50
- chmodSync(wrapperPath, '755');
51
- console.log(chalk.hex('#00ff88')(' ✓ Termux wrapper created at:'), chalk.hex('#00d2ff')(wrapperPath));
46
+ if (existsSync(symlinkPath)) {
47
+ const stat = lstatSync(symlinkPath);
48
+ if (stat.isSymbolicLink()) {
49
+ unlinkSync(symlinkPath);
50
+ }
52
51
  }
53
- } catch {}
52
+ const clarityPkgDir = process.env.PREFIX ? `${process.env.PREFIX}/lib/node_modules/clarity-ai` : resolve(process.cwd());
53
+ writeFileSync(symlinkPath, `#!/data/data/com.termux/files/usr/bin/bash\nexec node "${clarityPkgDir}/bin/clarity.js" "$@"\n`);
54
+ chmodSync(symlinkPath, '755');
55
+ console.log(chalk.hex('#00ff88')(' ✓ Termux wrapper created at:'), chalk.hex('#00d2ff')(symlinkPath));
56
+ } catch (e) {
57
+ console.error(chalk.hex('#ff4466')(' ✗ Failed to create Termux wrapper:'), e.message);
58
+ }
54
59
  }
@@ -0,0 +1,18 @@
1
+ import { execSync } from 'child_process';
2
+ import { writeFileSync, unlinkSync } from 'fs';
3
+
4
+ export async function codeAgent({ language, code, test }) {
5
+ const ext = language === 'python' ? 'py' : language === 'javascript' ? 'js' : 'sh';
6
+ const tmpFile = `/tmp/clarity-agent.${ext}`;
7
+ try {
8
+ writeFileSync(tmpFile, code + (test ? `\n${test}` : ''), 'utf8');
9
+ const cmd = language === 'python' ? `python3 ${tmpFile}` :
10
+ language === 'javascript' ? `node ${tmpFile}` : `bash ${tmpFile}`;
11
+ const out = execSync(cmd, { encoding: 'utf8', timeout: 15000 });
12
+ return out || '(no output)';
13
+ } catch (err) {
14
+ return err.stdout || err.stderr || err.message;
15
+ } finally {
16
+ try { unlinkSync(tmpFile); } catch {}
17
+ }
18
+ }
@@ -0,0 +1,24 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync } from 'fs';
2
+ import { dirname, resolve } from 'path';
3
+
4
+ export async function fileAgent({ action, path, content, pattern }) {
5
+ const abs = resolve(path || '.');
6
+ switch (action) {
7
+ case 'read':
8
+ if (!existsSync(abs)) return `Error: Not found: ${abs}`;
9
+ return readFileSync(abs, 'utf8');
10
+ case 'write':
11
+ if (!existsSync(dirname(abs))) mkdirSync(dirname(abs), { recursive: true });
12
+ writeFileSync(abs, content || '', 'utf8');
13
+ return `Written: ${abs}`;
14
+ case 'delete':
15
+ if (!existsSync(abs)) return `Error: Not found: ${abs}`;
16
+ unlinkSync(abs);
17
+ return `Deleted: ${abs}`;
18
+ case 'list':
19
+ if (!existsSync(abs)) return `Error: Not found: ${abs}`;
20
+ return readdirSync(abs).join('\n');
21
+ default:
22
+ return 'Usage: file <read/write/delete/list> <path> [content]';
23
+ }
24
+ }
@@ -0,0 +1,27 @@
1
+ import { execSync } from 'child_process';
2
+
3
+ export async function gitAgent({ action, message, branch }) {
4
+ try {
5
+ switch (action) {
6
+ case 'status':
7
+ return execSync('git status', { encoding: 'utf8' });
8
+ case 'log':
9
+ return execSync('git log --oneline -10', { encoding: 'utf8' });
10
+ case 'commit':
11
+ execSync('git add .', { encoding: 'utf8' });
12
+ return execSync(`git commit -m "${message || 'update'}"`, { encoding: 'utf8' });
13
+ case 'push':
14
+ return execSync('git push', { encoding: 'utf8', timeout: 30000 });
15
+ case 'pull':
16
+ return execSync('git pull', { encoding: 'utf8', timeout: 30000 });
17
+ case 'branch':
18
+ return execSync(`git branch ${branch || ''}`, { encoding: 'utf8' });
19
+ case 'diff':
20
+ return execSync('git diff', { encoding: 'utf8' });
21
+ default:
22
+ return 'Usage: git <status|log|commit|push|pull|branch|diff> [args]';
23
+ }
24
+ } catch (err) {
25
+ return err.stderr || err.message;
26
+ }
27
+ }
@@ -0,0 +1,85 @@
1
+ import { callProvider } from '../providers/index.js';
2
+ import { buildSystemPrompt } from '../core/context.js';
3
+ import { addMessage } from '../core/history.js';
4
+
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"}`;
25
+
26
+ export async function agentLoop(userMessage, config, history) {
27
+ const messages = [
28
+ { role: 'system', content: buildSystemPrompt(TOOLS_DESC) },
29
+ ...history.slice(-20).map(m => ({ role: m.role === 'assistant' ? 'assistant' : 'user', content: m.content })),
30
+ { role: 'user', content: userMessage },
31
+ ];
32
+
33
+ let finalResponse = '';
34
+ let toolCalls = 0;
35
+ const maxToolCalls = 20;
36
+
37
+ while (toolCalls < maxToolCalls) {
38
+ let fullResponse = '';
39
+ const stream = callProvider(config, messages);
40
+ for await (const chunk of stream) {
41
+ fullResponse += chunk;
42
+ }
43
+
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
+ }
70
+
71
+ messages.push({ role: 'assistant', content: fullResponse });
72
+ messages.push({ role: 'user', content: `Tool result: ${result}` });
73
+ }
74
+ } catch {
75
+ finalResponse = fullResponse;
76
+ break;
77
+ }
78
+ }
79
+
80
+ if (toolCalls >= maxToolCalls) {
81
+ finalResponse += '\n\n(Max tool calls reached)';
82
+ }
83
+
84
+ return finalResponse || 'No response generated';
85
+ }
@@ -0,0 +1,22 @@
1
+ import { watch } from 'fs';
2
+ import { resolve } from 'path';
3
+
4
+ let watchers = [];
5
+
6
+ export async function monitorAgent({ action, path, event }) {
7
+ if (action === 'watch') {
8
+ const abs = resolve(path || '.');
9
+ const watcher = watch(abs, { recursive: true }, (eventType, filename) => {
10
+ console.log(`[monitor] ${eventType}: ${filename}`);
11
+ });
12
+ watchers.push(watcher);
13
+ return `Watching: ${abs}`;
14
+ } else if (action === 'stop') {
15
+ for (const w of watchers) w.close();
16
+ watchers = [];
17
+ return 'Monitoring stopped';
18
+ } else if (action === 'status') {
19
+ return `Active watchers: ${watchers.length}`;
20
+ }
21
+ return 'Usage: monitor <watch|stop|status> [path]';
22
+ }
@@ -0,0 +1,21 @@
1
+ import { callProvider } from '../providers/index.js';
2
+
3
+ export async function planTask(userMessage, config) {
4
+ const messages = [
5
+ { role: 'system', content: 'You are a task planner. Break down the user\'s goal into a numbered step-by-step plan. Be specific and actionable. Respond with a JSON array of steps.' },
6
+ { role: 'user', content: userMessage },
7
+ ];
8
+
9
+ let response = '';
10
+ const stream = callProvider(config, messages);
11
+ for await (const chunk of stream) {
12
+ response += chunk;
13
+ }
14
+
15
+ try {
16
+ const jsonMatch = response.match(/\[[^]*\]/);
17
+ if (jsonMatch) return JSON.parse(jsonMatch[0]);
18
+ } catch {}
19
+
20
+ return response.split('\n').filter(l => /^\d+\./.test(l)).map((l, i) => ({ step: i + 1, task: l.replace(/^\d+\.\s*/, '') }));
21
+ }
@@ -0,0 +1,11 @@
1
+ import { execSync } from 'child_process';
2
+
3
+ export async function shellAgent({ command, cwd, timeout }) {
4
+ if (!command) return 'No command specified';
5
+ try {
6
+ const out = execSync(command, { encoding: 'utf8', timeout: timeout || 30000, cwd: cwd || process.cwd() });
7
+ return out || '(no output)';
8
+ } catch (err) {
9
+ return err.stderr || err.message;
10
+ }
11
+ }
@@ -0,0 +1,16 @@
1
+ export async function webAgent({ action, url, query }) {
2
+ try {
3
+ if (action === 'search') {
4
+ const res = await fetch(`https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json&no_html=1`);
5
+ const data = await res.json();
6
+ return data.AbstractText || JSON.stringify(data.RelatedTopics?.slice(0, 5) || 'No results');
7
+ } else if (action === 'fetch') {
8
+ const res = await fetch(url);
9
+ const html = await res.text();
10
+ return html.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim().substring(0, 5000);
11
+ }
12
+ return 'Usage: web <search|fetch> <query|url>';
13
+ } catch (err) {
14
+ return `Error: ${err.message}`;
15
+ }
16
+ }
@@ -0,0 +1,14 @@
1
+ import { clr } from '../ui/colors.js';
2
+ export async function agentCommand(args, config) {
3
+ const mode = args[0];
4
+ if (mode === 'on' || mode === 'true') {
5
+ config.agentMode = true;
6
+ console.log(clr.success('Agent mode: ON'));
7
+ } else if (mode === 'off' || mode === 'false') {
8
+ config.agentMode = false;
9
+ console.log(clr.warning('Agent mode: OFF'));
10
+ } else {
11
+ console.log(clr.info(`Agent mode: ${config.agentMode ? 'ON' : 'OFF'}`));
12
+ }
13
+ return config;
14
+ }
@@ -1,104 +1,8 @@
1
- import blocks from '../blocks.js';
2
- import c from '../colors.js';
3
- import memStore from '../memory/store.js';
4
- import { writeFileSync } from 'fs';
5
- import { resolve } from 'path';
6
-
7
- const { info, success, warn, error: errorBox, table, divider } = blocks;
8
-
9
- export async function clear(args, context) {
10
- console.clear();
11
- success('Cleared', 'Screen and context cleared.');
12
- }
13
-
14
- export async function history(args, context) {
15
- const sub = args[0];
16
- if (!sub) { warn('Usage', '/history show|clear|export [n|file]'); return; }
17
-
18
- switch (sub) {
19
- case 'show': {
20
- const n = args[1] ? parseInt(args[1]) : 20;
21
- const ctx = memStore.show();
22
- const recent = ctx.slice(-n);
23
- if (recent.length === 0) { info('History', 'No history.'); return; }
24
- recent.forEach(m => console.log(`${c.muted(m.role)}: ${m.content.slice(0, 150)}`));
25
- break;
26
- }
27
- case 'clear': {
28
- memStore.clear();
29
- success('Cleared', 'History cleared.');
30
- break;
31
- }
32
- case 'export': {
33
- const file = args[1] || `clarity-chat-${Date.now()}.md`;
34
- const fullPath = resolve(file);
35
- const ctx = memStore.show();
36
- const md = ctx.map(m => `**${m.role}**: ${m.content}`).join('\n\n');
37
- writeFileSync(fullPath, `# CLARITY Chat Export\n\n${md}`, 'utf8');
38
- success('Exported', c.filename(fullPath));
39
- break;
40
- }
41
- default:
42
- warn('Usage', '/history show|clear|export');
43
- }
44
- }
45
-
46
- export async function retry(args, context) {
47
- const ctx = memStore.show();
48
- const lastUser = ctx.filter(m => m.role === 'user').pop();
49
- if (!lastUser) { warn('Retry', 'No previous user message to retry.'); return; }
50
- info('Retry', `Retrying: "${lastUser.content.slice(0, 80)}..."`);
51
- }
52
-
53
- export async function copy(args, context) {
54
- const ctx = memStore.show();
55
- const last = ctx.filter(m => m.role === 'assistant').pop();
56
- if (!last) { warn('Copy', 'No AI response to copy.'); return; }
57
- try {
58
- if (process.env.PREFIX?.includes('com.termux') || process.env.TERMUX_VERSION) {
59
- const { execSync } = await import('child_process');
60
- execSync(`termux-clipboard-set "${last.content.replace(/"/g, '\\"')}"`);
61
- } else {
62
- const { default: clipboard } = await import('clipboardy');
63
- clipboard.writeSync(last.content);
64
- }
65
- success('Copied', 'Last AI response copied to clipboard.');
66
- } catch {
67
- info('Copy', last.content.slice(0, 500));
68
- }
69
- }
70
-
71
- export async function ask(args, context) {
72
- const question = args.join(' ');
73
- if (!question) { warn('Usage', '/ask <question>'); return; }
74
- blocks.user(question);
75
- info('Ask', 'Configure API keys with /init first to get AI responses.');
76
- }
77
-
78
- export async function memory(args, context) {
79
- const sub = args[0];
80
- if (!sub) { warn('Usage', '/memory show|add|clear'); return; }
81
-
82
- switch (sub) {
83
- case 'show': {
84
- const ctx = memStore.show();
85
- if (ctx.length === 0) { info('Memory', 'No context stored.'); return; }
86
- ctx.forEach(m => console.log(`${c.muted(m.role)}: ${m.content.slice(0, 200)}`));
87
- break;
88
- }
89
- case 'add': {
90
- const note = args.slice(1).join(' ');
91
- if (!note) { warn('Usage', '/memory add <note>'); return; }
92
- memStore.addNote(note);
93
- success('Memory Added', note);
94
- break;
95
- }
96
- case 'clear': {
97
- memStore.clear();
98
- success('Cleared', 'Memory cleared.');
99
- break;
100
- }
101
- default:
102
- warn('Usage', '/memory show|add|clear');
1
+ import { blocks } from '../ui/blocks.js';
2
+ export async function chatCommand(args, config) {
3
+ if (args[0] === 'new') {
4
+ console.log(blocks.info('New chat session started.', '◆ CHAT'));
5
+ return { ...config, chatHistory: [] };
103
6
  }
7
+ console.log(blocks.info('Chat mode active. Provider: ' + config.provider + ', Model: ' + config.model, '◆ CHAT'));
104
8
  }
@@ -0,0 +1,5 @@
1
+ export async function clearCommand(args, config) {
2
+ process.stdout.write('\x1Bc');
3
+ const { showBanner } = await import('../ui/banner.js');
4
+ await showBanner(config.version, config.provider, config.model);
5
+ }
@@ -1,146 +1,19 @@
1
- import blocks from '../blocks.js';
2
- import c from '../colors.js';
3
- import settings from '../config/settings.js';
4
- import { setKey, getKey, listKeys, removeKey, testKey, hasAnyKeys, PROVIDER_NAMES, PROVIDER_REGEX } from '../config/keys.js';
5
- import { listModels, getPriorityProviders } from '../providers/index.js';
6
-
7
- const { info, success, warn, error: errorBox, table, divider } = blocks;
8
-
9
- export async function keys(args, context) {
10
- const sub = args[0];
11
- if (!sub) { warn('Usage', '/keys set|list|remove|test <provider> [key]'); return; }
12
-
13
- switch (sub) {
14
- case 'set': {
15
- if (args.length < 3) { warn('Usage', '/keys set <provider> <key>'); return; }
16
- try {
17
- setKey(args[1], args.slice(2).join(' '));
18
- success('Key Saved', `${PROVIDER_NAMES[args[1]] || args[1]} API key configured.`);
19
- } catch (err) { errorBox('Error', err.message); }
20
- break;
21
- }
22
- case 'list': {
23
- const k = listKeys();
24
- if (Object.keys(k).length === 0) { info('No Keys', 'No API keys configured. Run /init to set up.'); return; }
25
- const rows = Object.entries(k).map(([p, info]) => [PROVIDER_NAMES[p] || p, info.keyPreview]);
26
- table(['Provider', 'Key'], rows);
27
- break;
28
- }
29
- case 'remove': {
30
- if (args.length < 2) { warn('Usage', '/keys remove <provider>'); return; }
31
- removeKey(args[1]);
32
- success('Removed', `${PROVIDER_NAMES[args[1]] || args[1]} key removed.`);
33
- break;
34
- }
35
- case 'test': {
36
- if (args.length < 2) { warn('Usage', '/keys test <provider>'); return; }
37
- const result = await testKey(args[1]);
38
- if (result.success) success('Key Valid', `${PROVIDER_NAMES[args[1]] || args[1]} key is working!`);
39
- else errorBox('Key Invalid', `${PROVIDER_NAMES[args[1]] || args[1]}: ${result.error}`);
40
- break;
41
- }
42
- default:
43
- warn('Usage', '/keys set|list|remove|test');
44
- }
45
- }
46
-
47
- export async function key(args, context) {
48
- if (args.length < 2) { warn('Usage', '/key <provider> <key>'); return; }
49
- try {
50
- setKey(args[0], args.slice(1).join(' '));
51
- success('Key Saved', `${PROVIDER_NAMES[args[0]] || args[0]} API key configured.`);
52
- } catch (err) { errorBox('Error', err.message); }
53
- }
54
-
55
- export async function config(args, context) {
56
- const sub = args[0];
57
- if (!sub) { warn('Usage', '/config show|reset [key] [value]'); return; }
58
-
59
- switch (sub) {
60
- case 'show': {
61
- const all = settings.store;
62
- const rows = Object.entries(all).map(([k, v]) => [k, String(v)]);
63
- table(['Setting', 'Value'], rows);
64
- break;
65
- }
66
- case 'reset': {
67
- settings.clear();
68
- success('Reset', 'Settings reset to defaults.');
69
- break;
70
- }
71
- default:
72
- warn('Usage', '/config show|reset');
73
- }
74
- }
75
-
76
- export async function set(args, context) {
77
- if (args.length < 2) { warn('Usage', '/set <key> <value>'); return; }
78
- const val = args.slice(1).join(' ');
79
- const num = parseFloat(val);
80
- settings.set(args[0], isNaN(num) ? val : num);
81
- success('Set', `${args[0]} = ${settings.get(args[0])}`);
82
- }
83
-
84
- export async function reset(args, context) {
85
- settings.clear();
86
- success('Reset', 'All settings reset to defaults.');
87
- }
88
-
89
- export async function init(args, context) {
90
- const { default: inquirer } = await import('inquirer');
91
- divider('CLARITY Setup');
92
- info('Welcome', "Let's configure your AI agent CLI\nPress Ctrl+C to cancel at any time.");
93
-
94
- const { providers: selected } = await inquirer.prompt([{
95
- type: 'checkbox',
96
- name: 'providers',
97
- message: 'Which AI providers do you want to configure?',
98
- choices: [
99
- { name: 'Groq (FREE \u2014 Recommended)', value: 'groq', checked: true },
100
- { name: 'Google Gemini (FREE)', value: 'gemini', checked: true },
101
- { name: 'OpenRouter (FREE models)', value: 'openrouter' },
102
- { name: 'Anthropic Claude', value: 'anthropic' },
103
- { name: 'OpenAI GPT', value: 'openai' },
104
- { name: 'DeepSeek', value: 'deepseek' },
105
- ]
106
- }]);
107
-
108
- for (const p of selected) {
109
- const { key: apiKey } = await inquirer.prompt([{
110
- type: 'password',
111
- name: 'key',
112
- message: `Enter your ${PROVIDER_NAMES[p] || p} API key:`,
113
- validate: v => v.length > 0 || 'Key is required',
114
- }]);
115
- try {
116
- setKey(p, apiKey);
117
- success(p, `Key saved for ${PROVIDER_NAMES[p] || p}`);
118
- } catch (err) {
119
- errorBox(p, err.message);
1
+ import { loadConfig, saveConfig } from '../config/settings.js';
2
+ import { clr } from '../ui/colors.js';
3
+ export async function configCommand(args, config) {
4
+ if (args.length === 0) {
5
+ const cfg = loadConfig();
6
+ for (const [key, val] of Object.entries(cfg || config)) {
7
+ if (key === 'apiKeys') continue;
8
+ console.log(' ' + clr.secondary(key) + ': ' + clr.dim(JSON.stringify(val)));
120
9
  }
10
+ return;
121
11
  }
122
-
123
- const configured = Object.keys(listKeys());
124
- if (configured.length > 0) {
125
- const defaultProvider = getPriorityProviders(configured)[0] || configured[0];
126
- const models = listModels(defaultProvider);
127
- const { model: defaultModel } = await inquirer.prompt([{
128
- type: 'list',
129
- name: 'model',
130
- message: 'Select your default model:',
131
- choices: models.map(m => ({ name: `${defaultProvider}/${m}`, value: `${defaultProvider}/${m}` })),
132
- }]);
133
- settings.set('defaultModel', defaultModel);
134
-
135
- const { stream, showTokens, saveHist } = await inquirer.prompt([
136
- { type: 'confirm', name: 'stream', message: 'Stream responses?', default: true },
137
- { type: 'confirm', name: 'showTokens', message: 'Show token usage?', default: true },
138
- { type: 'confirm', name: 'saveHist', message: 'Save chat history?', default: true },
139
- ]);
140
- settings.set('stream', stream);
141
- settings.set('showTokens', showTokens);
142
- settings.set('saveHistory', saveHist);
12
+ if (args.length === 2) {
13
+ config[args[0]] = args[1];
14
+ saveConfig(config);
15
+ console.log(clr.success(args[0] + ' = ' + args[1]));
16
+ return config;
143
17
  }
144
-
145
- success('Setup Complete', 'CLARITY configured successfully!\nRun \'clarity\' to start chatting');
18
+ console.log(' ' + clr.dim('Usage: /config [key] [value]'));
146
19
  }
@@ -0,0 +1,12 @@
1
+ import { clr } from '../ui/colors.js';
2
+ export async function diffCommand(args, config) {
3
+ const [f1, f2] = args;
4
+ if (!f1 || !f2) return console.log(clr.error('Usage: /diff <file1> <file2>'));
5
+ try {
6
+ const { diffTool } = await import('../tools/diff-tool.js');
7
+ const result = await diffTool({ file1: f1, file2: f2 });
8
+ console.log('\n' + result + '\n');
9
+ } catch (err) {
10
+ console.log(clr.error(`Diff error: ${err.message}`));
11
+ }
12
+ }
@@ -0,0 +1,5 @@
1
+ import { blocks } from '../ui/blocks.js';
2
+ export async function exitCommand(args, config, rl) {
3
+ console.log('\n' + blocks.info('Session ended. Goodbye.', '◆ EXIT'));
4
+ process.exit(0);
5
+ }