grokcodecli 0.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.
Files changed (99) hide show
  1. package/.claude/settings.local.json +32 -0
  2. package/README.md +1464 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +61 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/loader.d.ts +34 -0
  8. package/dist/commands/loader.d.ts.map +1 -0
  9. package/dist/commands/loader.js +192 -0
  10. package/dist/commands/loader.js.map +1 -0
  11. package/dist/config/manager.d.ts +21 -0
  12. package/dist/config/manager.d.ts.map +1 -0
  13. package/dist/config/manager.js +203 -0
  14. package/dist/config/manager.js.map +1 -0
  15. package/dist/conversation/chat.d.ts +50 -0
  16. package/dist/conversation/chat.d.ts.map +1 -0
  17. package/dist/conversation/chat.js +1145 -0
  18. package/dist/conversation/chat.js.map +1 -0
  19. package/dist/conversation/history.d.ts +24 -0
  20. package/dist/conversation/history.d.ts.map +1 -0
  21. package/dist/conversation/history.js +103 -0
  22. package/dist/conversation/history.js.map +1 -0
  23. package/dist/grok/client.d.ts +86 -0
  24. package/dist/grok/client.d.ts.map +1 -0
  25. package/dist/grok/client.js +106 -0
  26. package/dist/grok/client.js.map +1 -0
  27. package/dist/index.d.ts +7 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +8 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/permissions/manager.d.ts +26 -0
  32. package/dist/permissions/manager.d.ts.map +1 -0
  33. package/dist/permissions/manager.js +170 -0
  34. package/dist/permissions/manager.js.map +1 -0
  35. package/dist/tools/bash.d.ts +8 -0
  36. package/dist/tools/bash.d.ts.map +1 -0
  37. package/dist/tools/bash.js +102 -0
  38. package/dist/tools/bash.js.map +1 -0
  39. package/dist/tools/edit.d.ts +9 -0
  40. package/dist/tools/edit.d.ts.map +1 -0
  41. package/dist/tools/edit.js +61 -0
  42. package/dist/tools/edit.js.map +1 -0
  43. package/dist/tools/glob.d.ts +7 -0
  44. package/dist/tools/glob.d.ts.map +1 -0
  45. package/dist/tools/glob.js +38 -0
  46. package/dist/tools/glob.js.map +1 -0
  47. package/dist/tools/grep.d.ts +8 -0
  48. package/dist/tools/grep.d.ts.map +1 -0
  49. package/dist/tools/grep.js +78 -0
  50. package/dist/tools/grep.js.map +1 -0
  51. package/dist/tools/read.d.ts +8 -0
  52. package/dist/tools/read.d.ts.map +1 -0
  53. package/dist/tools/read.js +96 -0
  54. package/dist/tools/read.js.map +1 -0
  55. package/dist/tools/registry.d.ts +42 -0
  56. package/dist/tools/registry.d.ts.map +1 -0
  57. package/dist/tools/registry.js +230 -0
  58. package/dist/tools/registry.js.map +1 -0
  59. package/dist/tools/webfetch.d.ts +10 -0
  60. package/dist/tools/webfetch.d.ts.map +1 -0
  61. package/dist/tools/webfetch.js +108 -0
  62. package/dist/tools/webfetch.js.map +1 -0
  63. package/dist/tools/websearch.d.ts +7 -0
  64. package/dist/tools/websearch.d.ts.map +1 -0
  65. package/dist/tools/websearch.js +180 -0
  66. package/dist/tools/websearch.js.map +1 -0
  67. package/dist/tools/write.d.ts +7 -0
  68. package/dist/tools/write.d.ts.map +1 -0
  69. package/dist/tools/write.js +80 -0
  70. package/dist/tools/write.js.map +1 -0
  71. package/dist/utils/security.d.ts +36 -0
  72. package/dist/utils/security.d.ts.map +1 -0
  73. package/dist/utils/security.js +227 -0
  74. package/dist/utils/security.js.map +1 -0
  75. package/dist/utils/ui.d.ts +49 -0
  76. package/dist/utils/ui.d.ts.map +1 -0
  77. package/dist/utils/ui.js +302 -0
  78. package/dist/utils/ui.js.map +1 -0
  79. package/package.json +45 -0
  80. package/src/cli.ts +68 -0
  81. package/src/commands/loader.ts +244 -0
  82. package/src/config/manager.ts +239 -0
  83. package/src/conversation/chat.ts +1294 -0
  84. package/src/conversation/history.ts +131 -0
  85. package/src/grok/client.ts +192 -0
  86. package/src/index.ts +8 -0
  87. package/src/permissions/manager.ts +208 -0
  88. package/src/tools/bash.ts +119 -0
  89. package/src/tools/edit.ts +73 -0
  90. package/src/tools/glob.ts +49 -0
  91. package/src/tools/grep.ts +96 -0
  92. package/src/tools/read.ts +116 -0
  93. package/src/tools/registry.ts +248 -0
  94. package/src/tools/webfetch.ts +127 -0
  95. package/src/tools/websearch.ts +219 -0
  96. package/src/tools/write.ts +94 -0
  97. package/src/utils/security.ts +259 -0
  98. package/src/utils/ui.ts +382 -0
  99. package/tsconfig.json +22 -0
