clarity-ai 1.0.0 → 1.1.1

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": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "AI agent CLI for Termux and terminal — chat, code, automate",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -12,6 +12,9 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSy
12
12
  import { resolve, dirname, basename, extname, join } from 'path';
13
13
  import readline from 'readline';
14
14
 
15
+ const PKG = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url)));
16
+ const VERSION = PKG.version;
17
+
15
18
  const commandRegistry = {
16
19
  async execute(input, context = {}) {
17
20
  const parts = input.trim().split(/\s+/);
@@ -624,7 +627,7 @@ const commandRegistry = {
624
627
  status() {
625
628
  const mem = process.memoryUsage();
626
629
  const rows = [
627
- ['CLARITY', 'v1.0.0'],
630
+ ['CLARITY', `v${VERSION}`],
628
631
  ['Node.js', process.version],
629
632
  ['Platform', `${process.platform} ${process.arch}`],
630
633
  ['Termux', isTermux() ? 'Yes' : 'No'],
@@ -640,7 +643,7 @@ const commandRegistry = {
640
643
  },
641
644
 
642
645
  version() {
643
- blocks.info('CLARITY v1.0.0', 'AI Agent CLI for Termux\nNode.js ' + process.version + '\n' + (isTermux() ? 'Termux mode: active' : 'Standard terminal'));
646
+ blocks.info(`CLARITY v${VERSION}`, 'AI Agent CLI for Termux\nNode.js ' + process.version + '\n' + (isTermux() ? 'Termux mode: active' : 'Standard terminal'));
644
647
  },
645
648
 
646
649
  summarize(target) {
package/src/index.js CHANGED
@@ -39,6 +39,7 @@ process.on('exit', () => {
39
39
  });
40
40
 
41
41
  async function main() {
42
+ console.clear();
42
43
  const args = process.argv.slice(2);
43
44
 
44
45
  if (args.length === 0 && !hasAnyKeys()) {
@@ -18,11 +18,11 @@ const capabilities = {
18
18
  openai: { free: false, streaming: true, models: ['gpt-4o-mini', 'gpt-4o', 'gpt-3.5-turbo'], baseURL: 'https://api.openai.com/v1' },
19
19
  };
20
20
 
21
- async function sendMessage(apiKey, messages, model, stream = true) {
21
+ function sendMessage(apiKey, messages, model, stream = true) {
22
22
  const [providerName] = model.split('/');
23
23
  const provider = providers[providerName];
24
24
  if (!provider) throw new Error(`Unknown provider: ${providerName}`);
25
-
25
+
26
26
  const modelName = model.includes('/') ? model.slice(model.indexOf('/') + 1) : model;
27
27
  return provider.sendMessage(apiKey, messages, modelName, stream);
28
28
  }
package/src/ui/banner.js CHANGED
@@ -1,14 +1,24 @@
1
1
  import figlet from 'figlet';
2
2
  import gradient from 'gradient-string';
3
- import c from './colors.js';
3
+ import { readFileSync } from 'fs';
4
4
 
5
- const GRADIENT = gradient(['#00d2ff', '#7b2ff7']);
5
+ const PKG = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url)));
6
+ const VERSION = PKG.version;
7
+ const grad = gradient(['#00d2ff', '#7b2ff7', '#bb88ff']);
8
+
9
+ function center(text) {
10
+ const width = process.stdout.columns || 80;
11
+ const lines = text.split('\n');
12
+ return lines.map(l => ' '.repeat(Math.max(0, Math.floor((width - l.length) / 2))) + l).join('\n');
13
+ }
6
14
 
7
15
  function renderBanner() {
8
- const text = figlet.textSync('CLARITY', { font: 'ANSI Shadow' });
9
- console.log(GRADIENT(text));
10
- console.log(c.muted('AI Agent CLI — v1.0.0 ⚡ Termux Ready'));
11
- console.log(c.muted('─'.repeat(process.stdout.columns || 80)));
16
+ const ascii = figlet.textSync('CLARITY', { font: 'ANSI Shadow' });
17
+ console.log();
18
+ console.log(center(grad(ascii)));
19
+ console.log();
20
+ const tag = '✦ AI Agent CLI — v' + VERSION + ' ✦';
21
+ console.log(center(grad(tag)));
12
22
  console.log();
13
23
  }
14
24
 
package/src/ui/blocks.js CHANGED
@@ -1,54 +1,109 @@
1
- import boxen from 'boxen';
2
1
  import cliTable3 from 'cli-table3';
3
2
  import c from './colors.js';
4
3
 
4
+ function hr(char = '─', color = c.dim) {
5
+ const w = process.stdout.columns || 80;
6
+ console.log(color(char.repeat(w)));
7
+ }
8
+
9
+ function gap(n = 1) {
10
+ console.log('\n'.repeat(n - 1));
11
+ }
12
+
5
13
  const blocks = {
6
- info(title, message) {
7
- console.log(boxen(message, { padding: 1, borderColor: '#44aaff', borderStyle: 'round', title: ` ℹ ${title} `, titleAlignment: 'left' }));
14
+ info(title, msg) {
15
+ gap();
16
+ console.log(c.info(` ┌── ℹ ${title} ${'─'.repeat(Math.max(0, (process.stdout.columns || 80) - title.length - 10))}┐`));
17
+ msg.split('\n').forEach(l => console.log(c.info(' │') + c.white(` ${l}`.padEnd((process.stdout.columns || 80) - 4)) + c.info('│')));
18
+ console.log(c.info(` └${'─'.repeat((process.stdout.columns || 80) - 4)}┘`));
19
+ gap();
8
20
  },
9
- success(title, message) {
10
- console.log(boxen(message, { padding: 1, borderColor: '#00ff88', borderStyle: 'round', title: ` ✓ ${title} `, titleAlignment: 'left' }));
21
+
22
+ success(title, msg) {
23
+ gap();
24
+ console.log(c.success(` ┌── ✓ ${title} ${'─'.repeat(Math.max(0, (process.stdout.columns || 80) - title.length - 10))}┐`));
25
+ msg.split('\n').forEach(l => console.log(c.success(' │') + c.white(` ${l}`.padEnd((process.stdout.columns || 80) - 4)) + c.success('│')));
26
+ console.log(c.success(` └${'─'.repeat((process.stdout.columns || 80) - 4)}┘`));
27
+ gap();
11
28
  },
12
- warn(title, message) {
13
- console.log(boxen(message, { padding: 1, borderColor: '#ffcc00', borderStyle: 'round', title: ` ⚠ ${title} `, titleAlignment: 'left' }));
29
+
30
+ warn(title, msg) {
31
+ gap();
32
+ console.log(c.warning(` ┌── ⚠ ${title} ${'─'.repeat(Math.max(0, (process.stdout.columns || 80) - title.length - 10))}┐`));
33
+ msg.split('\n').forEach(l => console.log(c.warning(' │') + c.white(` ${l}`.padEnd((process.stdout.columns || 80) - 4)) + c.warning('│')));
34
+ console.log(c.warning(` └${'─'.repeat((process.stdout.columns || 80) - 4)}┘`));
35
+ gap();
14
36
  },
15
- error(title, message) {
16
- console.log(boxen(message, { padding: 1, borderColor: '#ff4466', borderStyle: 'round', title: ` ✗ ${title} `, titleAlignment: 'left' }));
37
+
38
+ error(title, msg) {
39
+ gap();
40
+ console.log(c.error(` ┌── ✗ ${title} ${'─'.repeat(Math.max(0, (process.stdout.columns || 80) - title.length - 10))}┐`));
41
+ msg.split('\n').forEach(l => console.log(c.error(' │') + c.white(` ${l}`.padEnd((process.stdout.columns || 80) - 4)) + c.error('│')));
42
+ console.log(c.error(` └${'─'.repeat((process.stdout.columns || 80) - 4)}┘`));
43
+ gap();
17
44
  },
18
- code(language, code) {
19
- console.log(`\n${c.muted('```' + language)}`);
20
- console.log(c.code(code));
21
- console.log(`${c.muted('```')}\n`);
45
+
46
+ code(lang, code) {
47
+ console.log(c.dim(` ┌─ ${lang} ${'─'.repeat(Math.max(0, (process.stdout.columns || 80) - lang.length - 8))}`));
48
+ code.split('\n').forEach(l => console.log(c.code(` │ ${l}`)));
49
+ console.log(c.dim(` └${'─'.repeat((process.stdout.columns || 80) - 4)}`));
22
50
  },
51
+
23
52
  file(path, content) {
24
- console.log(boxen(c.filename(path) + '\n\n' + content, { padding: 1, borderColor: '#ffcc66', borderStyle: 'single' }));
53
+ console.log(c.filename(` ┌─ ${path}`));
54
+ content.split('\n').forEach(l => console.log(c.white(` │ ${l}`)));
55
+ console.log(c.dim(` └${'─'.repeat((process.stdout.columns || 80) - 4)}`));
25
56
  },
26
- ai(message) {
27
- console.log(boxen(c.ai(message), { padding: 1, borderColor: '#7b2ff7', borderStyle: 'double' }));
57
+
58
+ ai(msg) {
59
+ const w = process.stdout.columns || 80;
60
+ console.log(c.accent(` ╔${'═'.repeat(w - 4)}╗`));
61
+ msg.split('\n').forEach(l => console.log(c.accent(' ║') + c.ai(` ${l}`.padEnd(w - 4)) + c.accent('║')));
62
+ console.log(c.accent(` ╚${'═'.repeat(w - 4)}╝`));
63
+ gap();
28
64
  },
29
- user(message) {
30
- console.log(boxen(c.user(message), { padding: 1, borderColor: '#00d2ff', borderStyle: 'single' }));
65
+
66
+ user(msg) {
67
+ const w = process.stdout.columns || 80;
68
+ console.log(c.primary(` ┌${'─'.repeat(w - 4)}┐`));
69
+ msg.split('\n').forEach(l => console.log(c.primary(' │') + c.user(` ${l}`.padEnd(w - 4)) + c.primary('│')));
70
+ console.log(c.primary(` └${'─'.repeat(w - 4)}┘`));
71
+ gap();
31
72
  },
73
+
32
74
  table(headers, rows) {
33
- const t = new cliTable3({ head: headers.map(h => c.primary(h)), style: { border: ['#555577'] } });
34
- rows.forEach(r => t.push(r));
75
+ const t = new cliTable3({
76
+ head: headers.map(h => c.primary(h)),
77
+ style: { head: [], border: ['#2a2a4a'] },
78
+ chars: { 'top': '─', 'top-mid': '┬', 'top-left': '┌', 'top-right': '┐',
79
+ 'bottom': '─', 'bottom-mid': '┴', 'bottom-left': '└', 'bottom-right': '┘',
80
+ 'left': '│', 'left-mid': '├', 'mid': '─', 'mid-mid': '┼',
81
+ 'right': '│', 'right-mid': '┤' }
82
+ });
83
+ rows.forEach(r => t.push(r.map(v => c.white(String(v)))));
35
84
  console.log(t.toString());
36
85
  },
86
+
37
87
  divider(label) {
38
- const width = process.stdout.columns || 80;
39
- const side = Math.floor((width - (label ? label.length + 4 : 0)) / 2);
40
- console.log(c.muted('─'.repeat(side) + (label ? ` ${label} ` : '') + '─'.repeat(side)));
88
+ const w = process.stdout.columns || 80;
89
+ if (label) {
90
+ const side = Math.floor((w - label.length - 4) / 2);
91
+ console.log(c.dim(` ${'─'.repeat(side)} ${label} ${'─'.repeat(side)}`));
92
+ } else {
93
+ hr();
94
+ }
41
95
  },
96
+
42
97
  badge(text, color = 'cyan') {
43
98
  const colors = { cyan: c.primary, purple: c.accent, green: c.success, yellow: c.warning, red: c.error };
44
99
  return (colors[color] || c.primary)(`[${text}]`);
45
100
  },
101
+
46
102
  progress(label, pct) {
47
- const width = 20;
48
- const filled = Math.round(width * pct / 100);
49
- const empty = width - filled;
50
- const bar = '█'.repeat(filled) + '░'.repeat(empty);
51
- console.log(`${c.muted(label)} ${c.primary(bar)} ${c.white(String(pct))}%`);
103
+ const w = 20;
104
+ const filled = Math.round(w * pct / 100);
105
+ const bar = c.primary('█'.repeat(filled)) + c.dim('░'.repeat(w - filled));
106
+ console.log(` ${c.muted(label)} ${bar} ${c.white(String(pct))}%`);
52
107
  }
53
108
  };
54
109
 
package/src/ui/chatbox.js CHANGED
@@ -1,25 +1,28 @@
1
- import readline from 'readline';
2
1
  import c from './colors.js';
3
2
  import renderBanner from './banner.js';
4
3
  import settings from '../config/settings.js';
5
4
  import { hasAnyKeys } from '../config/keys.js';
6
5
  import { getKey, PROVIDER_NAMES } from '../config/keys.js';
7
6
  import { sendMessage } from '../providers/index.js';
8
- import { createPrompt, addToHistory, loadHistory } from './prompt.js';
7
+ import { createPrompt, showPrompt, addToHistory, loadHistory } from './prompt.js';
9
8
  import blocks from './blocks.js';
10
9
  import spin from './spinner.js';
11
- import { renderMarkdown } from '../utils/markdown.js';
12
10
  import commandRegistry from '../commands/index.js';
13
11
  import memory from '../memory/store.js';
14
12
 
15
13
  let rl = null;
16
14
  let conversation = [];
17
15
 
18
- function getHeader() {
16
+ function renderHeader() {
19
17
  const model = settings.get('defaultModel') || 'groq/llama3-70b-8192';
20
- const width = process.stdout.columns || 80;
21
- const header = ` CLARITY CHAT ──────── Model: ${model} ── /help for commands `;
22
- return c.primary('┌─') + c.muted(header.padEnd(width - 4, '─')) + c.primary('┐');
18
+ const w = process.stdout.columns || 80;
19
+ const left = c.accent('◈');
20
+ const mid = c.muted(` ${model} `);
21
+ const right = c.muted('/help');
22
+ const total = 6 + mid.length + right.length + 2;
23
+ const pad = '.'.repeat(Math.max(0, w - total - 4));
24
+ console.log(c.border(` ${left}${c.dim(pad)}${mid}${c.dim(pad)}${right}`));
25
+ blocks.divider();
23
26
  }
24
27
 
25
28
  function startChat() {
@@ -31,27 +34,27 @@ function startChat() {
31
34
  loadHistory();
32
35
  console.clear();
33
36
  renderBanner();
34
- console.log(getHeader());
37
+ renderHeader();
35
38
  console.log();
36
39
 
37
40
  rl = createPrompt();
38
- rl.prompt();
41
+ showPrompt();
39
42
 
40
43
  rl.on('line', async (line) => {
41
44
  const input = line.trim();
42
- if (!input) { rl.prompt(); return; }
45
+ if (!input) { showPrompt(); return; }
43
46
 
44
47
  addToHistory(input);
45
48
 
46
49
  if (input.startsWith('/')) {
47
- const result = await commandRegistry.execute(input, { rl, conversation });
50
+ const result = await commandRegistry.execute(input, { rl, conversation, showPrompt });
48
51
  if (result?.exit) { closeChat(); return; }
49
52
  } else {
50
53
  conversation.push({ role: 'user', content: input });
51
54
  blocks.user(input);
52
55
  await handleAIResponse();
53
56
  }
54
- rl.prompt();
57
+ showPrompt();
55
58
  });
56
59
 
57
60
  rl.on('close', () => {
@@ -61,7 +64,7 @@ function startChat() {
61
64
 
62
65
  rl.on('SIGINT', () => {
63
66
  console.log(c.muted('\nUse /exit to quit'));
64
- rl.prompt();
67
+ showPrompt();
65
68
  });
66
69
  }
67
70
 
@@ -85,23 +88,26 @@ async function handleAIResponse() {
85
88
 
86
89
  if (settings.get('stream')) {
87
90
  spin.stop();
88
- process.stdout.write(c.ai('CLARITY '));
91
+ const w = process.stdout.columns || 80;
92
+ console.log(c.accent(` ╔${'═'.repeat(w - 4)}╗`));
93
+ process.stdout.write(c.accent(' ║ ') + c.ai('CLARITY ') + c.white(''));
89
94
  let full = '';
90
95
  for await (const chunk of stream) {
91
96
  full += chunk;
92
97
  process.stdout.write(c.white(chunk));
93
98
  }
94
- process.stdout.write('\n\n');
99
+ console.log();
100
+ console.log(c.accent(` ╚${'═'.repeat(w - 4)}╝`));
101
+ console.log();
95
102
  conversation.push({ role: 'assistant', content: full });
96
103
  memory.add(conversation);
97
104
  } else {
105
+ spin.stop();
98
106
  let full = '';
99
107
  for await (const chunk of stream) {
100
108
  full += chunk;
101
109
  }
102
- spin.stop();
103
- const rendered = renderMarkdown(full);
104
- blocks.ai(rendered);
110
+ blocks.ai(full);
105
111
  conversation.push({ role: 'assistant', content: full });
106
112
  memory.add(conversation);
107
113
  }
@@ -109,7 +115,7 @@ async function handleAIResponse() {
109
115
  if (settings.get('showTokens')) {
110
116
  const inTokens = Math.ceil(conversation.reduce((s, m) => s + m.content.length, 0) / 4);
111
117
  const outTokens = Math.ceil(conversation.filter(m => m.role === 'assistant').reduce((s, m) => s + m.content.length, 0) / 4);
112
- console.log(c.muted(`[tokens: ${inTokens} in / ${outTokens} out | cost: free]`));
118
+ console.log(c.dim(` tokens: ${inTokens} in / ${outTokens} outfree`));
113
119
  }
114
120
  } catch (err) {
115
121
  spin.fail('Error');
@@ -119,7 +125,7 @@ async function handleAIResponse() {
119
125
 
120
126
  function closeChat() {
121
127
  if (rl) rl.close();
122
- console.log(c.muted('\nThank you for using CLARITY!'));
128
+ console.log(c.muted('\nGoodbye!'));
123
129
  process.exit(0);
124
130
  }
125
131
 
package/src/ui/colors.js CHANGED
@@ -1,22 +1,27 @@
1
1
  import chalk from 'chalk';
2
- export const c = {
3
- primary: chalk.hex('#00d2ff'),
4
- accent: chalk.hex('#7b2ff7'),
5
- success: chalk.hex('#00ff88'),
6
- warning: chalk.hex('#ffcc00'),
7
- error: chalk.hex('#ff4466'),
8
- info: chalk.hex('#00aaff'),
9
- muted: chalk.hex('#666888'),
10
- white: chalk.hex('#f0f0ff'),
11
- user: chalk.hex('#00d2ff').bold,
12
- ai: chalk.hex('#bb88ff').bold,
13
- system: chalk.hex('#888888').italic,
14
- code: chalk.hex('#aaffcc'),
15
- filename: chalk.hex('#ffcc66').underline,
16
- cmd: chalk.hex('#ff8844').bold,
17
- bgError: chalk.bgHex('#3d0010').hex('#ff4466'),
18
- bgSuccess: chalk.bgHex('#003d1a').hex('#00ff88'),
19
- bgInfo: chalk.bgHex('#001a3d').hex('#00aaff'),
20
- bgWarn: chalk.bgHex('#3d3000').hex('#ffcc00'),
2
+
3
+ const c = {
4
+ primary: chalk.hex('#00d2ff'),
5
+ accent: chalk.hex('#7b2ff7'),
6
+ success: chalk.hex('#00ff88'),
7
+ warning: chalk.hex('#ffcc00'),
8
+ error: chalk.hex('#ff4466'),
9
+ info: chalk.hex('#44aaff'),
10
+ muted: chalk.hex('#555577'),
11
+ white: chalk.hex('#e0e0ff'),
12
+ user: chalk.bold.hex('#00d2ff'),
13
+ ai: chalk.bold.hex('#bb88ff'),
14
+ system: chalk.italic.hex('#666888'),
15
+ code: chalk.hex('#aaffcc'),
16
+ filename: chalk.underline.hex('#ffcc66'),
17
+ cmd: chalk.bold.hex('#ff8844'),
18
+ bgError: chalk.bgHex('#1a0008').hex('#ff4466'),
19
+ bgSuccess: chalk.bgHex('#001a0a').hex('#00ff88'),
20
+ bgInfo: chalk.bgHex('#000d1a').hex('#44aaff'),
21
+ bgWarn: chalk.bgHex('#1a1500').hex('#ffcc00'),
22
+ border: chalk.hex('#2a2a4a'),
23
+ glow: chalk.hex('#3a3a6a'),
24
+ dim: chalk.hex('#3a3a5a'),
21
25
  };
26
+
22
27
  export default c;
package/src/ui/prompt.js CHANGED
@@ -1,27 +1,26 @@
1
1
  import readline from 'readline';
2
- import { createReadStream, createWriteStream, existsSync } from 'fs';
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
3
+ import { dirname } from 'path';
3
4
  import paths from '../config/paths.js';
4
5
  import c from './colors.js';
5
6
 
6
7
  const HISTORY_FILE = paths.history;
7
8
  const MAX_HISTORY = 500;
8
-
9
9
  let history = [];
10
10
 
11
11
  function loadHistory() {
12
12
  try {
13
13
  if (existsSync(HISTORY_FILE)) {
14
- const data = require('fs').readFileSync(HISTORY_FILE, 'utf8');
15
- history = data.split('\n').filter(Boolean).slice(-MAX_HISTORY);
14
+ history = readFileSync(HISTORY_FILE, 'utf8').split('\n').filter(Boolean).slice(-MAX_HISTORY);
16
15
  }
17
16
  } catch {}
18
17
  }
19
18
 
20
19
  function saveHistory() {
21
20
  try {
22
- const dir = require('path').dirname(HISTORY_FILE);
23
- if (!existsSync(dir)) require('fs').mkdirSync(dir, { recursive: true });
24
- require('fs').writeFileSync(HISTORY_FILE, history.slice(-MAX_HISTORY).join('\n'));
21
+ const dir = dirname(HISTORY_FILE);
22
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
23
+ writeFileSync(HISTORY_FILE, history.slice(-MAX_HISTORY).join('\n'));
25
24
  } catch {}
26
25
  }
27
26
 
@@ -33,17 +32,22 @@ function addToHistory(line) {
33
32
  }
34
33
  }
35
34
 
36
- function createPrompt(onselect) {
35
+ function createPrompt() {
36
+ loadHistory();
37
37
  const rl = readline.createInterface({
38
38
  input: process.stdin,
39
39
  output: process.stdout,
40
- history: history,
40
+ history,
41
41
  historySize: MAX_HISTORY,
42
42
  terminal: true,
43
- prompt: c.user('┃ you › '),
43
+ prompt: '',
44
44
  });
45
-
46
45
  return rl;
47
46
  }
48
47
 
49
- export { createPrompt, loadHistory, addToHistory, history };
48
+ function showPrompt() {
49
+ const prefix = c.primary('◇');
50
+ process.stdout.write(` ${prefix} `);
51
+ }
52
+
53
+ export { createPrompt, showPrompt, addToHistory, loadHistory, history };
package/src/ui/spinner.js CHANGED
@@ -1,43 +1,49 @@
1
- import ora from 'ora';
2
- import { isTermux } from '../utils/termux.js';
1
+ import c from './colors.js';
3
2
 
4
- const useUnicode = !isTermux() || process.env.TERMUX_VERSION !== undefined;
5
- const spinnerFrames = useUnicode
6
- ? ['','⣽','⣻','⢿','⡿','⣟','⣯','⣷']
7
- : ['|','/','-','\\'];
3
+ const frames = ['◐', '◓', '◑', '◒'];
4
+ let interval = null;
5
+ let currentText = '';
8
6
 
9
- let spinner = null;
7
+ function start(text) {
8
+ stop();
9
+ currentText = text;
10
+ let i = 0;
11
+ process.stdout.write(c.accent(frames[i]) + c.muted(` ${text}`));
12
+ interval = setInterval(() => {
13
+ i = (i + 1) % frames.length;
14
+ process.stdout.clearLine(0);
15
+ process.stdout.cursorTo(0);
16
+ process.stdout.write(c.accent(frames[i]) + c.muted(` ${currentText}`));
17
+ }, 120);
18
+ return spin;
19
+ }
10
20
 
11
- const spin = {
12
- start(text) {
13
- if (spinner) spinner.stop();
14
- spinner = ora({ text, spinner: { frames: spinnerFrames, interval: 80 } }).start();
15
- return spin;
16
- },
17
- stop() {
18
- if (spinner) { spinner.stop(); spinner = null; }
19
- return spin;
20
- },
21
- succeed(text) {
22
- if (spinner) { spinner.succeed(text); spinner = null; }
23
- return spin;
24
- },
25
- fail(text) {
26
- if (spinner) { spinner.fail(text); spinner = null; }
27
- return spin;
28
- },
29
- info(text) {
30
- if (spinner) { spinner.info(text); spinner = null; }
31
- return spin;
32
- },
33
- warn(text) {
34
- if (spinner) { spinner.warn(text); spinner = null; }
35
- return spin;
36
- },
37
- update(text) {
38
- if (spinner) spinner.text = text;
39
- return spin;
21
+ function stop() {
22
+ if (interval) {
23
+ clearInterval(interval);
24
+ interval = null;
25
+ process.stdout.clearLine(0);
26
+ process.stdout.cursorTo(0);
40
27
  }
41
- };
28
+ return spin;
29
+ }
42
30
 
31
+ function succeed(text) {
32
+ stop();
33
+ if (text) console.log(c.success(` ✓ ${text}`));
34
+ return spin;
35
+ }
36
+
37
+ function fail(text) {
38
+ stop();
39
+ if (text) console.log(c.error(` ✗ ${text}`));
40
+ return spin;
41
+ }
42
+
43
+ function update(text) {
44
+ currentText = text;
45
+ return spin;
46
+ }
47
+
48
+ const spin = { start, stop, succeed, fail, update };
43
49
  export default spin;