@taj-special/dravix-code 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.
@@ -0,0 +1,60 @@
1
+ const USAGE_URL = 'https://dravix.app/cli-usage.php';
2
+ export async function checkUsage(cliToken) {
3
+ try {
4
+ const res = await fetch(USAGE_URL, {
5
+ method: 'POST',
6
+ headers: { 'Content-Type': 'application/json' },
7
+ body: JSON.stringify({ action: 'check', token: cliToken }),
8
+ });
9
+ if (!res.ok)
10
+ return null;
11
+ return await res.json();
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ }
17
+ export async function reportUsage(cliToken, tokens) {
18
+ try {
19
+ const res = await fetch(USAGE_URL, {
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/json' },
22
+ body: JSON.stringify({ action: 'add', token: cliToken, tokens }),
23
+ });
24
+ if (!res.ok)
25
+ return null;
26
+ return await res.json();
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
32
+ // Estimate tokens from text (~4 chars per token)
33
+ export function estimateTokens(text) {
34
+ return Math.max(1, Math.ceil(text.length / 4));
35
+ }
36
+ // Format time until reset — uses server-side seconds to avoid timezone issues
37
+ export function formatResetTime(info) {
38
+ const secs = typeof info === 'string'
39
+ ? Math.max(0, Math.floor((new Date(info).getTime() - Date.now()) / 1000))
40
+ : (info.reset_in_seconds ?? 0);
41
+ if (secs <= 0)
42
+ return 'soon';
43
+ const h = Math.floor(secs / 3600);
44
+ const m = Math.floor((secs % 3600) / 60);
45
+ if (h > 0)
46
+ return `${h}h ${m}m`;
47
+ return `${m}m`;
48
+ }
49
+ // Format number with K suffix
50
+ export function fmtNum(n) {
51
+ return n >= 1000 ? (n / 1000).toFixed(1).replace('.0', '') + 'k' : String(n);
52
+ }
53
+ // Progress bar: ████████░░░░ pct%
54
+ export function usageBar(used, limit, width = 18) {
55
+ const pct = Math.min(Math.round((used / limit) * 100), 100);
56
+ const filled = Math.round((pct / 100) * width);
57
+ const empty = width - filled;
58
+ const bar = '█'.repeat(filled) + '░'.repeat(empty);
59
+ return { bar, pct };
60
+ }
@@ -0,0 +1,258 @@
1
+ import chalk from 'chalk';
2
+ import { readFileSync } from 'fs';
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname, join } from 'path';
5
+ const _dir = dirname(fileURLToPath(import.meta.url));
6
+ const _pkg = JSON.parse(readFileSync(join(_dir, '../../package.json'), 'utf-8'));
7
+ export const VERSION = _pkg.version;
8
+ export const colors = {
9
+ primary: chalk.hex('#6366f1'),
10
+ success: chalk.hex('#34d399'),
11
+ error: chalk.hex('#f87171'),
12
+ warn: chalk.hex('#fbbf24'),
13
+ muted: chalk.hex('#6b7280'),
14
+ ai: chalk.hex('#c4b5fd'),
15
+ dim: chalk.hex('#374151'),
16
+ };
17
+ export function banner() {
18
+ console.log(colors.primary.bold(`
19
+ ██████╗ ██████╗ █████╗ ██╗ ██╗██╗██╗ ██╗
20
+ ██╔══██╗██╔══██╗██╔══██╗██║ ██║██║╚██╗██╔╝
21
+ ██║ ██║██████╔╝███████║██║ ██║██║ ╚███╔╝
22
+ ██║ ██║██╔══██╗██╔══██║╚██╗ ██╔╝██║ ██╔██╗
23
+ ██████╔╝██║ ██║██║ ██║ ╚████╔╝ ██║██╔╝ ██╗
24
+ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═╝`));
25
+ console.log(chalk.hex('#6366f1').bold(' Code') + chalk.hex('#4b5563')(` v${VERSION} · `) + chalk.hex('#818cf8')('AI-powered coding assistant for your terminal'));
26
+ console.log(chalk.hex('#374151')(' ─────────────────────────────────────────────\n'));
27
+ }
28
+ export function printAI(chunk) {
29
+ process.stdout.write(colors.ai(chunk));
30
+ }
31
+ export function printOpResult(result) {
32
+ if (result.type === 'created') {
33
+ console.log(colors.success(' ✓') + chalk.white(' Created ') + colors.muted(result.path ?? ''));
34
+ }
35
+ else if (result.type === 'skipped') {
36
+ console.log(colors.muted(' ·') + chalk.white(' Unchanged ') + colors.muted(result.path ?? ''));
37
+ }
38
+ else if (result.type === 'folder_created') {
39
+ console.log(colors.success(' ✓') + chalk.white(' Created dir ') + colors.muted(result.path ?? ''));
40
+ }
41
+ else if (result.type === 'deleted') {
42
+ console.log(colors.error(' ✗') + chalk.white(' Deleted ') + colors.muted(result.path ?? ''));
43
+ }
44
+ else if (result.type === 'modified') {
45
+ const diff = result.diff ?? [];
46
+ const adds = diff.filter(d => d.kind === 'add').length;
47
+ const removes = diff.filter(d => d.kind === 'remove').length;
48
+ const cols = process.stdout.columns ?? 80;
49
+ // ── Header ──────────────────────────────────────────────────
50
+ // For large files (>1500 lines), diff is empty — use raw line counts
51
+ const displayAdds = adds > 0 ? adds : (result.linesAfter ?? 0) - Math.min(result.linesBefore ?? 0, result.linesAfter ?? 0);
52
+ const displayRemoves = removes > 0 ? removes : (result.linesBefore ?? 0) - Math.min(result.linesBefore ?? 0, result.linesAfter ?? 0);
53
+ const statStr = [
54
+ (adds > 0 || result.linesAfter !== undefined) && displayAdds > 0 ? chalk.hex('#34d399').bold(`+${displayAdds}`) : '',
55
+ (removes > 0 || result.linesBefore !== undefined) && displayRemoves > 0 ? chalk.hex('#f87171').bold(`-${displayRemoves}`) : '',
56
+ ].filter(Boolean).join(' ');
57
+ const pathStr = chalk.hex('#e2e8f0')(result.path ?? '');
58
+ const iconStr = chalk.hex('#fbbf24')(' ~');
59
+ const labelStr = chalk.hex('#94a3b8')(' Updated ');
60
+ const statPad = statStr ? ' ' + statStr : '';
61
+ console.log(iconStr + labelStr + pathStr + statPad);
62
+ // Large file — show line count change instead of full diff
63
+ if (diff.length === 0 && result.linesBefore !== undefined) {
64
+ const delta = (result.linesAfter ?? 0) - (result.linesBefore ?? 0);
65
+ const deltaStr = delta > 0 ? chalk.hex('#34d399')(`+${delta} lines`) : delta < 0 ? chalk.hex('#f87171')(`${delta} lines`) : 'no line count change';
66
+ console.log(chalk.hex('#4b5563')(` ${result.linesBefore} → ${result.linesAfter} lines (${deltaStr})`));
67
+ return;
68
+ }
69
+ if (diff.length === 0 || (adds === 0 && removes === 0)) {
70
+ console.log(chalk.hex('#4b5563')(' (formatting / whitespace change)'));
71
+ return;
72
+ }
73
+ // ── Diff body ────────────────────────────────────────────────
74
+ const CONTEXT = 3;
75
+ const MAX_SIDE = 40; // max removes and max adds shown (each independently)
76
+ const show = new Uint8Array(diff.length);
77
+ for (let i = 0; i < diff.length; i++) {
78
+ if (diff[i].kind !== 'context') {
79
+ for (let c = Math.max(0, i - CONTEXT); c <= Math.min(diff.length - 1, i + CONTEXT); c++)
80
+ show[c] = 1;
81
+ }
82
+ }
83
+ const maxTextW = cols - 12;
84
+ const clip = (t) => t.length > maxTextW ? t.slice(0, maxTextW - 1) + '…' : t;
85
+ const renderLine = (line) => {
86
+ const num = String(line.lineNo).padStart(5, ' ');
87
+ if (line.kind === 'remove') {
88
+ const content = ` ${num} ${chalk.hex('#f87171')('-')} ${clip(line.text)}`;
89
+ console.log(' ' + chalk.hex('#7f1d1d')('▌') + chalk.bgHex('#1c0a0a')(content.padEnd(cols - 3)));
90
+ }
91
+ else if (line.kind === 'add') {
92
+ const content = ` ${num} ${chalk.hex('#34d399')('+')} ${clip(line.text)}`;
93
+ console.log(' ' + chalk.hex('#065f46')('▌') + chalk.bgHex('#021a0e')(content.padEnd(cols - 3)));
94
+ }
95
+ else {
96
+ console.log(chalk.hex('#374151')(` ${num} ${clip(line.text)}`));
97
+ }
98
+ };
99
+ // Build hunks (contiguous visible sections)
100
+ const hunks = [];
101
+ let curHunk = [];
102
+ let lastIdx = -1;
103
+ for (let i = 0; i < diff.length; i++) {
104
+ if (!show[i])
105
+ continue;
106
+ if (lastIdx >= 0 && i > lastIdx + 1) {
107
+ hunks.push(curHunk);
108
+ curHunk = [];
109
+ }
110
+ lastIdx = i;
111
+ curHunk.push(diff[i]);
112
+ }
113
+ if (curHunk.length > 0)
114
+ hunks.push(curHunk);
115
+ // Per-hunk rendering: leading context → all removes → all adds → trailing context
116
+ let shownRemoves = 0;
117
+ let shownAdds = 0;
118
+ for (let h = 0; h < hunks.length; h++) {
119
+ if (shownRemoves >= MAX_SIDE && shownAdds >= MAX_SIDE)
120
+ break;
121
+ if (h > 0)
122
+ console.log(chalk.hex('#374151')(' ╌╌╌'));
123
+ const hunk = hunks[h];
124
+ const before = [];
125
+ const hRemoves = [];
126
+ const hAdds = [];
127
+ let pendingCtx = [];
128
+ let seenChange = false;
129
+ for (const line of hunk) {
130
+ if (line.kind === 'context') {
131
+ if (!seenChange)
132
+ before.push(line);
133
+ else
134
+ pendingCtx.push(line);
135
+ }
136
+ else {
137
+ seenChange = true;
138
+ pendingCtx = [];
139
+ if (line.kind === 'remove')
140
+ hRemoves.push(line);
141
+ else
142
+ hAdds.push(line);
143
+ }
144
+ }
145
+ for (const l of before)
146
+ renderLine(l);
147
+ for (const l of hRemoves) {
148
+ if (shownRemoves >= MAX_SIDE)
149
+ break;
150
+ renderLine(l);
151
+ shownRemoves++;
152
+ }
153
+ for (const l of hAdds) {
154
+ if (shownAdds >= MAX_SIDE)
155
+ break;
156
+ renderLine(l);
157
+ shownAdds++;
158
+ }
159
+ for (const l of pendingCtx)
160
+ renderLine(l);
161
+ }
162
+ const hiddenRemoves = removes - shownRemoves;
163
+ const hiddenAdds = adds - shownAdds;
164
+ if (hiddenRemoves > 0 || hiddenAdds > 0) {
165
+ const parts = [
166
+ hiddenRemoves > 0 ? chalk.hex('#f87171')(`-${hiddenRemoves}`) : '',
167
+ hiddenAdds > 0 ? chalk.hex('#34d399')(`+${hiddenAdds}`) : '',
168
+ ].filter(Boolean).join(' ');
169
+ console.log(chalk.hex('#4b5563')(` … ${parts} more lines not shown`));
170
+ }
171
+ }
172
+ else if (result.type === 'run') {
173
+ const cmd = result.message ?? '';
174
+ const cols = process.stdout.columns ?? 80;
175
+ console.log(colors.primary(' $') + ' ' + chalk.white(cmd));
176
+ if (result.output) {
177
+ const outLines = result.output.split('\n').filter(l => l.trim()).slice(0, 20);
178
+ if (outLines.length > 0) {
179
+ const maxW = Math.min(Math.max(...outLines.map(l => l.length), 10) + 2, cols - 8);
180
+ console.log(chalk.hex('#374151')(' ╭' + '─'.repeat(maxW)));
181
+ for (const line of outLines) {
182
+ const clipped = line.length > maxW - 2 ? line.slice(0, maxW - 3) + '…' : line;
183
+ console.log(chalk.hex('#374151')(' │') + ' ' + chalk.hex('#94a3b8')(clipped));
184
+ }
185
+ console.log(chalk.hex('#374151')(' ╰' + '─'.repeat(maxW)));
186
+ }
187
+ }
188
+ }
189
+ else if (result.type === 'error') {
190
+ const msg = result.message ?? '';
191
+ const cols = process.stdout.columns ?? 80;
192
+ const errLines = msg.split('\n').filter(l => l.trim());
193
+ if (errLines.length <= 1) {
194
+ console.log(colors.error(' ✗') + chalk.white(' ' + msg));
195
+ }
196
+ else {
197
+ console.log(colors.error(' ✗') + chalk.white(' ' + errLines[0]));
198
+ const rest = errLines.slice(1);
199
+ const maxW = Math.min(Math.max(...rest.map(l => l.length), 10) + 2, cols - 8);
200
+ console.log(chalk.hex('#450a0a')(' ╭' + '─'.repeat(maxW)));
201
+ for (const line of rest) {
202
+ const clipped = line.length > maxW - 2 ? line.slice(0, maxW - 3) + '…' : line;
203
+ console.log(chalk.hex('#450a0a')(' │') + ' ' + chalk.hex('#fca5a5')(clipped));
204
+ }
205
+ console.log(chalk.hex('#450a0a')(' ╰' + '─'.repeat(maxW)));
206
+ }
207
+ }
208
+ }
209
+ export function printError(msg) {
210
+ console.log('\n' + colors.error(' ✗ ') + chalk.white(msg) + '\n');
211
+ }
212
+ export function printInfo(msg) {
213
+ console.log(colors.muted(' ' + msg));
214
+ }
215
+ export function printHelp() {
216
+ const C = chalk.hex('#818cf8');
217
+ const DIM = chalk.hex('#4b5563');
218
+ const W = chalk.hex('#e2e8f0');
219
+ const cols = process.stdout.columns ?? 80;
220
+ const line = DIM(' ' + '─'.repeat(Math.min(cols - 4, 60)));
221
+ console.log(`
222
+ ${colors.primary.bold(' Commands')}
223
+ ${line}
224
+ ${C('/files')} ${W('List project files and git status')}
225
+ ${C('/resume')} ${W('Resume a saved conversation')}
226
+ ${C('/retry')} ${W('Resend your last message')}
227
+ ${C('/copy')} ${W('Copy last AI response to clipboard')}
228
+ ${C('/model')} ${W('Show current AI model')}
229
+ ${C('/clear')} ${W('Clear screen')}
230
+ ${C('/whoami')} ${W('Show signed-in user')}
231
+ ${C('/logout')} ${W('Sign out from Dravix Code')}
232
+ ${C('/help')} ${W('Show this help')}
233
+ ${C('/exit')} ${W('Exit Dravix Code')}
234
+
235
+ ${colors.primary.bold(' Keyboard shortcuts')}
236
+ ${line}
237
+ ${C('Tab')} ${W('Path autocomplete — navigate project files')}
238
+ ${C('@')} ${W('File picker — browse and insert file path')}
239
+ ${C('Ctrl+C')} ${W('Cancel AI response while streaming')}
240
+ ${C('Ctrl+L')} ${W('Clear screen')}
241
+ ${C('→ ←')} ${W('Enter / exit folder in file picker')}
242
+ ${C('↑ ↓')} ${W('Navigate items in picker')}
243
+ ${C('Space')} ${W('Select item in file picker')}
244
+ ${C('Esc')} ${W('Close picker')}
245
+
246
+ ${colors.primary.bold(' File picker triggers')}
247
+ ${line}
248
+ ${C('@filename')} ${W('Search and insert a file reference')}
249
+ ${C('Tab')} ${W('Autocomplete path at cursor position')}
250
+
251
+ ${colors.primary.bold(' Tips')}
252
+ ${line}
253
+ ${colors.muted('·')} ${W('Start typing while AI responds — your message queues automatically')}
254
+ ${colors.muted('·')} ${W('Mention a filename in your message to auto-inject its content')}
255
+ ${colors.muted('·')} ${W('When approving changes:')} ${C('y')} ${DIM('yes')} ${C('a')} ${DIM('always')} ${C('n')} ${DIM('no')}
256
+ ${colors.muted('·')} ${W('Use /resume to continue a previous conversation')}
257
+ `);
258
+ }
@@ -0,0 +1,17 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ export function readFile(filePath) {
4
+ try {
5
+ return fs.readFileSync(filePath, 'utf-8');
6
+ }
7
+ catch {
8
+ return null;
9
+ }
10
+ }
11
+ export function writeFile(filePath, content) {
12
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
13
+ fs.writeFileSync(filePath, content, 'utf-8');
14
+ }
15
+ export function fileExists(filePath) {
16
+ return fs.existsSync(filePath);
17
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@taj-special/dravix-code",
3
+ "version": "1.1.1",
4
+ "description": "AI-powered coding assistant CLI — Dravix Code",
5
+ "type": "module",
6
+ "bin": {
7
+ "dravix": "dist/cli/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "start": "node dist/cli/index.js",
15
+ "dev": "tsc --watch",
16
+ "prepublishOnly": "tsc"
17
+ },
18
+ "dependencies": {
19
+ "chalk": "^5.3.0"
20
+ },
21
+ "devDependencies": {
22
+ "typescript": "^5.4.0",
23
+ "@types/node": "^20.0.0"
24
+ },
25
+ "keywords": [
26
+ "ai",
27
+ "cli",
28
+ "coding-assistant",
29
+ "dravix"
30
+ ],
31
+ "license": "MIT",
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ }
35
+ }