@@ -0,0 +1,382 @@
1
+ /**
2
+ * UI Utilities
3
+ *
4
+ * Beautiful terminal output, syntax highlighting, progress indicators,
5
+ * and user experience enhancements.
6
+ */
7
+
8
+ import chalk from 'chalk';
9
+
10
+ // ============================================================================
11
+ // Box Drawing & Borders
12
+ // ============================================================================
13
+
14
+ export const BOX = {
15
+ topLeft: '╭',
16
+ topRight: '╮',
17
+ bottomLeft: '╰',
18
+ bottomRight: '╯',
19
+ horizontal: '─',
20
+ vertical: '│',
21
+ teeRight: '├',
22
+ teeLeft: '┤',
23
+ teeDown: '┬',
24
+ teeUp: '┴',
25
+ cross: '┼',
26
+ };
27
+
28
+ export function drawBox(content: string[], options: {
29
+ title?: string;
30
+ width?: number;
31
+ padding?: number;
32
+ borderColor?: typeof chalk;
33
+ } = {}): string {
34
+ const {
35
+ title = '',
36
+ width = Math.max(...content.map(l => stripAnsi(l).length), title.length) + 4,
37
+ padding = 1,
38
+ borderColor = chalk.cyan,
39
+ } = options;
40
+
41
+ const innerWidth = width - 2;
42
+ const lines: string[] = [];
43
+
44
+ // Top border
45
+ if (title) {
46
+ const titlePadded = ` ${title} `;
47
+ const leftPad = Math.floor((innerWidth - titlePadded.length) / 2);
48
+ const rightPad = innerWidth - leftPad - titlePadded.length;
49
+ lines.push(
50
+ borderColor(BOX.topLeft) +
51
+ borderColor(BOX.horizontal.repeat(leftPad)) +
52
+ chalk.bold(titlePadded) +
53
+ borderColor(BOX.horizontal.repeat(rightPad)) +
54
+ borderColor(BOX.topRight)
55
+ );
56
+ } else {
57
+ lines.push(
58
+ borderColor(BOX.topLeft + BOX.horizontal.repeat(innerWidth) + BOX.topRight)
59
+ );
60
+ }
61
+
62
+ // Padding top
63
+ for (let i = 0; i < padding; i++) {
64
+ lines.push(borderColor(BOX.vertical) + ' '.repeat(innerWidth) + borderColor(BOX.vertical));
65
+ }
66
+
67
+ // Content
68
+ for (const line of content) {
69
+ const stripped = stripAnsi(line);
70
+ const pad = innerWidth - stripped.length - 2;
71
+ lines.push(
72
+ borderColor(BOX.vertical) +
73
+ ' ' + line + ' '.repeat(Math.max(0, pad + 1)) +
74
+ borderColor(BOX.vertical)
75
+ );
76
+ }
77
+
78
+ // Padding bottom
79
+ for (let i = 0; i < padding; i++) {
80
+ lines.push(borderColor(BOX.vertical) + ' '.repeat(innerWidth) + borderColor(BOX.vertical));
81
+ }
82
+
83
+ // Bottom border
84
+ lines.push(
85
+ borderColor(BOX.bottomLeft + BOX.horizontal.repeat(innerWidth) + BOX.bottomRight)
86
+ );
87
+
88
+ return lines.join('\n');
89
+ }
90
+
91
+ // ============================================================================
92
+ // Syntax Highlighting
93
+ // ============================================================================
94
+
95
+ const LANGUAGE_KEYWORDS: Record<string, string[]> = {
96
+ typescript: ['const', 'let', 'var', 'function', 'class', 'interface', 'type', 'import', 'export', 'from', 'return', 'if', 'else', 'for', 'while', 'async', 'await', 'new', 'this', 'extends', 'implements', 'private', 'public', 'protected', 'static', 'readonly'],
97
+ javascript: ['const', 'let', 'var', 'function', 'class', 'import', 'export', 'from', 'return', 'if', 'else', 'for', 'while', 'async', 'await', 'new', 'this', 'extends'],
98
+ python: ['def', 'class', 'import', 'from', 'return', 'if', 'elif', 'else', 'for', 'while', 'try', 'except', 'finally', 'with', 'as', 'yield', 'lambda', 'None', 'True', 'False', 'and', 'or', 'not', 'in', 'is', 'async', 'await'],
99
+ rust: ['fn', 'let', 'mut', 'const', 'struct', 'enum', 'impl', 'trait', 'pub', 'use', 'mod', 'match', 'if', 'else', 'for', 'while', 'loop', 'return', 'async', 'await', 'self', 'Self'],
100
+ go: ['func', 'var', 'const', 'type', 'struct', 'interface', 'package', 'import', 'return', 'if', 'else', 'for', 'range', 'switch', 'case', 'default', 'defer', 'go', 'chan', 'map', 'make', 'new'],
101
+ bash: ['if', 'then', 'else', 'elif', 'fi', 'for', 'do', 'done', 'while', 'case', 'esac', 'function', 'return', 'exit', 'export', 'local', 'readonly'],
102
+ };
103
+
104
+ const LANG_ALIASES: Record<string, string> = {
105
+ ts: 'typescript',
106
+ js: 'javascript',
107
+ py: 'python',
108
+ rs: 'rust',
109
+ sh: 'bash',
110
+ shell: 'bash',
111
+ zsh: 'bash',
112
+ };
113
+
114
+ export function highlightCode(code: string, language?: string): string {
115
+ const lang = language ? (LANG_ALIASES[language] || language).toLowerCase() : '';
116
+ const keywords = LANGUAGE_KEYWORDS[lang] || [];
117
+
118
+ let result = code;
119
+
120
+ // Highlight strings
121
+ result = result.replace(/(["'`])(?:(?!\1)[^\\]|\\.)*\1/g, chalk.green('$&'));
122
+
123
+ // Highlight numbers
124
+ result = result.replace(/\b(\d+\.?\d*)\b/g, chalk.yellow('$1'));
125
+
126
+ // Highlight comments
127
+ result = result.replace(/(\/\/.*$|#.*$)/gm, chalk.gray('$1'));
128
+ result = result.replace(/(\/\*[\s\S]*?\*\/)/g, chalk.gray('$1'));
129
+
130
+ // Highlight keywords
131
+ for (const keyword of keywords) {
132
+ const regex = new RegExp(`\\b(${keyword})\\b`, 'g');
133
+ result = result.replace(regex, chalk.magenta('$1'));
134
+ }
135
+
136
+ // Highlight function calls
137
+ result = result.replace(/\b([a-zA-Z_]\w*)\s*\(/g, chalk.blue('$1') + '(');
138
+
139
+ return result;
140
+ }
141
+
142
+ export function formatCodeBlock(code: string, language?: string, showLineNumbers = true): string {
143
+ const highlighted = highlightCode(code, language);
144
+ const lines = highlighted.split('\n');
145
+
146
+ const header = chalk.gray(`─── ${language || 'code'} ${'─'.repeat(Math.max(0, 40 - (language?.length || 4)))}`);
147
+ const footer = chalk.gray('─'.repeat(45));
148
+
149
+ if (showLineNumbers) {
150
+ const padding = String(lines.length).length;
151
+ const numberedLines = lines.map((line, i) =>
152
+ chalk.gray(String(i + 1).padStart(padding) + ' │ ') + line
153
+ );
154
+ return `${header}\n${numberedLines.join('\n')}\n${footer}`;
155
+ }
156
+
157
+ return `${header}\n${highlighted}\n${footer}`;
158
+ }
159
+
160
+ // ============================================================================
161
+ // Progress & Status Indicators
162
+ // ============================================================================
163
+
164
+ export function progressBar(current: number, total: number, width = 30): string {
165
+ const percent = Math.min(100, Math.round((current / total) * 100));
166
+ const filled = Math.round((percent / 100) * width);
167
+ const empty = width - filled;
168
+
169
+ const bar = chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
170
+ return `[${bar}] ${percent}%`;
171
+ }
172
+
173
+ export function spinner(): { start: () => void; stop: (success?: boolean) => void; update: (text: string) => void } {
174
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
175
+ let i = 0;
176
+ let interval: NodeJS.Timeout | null = null;
177
+ let text = '';
178
+
179
+ return {
180
+ start() {
181
+ interval = setInterval(() => {
182
+ process.stdout.write(`\r${chalk.cyan(frames[i])} ${text}`);
183
+ i = (i + 1) % frames.length;
184
+ }, 80);
185
+ },
186
+ stop(success = true) {
187
+ if (interval) {
188
+ clearInterval(interval);
189
+ const icon = success ? chalk.green('✓') : chalk.red('✗');
190
+ process.stdout.write(`\r${icon} ${text}\n`);
191
+ }
192
+ },
193
+ update(newText: string) {
194
+ text = newText;
195
+ },
196
+ };
197
+ }
198
+
199
+ // ============================================================================
200
+ // Badges & Tags
201
+ // ============================================================================
202
+
203
+ export function badge(text: string, type: 'info' | 'success' | 'warning' | 'error' = 'info'): string {
204
+ const colors = {
205
+ info: chalk.bgCyan.black,
206
+ success: chalk.bgGreen.black,
207
+ warning: chalk.bgYellow.black,
208
+ error: chalk.bgRed.white,
209
+ };
210
+ return colors[type](` ${text} `);
211
+ }
212
+
213
+ export function tag(text: string, color: 'cyan' | 'green' | 'yellow' | 'red' | 'blue' | 'magenta' = 'cyan'): string {
214
+ const colors = {
215
+ cyan: chalk.cyan,
216
+ green: chalk.green,
217
+ yellow: chalk.yellow,
218
+ red: chalk.red,
219
+ blue: chalk.blue,
220
+ magenta: chalk.magenta,
221
+ };
222
+ return colors[color](`[${text}]`);
223
+ }
224
+
225
+ // ============================================================================
226
+ // Welcome Screen & Branding
227
+ // ============================================================================
228
+
229
+ export function welcomeScreen(version: string, model: string, cwd: string): string {
230
+ const logo = `
231
+ ██████╗ ██████╗ ██████╗ ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗
232
+ ██╔════╝ ██╔══██╗██╔═══██╗██║ ██╔╝ ██╔════╝██╔═══██╗██╔══██╗██╔════╝
233
+ ██║ ███╗██████╔╝██║ ██║█████╔╝ ██║ ██║ ██║██║ ██║█████╗
234
+ ██║ ██║██╔══██╗██║ ██║██╔═██╗ ██║ ██║ ██║██║ ██║██╔══╝
235
+ ╚██████╔╝██║ ██║╚██████╔╝██║ ██╗ ╚██████╗╚██████╔╝██████╔╝███████╗
236
+ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝`;
237
+
238
+ const info = [
239
+ '',
240
+ ` ${chalk.gray('Version:')} ${chalk.cyan(version)}`,
241
+ ` ${chalk.gray('Model:')} ${chalk.green(model)}`,
242
+ ` ${chalk.gray('CWD:')} ${chalk.blue(cwd)}`,
243
+ '',
244
+ ` ${chalk.gray('Type')} ${chalk.cyan('/help')} ${chalk.gray('for commands,')} ${chalk.yellow('exit')} ${chalk.gray('to quit')}`,
245
+ '',
246
+ ];
247
+
248
+ return chalk.cyan(logo) + info.join('\n');
249
+ }
250
+
251
+ export function compactWelcome(version: string, model: string): string {
252
+ return `
253
+ ${chalk.cyan('╭─────────────────────────────────────────────╮')}
254
+ ${chalk.cyan('│')} ${chalk.bold.cyan('🚀 Grok Code CLI')} ${chalk.gray(`v${version}`)} ${chalk.cyan('│')}
255
+ ${chalk.cyan('│')} ${chalk.gray('Model:')} ${chalk.green(model.padEnd(32))} ${chalk.cyan('│')}
256
+ ${chalk.cyan('│')} ${chalk.gray('Type /help for commands, exit to quit')} ${chalk.cyan('│')}
257
+ ${chalk.cyan('╰─────────────────────────────────────────────╯')}
258
+ `;
259
+ }
260
+
261
+ // ============================================================================
262
+ // Tips & Hints
263
+ // ============================================================================
264
+
265
+ const TIPS = [
266
+ 'Use /compact to reduce context when conversations get long',
267
+ 'Press Ctrl+C to cancel a running command',
268
+ 'Use /export conversation.md to save your chat',
269
+ 'The /doctor command checks your setup for issues',
270
+ 'You can resume previous sessions with /resume',
271
+ 'Use /model to switch between Grok 4, Grok 3 and specialized models',
272
+ 'The /context command shows how much context you\'re using',
273
+ 'Session are auto-saved - you can always pick up where you left off',
274
+ 'Use /history to see your recent conversations',
275
+ 'The Read, Write, and Edit tools work on any file type',
276
+ ];
277
+
278
+ export function randomTip(): string {
279
+ const tip = TIPS[Math.floor(Math.random() * TIPS.length)];
280
+ return chalk.gray(`💡 Tip: ${tip}`);
281
+ }
282
+
283
+ // ============================================================================
284
+ // Tables
285
+ // ============================================================================
286
+
287
+ export function table(headers: string[], rows: string[][], options: {
288
+ padding?: number;
289
+ headerColor?: typeof chalk;
290
+ } = {}): string {
291
+ const { padding = 1, headerColor = chalk.bold.cyan } = options;
292
+
293
+ // Calculate column widths
294
+ const colWidths = headers.map((h, i) => {
295
+ const maxContent = Math.max(h.length, ...rows.map(r => stripAnsi(r[i] || '').length));
296
+ return maxContent + padding * 2;
297
+ });
298
+
299
+ // Build separator
300
+ const separator = BOX.horizontal;
301
+ const topBorder = BOX.topLeft + colWidths.map(w => separator.repeat(w)).join(BOX.teeDown) + BOX.topRight;
302
+ const midBorder = BOX.teeRight + colWidths.map(w => separator.repeat(w)).join(BOX.cross) + BOX.teeLeft;
303
+ const bottomBorder = BOX.bottomLeft + colWidths.map(w => separator.repeat(w)).join(BOX.teeUp) + BOX.bottomRight;
304
+
305
+ // Build header row
306
+ const headerRow = BOX.vertical + headers.map((h, i) => {
307
+ const padded = h.padStart(Math.floor((colWidths[i] + h.length) / 2)).padEnd(colWidths[i]);
308
+ return headerColor(padded);
309
+ }).join(BOX.vertical) + BOX.vertical;
310
+
311
+ // Build content rows
312
+ const contentRows = rows.map(row =>
313
+ BOX.vertical + row.map((cell, i) => {
314
+ const stripped = stripAnsi(cell || '');
315
+ const padded = ' '.repeat(padding) + cell + ' '.repeat(colWidths[i] - stripped.length - padding);
316
+ return padded;
317
+ }).join(BOX.vertical) + BOX.vertical
318
+ );
319
+
320
+ return [topBorder, headerRow, midBorder, ...contentRows, bottomBorder].join('\n');
321
+ }
322
+
323
+ // ============================================================================
324
+ // Diff Display
325
+ // ============================================================================
326
+
327
+ export function formatDiff(oldContent: string, newContent: string): string {
328
+ const oldLines = oldContent.split('\n');
329
+ const newLines = newContent.split('\n');
330
+
331
+ const lines: string[] = [];
332
+
333
+ let oldIdx = 0;
334
+ let newIdx = 0;
335
+
336
+ while (oldIdx < oldLines.length || newIdx < newLines.length) {
337
+ if (oldIdx >= oldLines.length) {
338
+ lines.push(chalk.green(`+ ${newLines[newIdx]}`));
339
+ newIdx++;
340
+ } else if (newIdx >= newLines.length) {
341
+ lines.push(chalk.red(`- ${oldLines[oldIdx]}`));
342
+ oldIdx++;
343
+ } else if (oldLines[oldIdx] === newLines[newIdx]) {
344
+ lines.push(chalk.gray(` ${oldLines[oldIdx]}`));
345
+ oldIdx++;
346
+ newIdx++;
347
+ } else {
348
+ lines.push(chalk.red(`- ${oldLines[oldIdx]}`));
349
+ lines.push(chalk.green(`+ ${newLines[newIdx]}`));
350
+ oldIdx++;
351
+ newIdx++;
352
+ }
353
+ }
354
+
355
+ return lines.join('\n');
356
+ }
357
+
358
+ // ============================================================================
359
+ // Helpers
360
+ // ============================================================================
361
+
362
+ function stripAnsi(str: string): string {
363
+ return str.replace(/\x1b\[[0-9;]*m/g, '');
364
+ }
365
+
366
+ export function truncate(str: string, maxLength: number, suffix = '...'): string {
367
+ if (str.length <= maxLength) return str;
368
+ return str.slice(0, maxLength - suffix.length) + suffix;
369
+ }
370
+
371
+ export function indent(text: string, spaces = 2): string {
372
+ const pad = ' '.repeat(spaces);
373
+ return text.split('\n').map(line => pad + line).join('\n');
374
+ }
375
+
376
+ export function divider(char = '─', length = 50): string {
377
+ return chalk.gray(char.repeat(length));
378
+ }
379
+
380
+ export function timestamp(): string {
381
+ return chalk.gray(new Date().toLocaleTimeString());
382
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2022"],
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "sourceMap": true,
17
+ "jsx": "react-jsx",
18
+ "jsxImportSource": "react"
19
+ },
20
+ "include": ["src/**/*"],
21
+ "exclude": ["node_modules", "dist"]
22
+ }