draco-cli 0.0.0-stage → 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.
@@ -0,0 +1,114 @@
1
+ 'use strict';
2
+
3
+ const theme = require('./theme.js');
4
+
5
+ const tty = process.stdout.isTTY;
6
+
7
+ const c = theme.c;
8
+
9
+ function splash(cfg) {
10
+ process.stdout.write(theme.welcomeBanner(cfg) + '\n');
11
+ process.stdout.write('\n');
12
+ process.stdout.write(`Endpoint: OpenCode Zen API Server\n`);
13
+ process.stdout.write(`Model: Free Dynamic Routing Tier\n`);
14
+ process.stdout.write('\n');
15
+ process.stdout.write(`${c.bold}Workspace:${c.reset} ${cfg.workspaceRoot}\n`);
16
+ process.stdout.write(`${c.gray}Active model: ${cfg.model}${c.reset}\n`);
17
+ process.stdout.write(`Type ${c.cyan}/help${c.reset} for commands. ${c.gray}Esc stops a response.${c.reset}\n`);
18
+ if (!cfg.searchApiKey) {
19
+ process.stdout.write(
20
+ `${c.gray}Web search: on via DuckDuckGo (no key). Set DRACO_SEARCH_API_KEY to use Brave.${c.reset}\n`
21
+ );
22
+ }
23
+ process.stdout.write('\n');
24
+ }
25
+
26
+ function clearScreen() {
27
+ theme.clearScreen();
28
+ }
29
+
30
+ function promptText(basename) {
31
+ return `${c.green}➜${c.reset} ${c.cyan}${basename}${c.reset} `;
32
+ }
33
+
34
+ function divider(label) {
35
+ process.stdout.write(theme.dividerLine(label) + '\n');
36
+ }
37
+
38
+ function responseStart() {
39
+ process.stdout.write(theme.responseRule('top') + '\n');
40
+ }
41
+
42
+ function responseEnd() {
43
+ process.stdout.write(theme.responseRule('bottom') + '\n');
44
+ }
45
+
46
+ function info(msg) {
47
+ process.stdout.write(`${c.blue}ℹ${c.reset} ${msg}\n`);
48
+ }
49
+
50
+ function success(msg) {
51
+ process.stdout.write(`${c.green}✔${c.reset} ${msg}\n`);
52
+ }
53
+
54
+ function warn(msg) {
55
+ process.stdout.write(`${c.yellow}⚠ ${msg}${c.reset}\n`);
56
+ }
57
+
58
+ function error(msg) {
59
+ process.stdout.write(theme.errorTag(msg) + '\n');
60
+ }
61
+
62
+ function toolStart(name, args) {
63
+ let summary = '';
64
+ try {
65
+ summary = JSON.stringify(args);
66
+ } catch {
67
+ summary = '';
68
+ }
69
+ if (summary.length > 100) summary = summary.slice(0, 97) + '...';
70
+ process.stdout.write(`${c.cyan}● ${name}${c.reset} ${c.dim}${summary}${c.reset}\n`);
71
+ }
72
+
73
+ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
74
+
75
+ class Spinner {
76
+ constructor(text) {
77
+ this.text = text || 'Thinking…';
78
+ this.timer = null;
79
+ this.frame = 0;
80
+ }
81
+
82
+ start() {
83
+ if (!tty || this.timer) return;
84
+ this.timer = setInterval(() => {
85
+ const f = SPINNER_FRAMES[this.frame++ % SPINNER_FRAMES.length];
86
+ process.stdout.write(`\r${c.cyan}${f}${c.reset} ${this.text}`);
87
+ }, 80);
88
+ }
89
+
90
+ stop() {
91
+ if (this.timer) {
92
+ clearInterval(this.timer);
93
+ this.timer = null;
94
+ }
95
+ if (tty) process.stdout.write('\r\x1b[K');
96
+ }
97
+ }
98
+
99
+ module.exports = {
100
+ c,
101
+ theme,
102
+ splash,
103
+ clearScreen,
104
+ divider,
105
+ responseStart,
106
+ responseEnd,
107
+ promptText,
108
+ info,
109
+ success,
110
+ warn,
111
+ error,
112
+ toolStart,
113
+ Spinner,
114
+ };
@@ -0,0 +1,167 @@
1
+ 'use strict';
2
+
3
+ const ANSI = {
4
+ reset: '\x1b[0m',
5
+ bold: '\x1b[1m',
6
+ dim: '\x1b[2m',
7
+ italic: '\x1b[3m',
8
+ underline: '\x1b[4m',
9
+ red: '\x1b[31m',
10
+ green: '\x1b[32m',
11
+ yellow: '\x1b[33m',
12
+ blue: '\x1b[34m',
13
+ purple: '\x1b[35m',
14
+ cyan: '\x1b[36m',
15
+ gray: '\x1b[90m',
16
+ brightRed: '\x1b[91m',
17
+ brightGreen: '\x1b[92m',
18
+ brightYellow: '\x1b[93m',
19
+ brightBlue: '\x1b[94m',
20
+ brightPurple: '\x1b[95m',
21
+ brightCyan: '\x1b[96m',
22
+ };
23
+
24
+ const colorEnabled =
25
+ !process.env.NO_COLOR &&
26
+ (process.env.FORCE_COLOR !== undefined ||
27
+ Boolean(process.stdout.isTTY) ||
28
+ Boolean(process.stderr.isTTY));
29
+
30
+ const c = {};
31
+ for (const key of Object.keys(ANSI)) {
32
+ c[key] = colorEnabled ? ANSI[key] : '';
33
+ }
34
+
35
+ const GLYPHS = {
36
+ D: ['██████╗ ', '██╔══██╗', '██║ ██║', '██║ ██║', '██║ ██║', '╚█████╔╝', ' ╚════╝ '],
37
+ R: ['██████╗ ', '██╔══██╗', '██████╔╝', '██╔══██╗', '██║ ██║', '╚██╗ ██╔╝', ' ╚═══╝ '],
38
+ A: [' █████╗ ', '██╔══██╗', '███████║', '██╔══██║', '██║ ██║', '╚═╗ ╚═╝', ' '],
39
+ C: [' ██████╗', '██╔════╝', '██║ ', '██║ ', '██║ ', '╚█████╗ ', ' ╚════╝ '],
40
+ O: [' ██████╗ ', '██╔═══██╗', '██║ ██║', '██║ ██║', '██║ ██║', '╚██████╔╝', ' ╚═════╝ '],
41
+ };
42
+
43
+ const LOGO = (() => {
44
+ const rows = [];
45
+ for (let r = 0; r < 7; r++) {
46
+ rows.push('DRACO'.split('').map((ch) => GLYPHS[ch][r]).join(' ').replace(/\s+$/, ''));
47
+ }
48
+ return rows;
49
+ })();
50
+
51
+ const LOGO_COLORS = [c.red, c.red, c.purple, c.purple, c.cyan, c.cyan, c.cyan];
52
+
53
+ function paint(color, text) {
54
+ return `${color}${text}${c.reset}`;
55
+ }
56
+
57
+ function stripAnsi(text) {
58
+ return String(text).replace(/\x1b\[[0-9;]*m/g, '');
59
+ }
60
+
61
+ function textWidth(text) {
62
+ return stripAnsi(text).length;
63
+ }
64
+
65
+ function columns(max) {
66
+ const limit = max || 100;
67
+ return Math.max(20, Math.min(process.stdout.columns || 80, limit));
68
+ }
69
+
70
+ function box(lines, opts = {}) {
71
+ const borderColor = opts.borderColor || c.cyan;
72
+ const pad = opts.pad === undefined ? 2 : opts.pad;
73
+ const defaultAlign = opts.align || 'center';
74
+ const items = lines.map((line) => (typeof line === 'string' ? { text: line } : line));
75
+ const innerWidth = Math.max(0, ...items.map((item) => textWidth(item.text || '')));
76
+ const rule = '─'.repeat(innerWidth + pad * 2);
77
+ const out = [`${borderColor}┌${rule}┐${c.reset}`];
78
+
79
+ for (const item of items) {
80
+ const text = item.text || '';
81
+ const align = item.align || defaultAlign;
82
+ const slack = innerWidth - textWidth(text);
83
+ let left;
84
+ if (align === 'left') left = 0;
85
+ else if (align === 'right') left = slack;
86
+ else left = Math.floor(slack / 2);
87
+ const right = slack - left;
88
+ const color = item.color || '';
89
+ out.push(
90
+ `${borderColor}│${c.reset}${' '.repeat(pad)}${color}${' '.repeat(left)}${text}` +
91
+ `${c.reset}${' '.repeat(right)}${' '.repeat(pad)}${borderColor}│${c.reset}`
92
+ );
93
+ }
94
+
95
+ out.push(`${borderColor}└${rule}┘${c.reset}`);
96
+ return out.join('\n');
97
+ }
98
+
99
+ function clearScreen() {
100
+ if (!process.stdout.isTTY) return;
101
+ process.stdout.write('\x1b[2J\x1b[0H');
102
+ }
103
+
104
+ function welcomeBanner(cfg) {
105
+ clearScreen();
106
+ const version = cfg && cfg.version ? `v${cfg.version}` : '';
107
+ const lines = [
108
+ ...LOGO.map((row, i) => ({ text: row, color: c.bold + LOGO_COLORS[i], align: 'left' })),
109
+ { text: '' },
110
+ { text: 'DRACO CLI', color: c.bold + c.cyan, align: 'center' },
111
+ { text: 'AI coding agent for your terminal', color: c.purple, align: 'center' },
112
+ { text: version, color: c.gray, align: 'center' },
113
+ ];
114
+ return box(lines, { borderColor: c.cyan, pad: 2 });
115
+ }
116
+
117
+ function dividerLine(label) {
118
+ const total = columns(100);
119
+ const core = label ? ` ${label} ` : '';
120
+ const rest = Math.max(0, total - textWidth(core));
121
+ const left = Math.floor(rest / 2);
122
+ const right = rest - left;
123
+ return (
124
+ `${c.gray}${'─'.repeat(left)}${c.reset}` +
125
+ `${c.purple}${core}${c.reset}` +
126
+ `${c.gray}${'─'.repeat(right)}${c.reset}`
127
+ );
128
+ }
129
+
130
+ function responseRule(kind) {
131
+ const total = columns(100);
132
+ if (kind === 'bottom') {
133
+ return `${c.cyan}└${'─'.repeat(Math.max(0, total - 1))}${c.reset}`;
134
+ }
135
+ const head = '┌─ DRACO ';
136
+ return `${c.cyan}${head}${'─'.repeat(Math.max(0, total - textWidth(head)))}${c.reset}`;
137
+ }
138
+
139
+ function gutter() {
140
+ return `${c.cyan}│${c.reset}`;
141
+ }
142
+
143
+ function errorTag(message) {
144
+ return `${c.bold}${c.red}[ERROR]${c.reset} ${message}`;
145
+ }
146
+
147
+ function printError(message) {
148
+ process.stderr.write(errorTag(message) + '\n');
149
+ }
150
+
151
+ module.exports = {
152
+ ANSI,
153
+ c,
154
+ colorEnabled,
155
+ paint,
156
+ stripAnsi,
157
+ textWidth,
158
+ columns,
159
+ box,
160
+ clearScreen,
161
+ welcomeBanner,
162
+ dividerLine,
163
+ responseRule,
164
+ gutter,
165
+ errorTag,
166
+ printError,
167
+ };