prab-cli 1.1.0 → 1.2.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.
package/dist/lib/ui.js CHANGED
@@ -21,33 +21,147 @@ const syntaxColors = {
21
21
  // Common keywords for different languages
22
22
  const keywords = new Set([
23
23
  // JavaScript/TypeScript
24
- 'const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'do',
25
- 'switch', 'case', 'break', 'continue', 'try', 'catch', 'finally', 'throw',
26
- 'class', 'extends', 'new', 'this', 'super', 'import', 'export', 'from', 'as',
27
- 'default', 'async', 'await', 'yield', 'typeof', 'instanceof', 'in', 'of',
28
- 'true', 'false', 'null', 'undefined', 'void', 'delete', 'static', 'get', 'set',
29
- 'interface', 'type', 'enum', 'implements', 'private', 'public', 'protected',
24
+ "const",
25
+ "let",
26
+ "var",
27
+ "function",
28
+ "return",
29
+ "if",
30
+ "else",
31
+ "for",
32
+ "while",
33
+ "do",
34
+ "switch",
35
+ "case",
36
+ "break",
37
+ "continue",
38
+ "try",
39
+ "catch",
40
+ "finally",
41
+ "throw",
42
+ "class",
43
+ "extends",
44
+ "new",
45
+ "this",
46
+ "super",
47
+ "import",
48
+ "export",
49
+ "from",
50
+ "as",
51
+ "default",
52
+ "async",
53
+ "await",
54
+ "yield",
55
+ "typeof",
56
+ "instanceof",
57
+ "in",
58
+ "of",
59
+ "true",
60
+ "false",
61
+ "null",
62
+ "undefined",
63
+ "void",
64
+ "delete",
65
+ "static",
66
+ "get",
67
+ "set",
68
+ "interface",
69
+ "type",
70
+ "enum",
71
+ "implements",
72
+ "private",
73
+ "public",
74
+ "protected",
30
75
  // Python
31
- 'def', 'class', 'import', 'from', 'as', 'if', 'elif', 'else', 'for', 'while',
32
- 'try', 'except', 'finally', 'raise', 'with', 'lambda', 'return', 'yield',
33
- 'True', 'False', 'None', 'and', 'or', 'not', 'is', 'in', 'pass', 'global',
34
- 'nonlocal', 'assert', 'break', 'continue', 'self', 'async', 'await',
76
+ "def",
77
+ "class",
78
+ "import",
79
+ "from",
80
+ "as",
81
+ "if",
82
+ "elif",
83
+ "else",
84
+ "for",
85
+ "while",
86
+ "try",
87
+ "except",
88
+ "finally",
89
+ "raise",
90
+ "with",
91
+ "lambda",
92
+ "return",
93
+ "yield",
94
+ "True",
95
+ "False",
96
+ "None",
97
+ "and",
98
+ "or",
99
+ "not",
100
+ "is",
101
+ "in",
102
+ "pass",
103
+ "global",
104
+ "nonlocal",
105
+ "assert",
106
+ "break",
107
+ "continue",
108
+ "self",
109
+ "async",
110
+ "await",
35
111
  // Go
36
- 'func', 'package', 'import', 'type', 'struct', 'interface', 'map', 'chan',
37
- 'go', 'defer', 'select', 'range', 'make', 'append', 'len', 'cap', 'nil',
112
+ "func",
113
+ "package",
114
+ "import",
115
+ "type",
116
+ "struct",
117
+ "interface",
118
+ "map",
119
+ "chan",
120
+ "go",
121
+ "defer",
122
+ "select",
123
+ "range",
124
+ "make",
125
+ "append",
126
+ "len",
127
+ "cap",
128
+ "nil",
38
129
  // Rust
39
- 'fn', 'let', 'mut', 'pub', 'mod', 'use', 'struct', 'enum', 'impl', 'trait',
40
- 'match', 'loop', 'move', 'ref', 'self', 'Self', 'where', 'unsafe', 'async',
130
+ "fn",
131
+ "let",
132
+ "mut",
133
+ "pub",
134
+ "mod",
135
+ "use",
136
+ "struct",
137
+ "enum",
138
+ "impl",
139
+ "trait",
140
+ "match",
141
+ "loop",
142
+ "move",
143
+ "ref",
144
+ "self",
145
+ "Self",
146
+ "where",
147
+ "unsafe",
148
+ "async",
41
149
  // Common
42
- 'print', 'println', 'printf', 'console', 'log', 'error', 'warn',
150
+ "print",
151
+ "println",
152
+ "printf",
153
+ "console",
154
+ "log",
155
+ "error",
156
+ "warn",
43
157
  ]);
44
158
  /**
45
159
  * Truncate output for brief display
46
160
  */
47
161
  const truncateOutput = (text, maxLen) => {
48
- const firstLine = text.split('\n')[0];
162
+ const firstLine = text.split("\n")[0];
49
163
  if (firstLine.length > maxLen) {
50
- return firstLine.substring(0, maxLen) + '...';
164
+ return firstLine.substring(0, maxLen) + "...";
51
165
  }
52
166
  return firstLine;
53
167
  };
@@ -55,97 +169,102 @@ const truncateOutput = (text, maxLen) => {
55
169
  * Format diff output with colors
56
170
  */
57
171
  const formatDiffOutput = (output) => {
58
- const lines = output.split('\n');
59
- return lines.map(line => {
172
+ const lines = output.split("\n");
173
+ return lines
174
+ .map((line) => {
60
175
  // File headers
61
- if (line.startsWith('diff --git')) {
176
+ if (line.startsWith("diff --git")) {
62
177
  return chalk_1.default.bold.white(line);
63
178
  }
64
- if (line.startsWith('index ')) {
179
+ if (line.startsWith("index ")) {
65
180
  return chalk_1.default.gray(line);
66
181
  }
67
- if (line.startsWith('---')) {
182
+ if (line.startsWith("---")) {
68
183
  return chalk_1.default.red.bold(line);
69
184
  }
70
- if (line.startsWith('+++')) {
185
+ if (line.startsWith("+++")) {
71
186
  return chalk_1.default.green.bold(line);
72
187
  }
73
188
  // Hunk headers
74
- if (line.startsWith('@@')) {
189
+ if (line.startsWith("@@")) {
75
190
  return chalk_1.default.cyan(line);
76
191
  }
77
192
  // Added lines
78
- if (line.startsWith('+')) {
193
+ if (line.startsWith("+")) {
79
194
  return chalk_1.default.green(line);
80
195
  }
81
196
  // Removed lines
82
- if (line.startsWith('-')) {
197
+ if (line.startsWith("-")) {
83
198
  return chalk_1.default.red(line);
84
199
  }
85
200
  // Context lines
86
201
  return chalk_1.default.gray(line);
87
- }).join('\n');
202
+ })
203
+ .join("\n");
88
204
  };
89
205
  /**
90
206
  * Format git log output with colors
91
207
  */
92
208
  const formatGitLogOutput = (output) => {
93
- const lines = output.split('\n');
94
- return lines.map(line => {
209
+ const lines = output.split("\n");
210
+ return lines
211
+ .map((line) => {
95
212
  // Commit hash
96
213
  if (line.match(/^[a-f0-9]{7,40}\s/)) {
97
- const parts = line.split(' ');
214
+ const parts = line.split(" ");
98
215
  const hash = parts[0];
99
- const rest = parts.slice(1).join(' ');
100
- return chalk_1.default.yellow(hash) + ' ' + rest;
216
+ const rest = parts.slice(1).join(" ");
217
+ return chalk_1.default.yellow(hash) + " " + rest;
101
218
  }
102
219
  // Date lines
103
220
  if (line.match(/^\d{4}-\d{2}-\d{2}/)) {
104
221
  return chalk_1.default.blue(line);
105
222
  }
106
223
  // Author
107
- if (line.toLowerCase().includes('author:')) {
224
+ if (line.toLowerCase().includes("author:")) {
108
225
  return chalk_1.default.cyan(line);
109
226
  }
110
227
  // Commit message (indented)
111
- if (line.startsWith(' ')) {
228
+ if (line.startsWith(" ")) {
112
229
  return chalk_1.default.white(line);
113
230
  }
114
231
  return line;
115
- }).join('\n');
232
+ })
233
+ .join("\n");
116
234
  };
117
235
  /**
118
236
  * Format git status output with colors
119
237
  */
120
238
  const formatGitStatusOutput = (output) => {
121
- const lines = output.split('\n');
122
- return lines.map(line => {
239
+ const lines = output.split("\n");
240
+ return lines
241
+ .map((line) => {
123
242
  // Branch info
124
- if (line.startsWith('On branch') || line.startsWith('HEAD detached')) {
243
+ if (line.startsWith("On branch") || line.startsWith("HEAD detached")) {
125
244
  return chalk_1.default.cyan.bold(line);
126
245
  }
127
246
  // Modified files
128
- if (line.includes('modified:')) {
247
+ if (line.includes("modified:")) {
129
248
  return chalk_1.default.yellow(line);
130
249
  }
131
250
  // New files
132
- if (line.includes('new file:')) {
251
+ if (line.includes("new file:")) {
133
252
  return chalk_1.default.green(line);
134
253
  }
135
254
  // Deleted files
136
- if (line.includes('deleted:')) {
255
+ if (line.includes("deleted:")) {
137
256
  return chalk_1.default.red(line);
138
257
  }
139
258
  // Untracked files header
140
- if (line.includes('Untracked files:')) {
259
+ if (line.includes("Untracked files:")) {
141
260
  return chalk_1.default.magenta.bold(line);
142
261
  }
143
262
  // Staged changes header
144
- if (line.includes('Changes to be committed:')) {
263
+ if (line.includes("Changes to be committed:")) {
145
264
  return chalk_1.default.green.bold(line);
146
265
  }
147
266
  // Unstaged changes header
148
- if (line.includes('Changes not staged')) {
267
+ if (line.includes("Changes not staged")) {
149
268
  return chalk_1.default.yellow.bold(line);
150
269
  }
151
270
  // File status indicators (M, A, D, ??)
@@ -155,19 +274,20 @@ const formatGitStatusOutput = (output) => {
155
274
  const indicator = status[1];
156
275
  const filename = status[2];
157
276
  let color = chalk_1.default.white;
158
- if (indicator.includes('M'))
277
+ if (indicator.includes("M"))
159
278
  color = chalk_1.default.yellow;
160
- if (indicator.includes('A'))
279
+ if (indicator.includes("A"))
161
280
  color = chalk_1.default.green;
162
- if (indicator.includes('D'))
281
+ if (indicator.includes("D"))
163
282
  color = chalk_1.default.red;
164
- if (indicator.includes('?'))
283
+ if (indicator.includes("?"))
165
284
  color = chalk_1.default.gray;
166
285
  return color(` ${indicator} ${filename}`);
167
286
  }
168
287
  }
169
288
  return chalk_1.default.gray(line);
170
- }).join('\n');
289
+ })
290
+ .join("\n");
171
291
  };
172
292
  /**
173
293
  * Apply syntax highlighting to a line of code
@@ -200,21 +320,24 @@ const highlightCodeLine = (line) => {
200
320
  /**
201
321
  * Format file content with line numbers and syntax highlighting
202
322
  */
203
- const formatFileContent = (content, filename) => {
204
- const lines = content.split('\n');
323
+ const formatFileContent = (content, _filename) => {
324
+ const lines = content.split("\n");
205
325
  const lineNumWidth = String(lines.length).length;
206
- return lines.map((line, idx) => {
207
- const lineNum = String(idx + 1).padStart(lineNumWidth, ' ');
326
+ return lines
327
+ .map((line, idx) => {
328
+ const lineNum = String(idx + 1).padStart(lineNumWidth, " ");
208
329
  const highlighted = highlightCodeLine(line);
209
330
  return chalk_1.default.gray(`${lineNum} │ `) + highlighted;
210
- }).join('\n');
331
+ })
332
+ .join("\n");
211
333
  };
212
334
  /**
213
335
  * Format bash command output
214
336
  */
215
337
  const formatBashOutput = (output) => {
216
338
  // Check if it looks like a diff
217
- if (output.includes('diff --git') || output.includes('@@') && (output.includes('+') || output.includes('-'))) {
339
+ if (output.includes("diff --git") ||
340
+ (output.includes("@@") && (output.includes("+") || output.includes("-")))) {
218
341
  return formatDiffOutput(output);
219
342
  }
220
343
  // Check if it looks like git log
@@ -222,18 +345,21 @@ const formatBashOutput = (output) => {
222
345
  return formatGitLogOutput(output);
223
346
  }
224
347
  // Generic output with some highlighting
225
- const lines = output.split('\n');
226
- return lines.map(line => {
348
+ const lines = output.split("\n");
349
+ return lines
350
+ .map((line) => {
227
351
  // Error messages
228
- if (line.toLowerCase().includes('error') || line.toLowerCase().includes('failed')) {
352
+ if (line.toLowerCase().includes("error") || line.toLowerCase().includes("failed")) {
229
353
  return chalk_1.default.red(line);
230
354
  }
231
355
  // Warning messages
232
- if (line.toLowerCase().includes('warning') || line.toLowerCase().includes('warn')) {
356
+ if (line.toLowerCase().includes("warning") || line.toLowerCase().includes("warn")) {
233
357
  return chalk_1.default.yellow(line);
234
358
  }
235
359
  // Success messages
236
- if (line.toLowerCase().includes('success') || line.toLowerCase().includes('done') || line.toLowerCase().includes('passed')) {
360
+ if (line.toLowerCase().includes("success") ||
361
+ line.toLowerCase().includes("done") ||
362
+ line.toLowerCase().includes("passed")) {
237
363
  return chalk_1.default.green(line);
238
364
  }
239
365
  // Paths
@@ -241,53 +367,56 @@ const formatBashOutput = (output) => {
241
367
  return chalk_1.default.cyan(line);
242
368
  }
243
369
  return line;
244
- }).join('\n');
370
+ })
371
+ .join("\n");
245
372
  };
246
373
  /**
247
374
  * Format tool output based on tool type
248
375
  */
249
376
  const formatToolOutput = (toolName, output) => {
250
- if (!output || output.trim() === '') {
251
- return chalk_1.default.gray(' (no output)');
377
+ if (!output || output.trim() === "") {
378
+ return chalk_1.default.gray(" (no output)");
252
379
  }
253
- const separator = chalk_1.default.gray('─'.repeat(60));
254
380
  let formatted;
255
381
  switch (toolName.toLowerCase()) {
256
- case 'git_diff':
257
- case 'gitdiff':
382
+ case "git_diff":
383
+ case "gitdiff":
258
384
  formatted = formatDiffOutput(output);
259
385
  break;
260
- case 'git_log':
261
- case 'gitlog':
386
+ case "git_log":
387
+ case "gitlog":
262
388
  formatted = formatGitLogOutput(output);
263
389
  break;
264
- case 'git_status':
265
- case 'gitstatus':
390
+ case "git_status":
391
+ case "gitstatus":
266
392
  formatted = formatGitStatusOutput(output);
267
393
  break;
268
- case 'read_file':
269
- case 'readfile':
394
+ case "read_file":
395
+ case "readfile":
270
396
  formatted = formatFileContent(output);
271
397
  break;
272
- case 'bash':
273
- case 'shell':
398
+ case "bash":
399
+ case "shell":
274
400
  formatted = formatBashOutput(output);
275
401
  break;
276
- case 'glob':
277
- case 'grep':
402
+ case "glob":
403
+ case "grep":
278
404
  // File lists - highlight paths
279
- formatted = output.split('\n').map(line => {
280
- if (line.includes(':')) {
281
- const [path, ...rest] = line.split(':');
282
- return chalk_1.default.cyan(path) + ':' + chalk_1.default.white(rest.join(':'));
405
+ formatted = output
406
+ .split("\n")
407
+ .map((line) => {
408
+ if (line.includes(":")) {
409
+ const [path, ...rest] = line.split(":");
410
+ return chalk_1.default.cyan(path) + ":" + chalk_1.default.white(rest.join(":"));
283
411
  }
284
412
  return chalk_1.default.cyan(line);
285
- }).join('\n');
413
+ })
414
+ .join("\n");
286
415
  break;
287
416
  default:
288
417
  formatted = output;
289
418
  }
290
- return `\n${chalk_1.default.gray('Output:')}\n${formatted}`;
419
+ return `\n${chalk_1.default.gray("Output:")}\n${formatted}`;
291
420
  };
292
421
  exports.log = {
293
422
  info: (msg) => console.log(chalk_1.default.blue("ℹ"), msg),
@@ -339,11 +468,7 @@ const showDiff = (before, after, filename) => {
339
468
  }
340
469
  const diff = (0, diff_1.diffLines)(before, after);
341
470
  diff.forEach((part) => {
342
- const color = part.added
343
- ? chalk_1.default.green
344
- : part.removed
345
- ? chalk_1.default.red
346
- : chalk_1.default.gray;
471
+ const color = part.added ? chalk_1.default.green : part.removed ? chalk_1.default.red : chalk_1.default.gray;
347
472
  const prefix = part.added ? "+ " : part.removed ? "- " : " ";
348
473
  const lines = part.value.split("\n");
349
474
  lines.forEach((line) => {
@@ -364,7 +489,7 @@ const showTodoList = (todos) => {
364
489
  return;
365
490
  }
366
491
  console.log(chalk_1.default.bold("\nšŸ“‹ Todo List:"));
367
- todos.forEach((todo, index) => {
492
+ todos.forEach((todo, _index) => {
368
493
  const status = todo.status === "completed"
369
494
  ? chalk_1.default.green("āœ“")
370
495
  : todo.status === "in_progress"
@@ -386,11 +511,7 @@ exports.showTodoList = showTodoList;
386
511
  */
387
512
  const showToolProgress = (toolName, status) => {
388
513
  const icon = status === "started" ? "ā³" : status === "completed" ? "āœ“" : "āœ—";
389
- const color = status === "started"
390
- ? chalk_1.default.yellow
391
- : status === "completed"
392
- ? chalk_1.default.green
393
- : chalk_1.default.red;
514
+ const color = status === "started" ? chalk_1.default.yellow : status === "completed" ? chalk_1.default.green : chalk_1.default.red;
394
515
  console.log(color(`${icon} ${toolName} ${status}`));
395
516
  };
396
517
  exports.showToolProgress = showToolProgress;
@@ -402,19 +523,16 @@ const formatCodeBlock = (code, language) => {
402
523
  const maxLineLen = Math.max(...lines.map((l) => l.length), 40);
403
524
  const boxWidth = Math.min(maxLineLen + 4, 80);
404
525
  const langLabel = language ? chalk_1.default.cyan.bold(` ${language} `) : "";
405
- const topBorder = chalk_1.default.gray("ā•­") + langLabel + chalk_1.default.gray("─".repeat(Math.max(0, boxWidth - language.length - 3))) + chalk_1.default.gray("ā•®");
526
+ const topBorder = chalk_1.default.gray("ā•­") +
527
+ langLabel +
528
+ chalk_1.default.gray("─".repeat(Math.max(0, boxWidth - language.length - 3))) +
529
+ chalk_1.default.gray("ā•®");
406
530
  const bottomBorder = chalk_1.default.gray("ā•°" + "─".repeat(boxWidth - 1) + "╯");
407
531
  const formattedLines = lines.map((line) => {
408
532
  const highlighted = highlightCodeLine(line);
409
533
  return chalk_1.default.gray("│ ") + highlighted;
410
534
  });
411
- return [
412
- "",
413
- topBorder,
414
- ...formattedLines,
415
- bottomBorder,
416
- "",
417
- ].join("\n");
535
+ return ["", topBorder, ...formattedLines, bottomBorder, ""].join("\n");
418
536
  };
419
537
  /**
420
538
  * Format markdown text with colors for terminal display
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prab-cli",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "AI-powered coding assistant for your terminal. Built with Groq's lightning-fast LLMs, featuring autonomous tool execution, syntax-highlighted output, and git integration.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -15,8 +15,21 @@
15
15
  "log": "ts-node src/log-viewer.ts",
16
16
  "log:list": "ts-node src/log-viewer.ts list",
17
17
  "log:help": "ts-node src/log-viewer.ts help",
18
- "prepublishOnly": "npm run build",
19
- "test": "echo \"Error: no test specified\" && exit 1"
18
+ "lint": "eslint src/**/*.ts",
19
+ "lint:fix": "eslint src/**/*.ts --fix",
20
+ "format": "prettier --write src/**/*.ts",
21
+ "format:check": "prettier --check src/**/*.ts",
22
+ "typecheck": "tsc --noEmit",
23
+ "validate": "npm run lint && npm run typecheck && npm run build",
24
+ "prepublishOnly": "npm run validate",
25
+ "test": "npm run validate",
26
+ "prepare": "husky"
27
+ },
28
+ "lint-staged": {
29
+ "src/**/*.ts": [
30
+ "eslint --fix",
31
+ "prettier --write"
32
+ ]
20
33
  },
21
34
  "keywords": [
22
35
  "cli",
@@ -66,11 +79,20 @@
66
79
  "zod": "^4.3.5"
67
80
  },
68
81
  "devDependencies": {
82
+ "@eslint/js": "^9.39.2",
69
83
  "@types/diff": "^7.0.2",
70
84
  "@types/glob": "^8.1.0",
71
85
  "@types/inquirer": "^9.0.9",
72
86
  "@types/node": "^25.0.3",
87
+ "@typescript-eslint/eslint-plugin": "^8.53.1",
88
+ "@typescript-eslint/parser": "^8.53.1",
89
+ "eslint": "^9.39.2",
90
+ "eslint-config-prettier": "^10.1.8",
91
+ "eslint-plugin-prettier": "^5.5.5",
92
+ "husky": "^9.1.7",
93
+ "lint-staged": "^16.2.7",
73
94
  "nodemon": "^3.1.11",
95
+ "prettier": "^3.8.0",
74
96
  "ts-node": "^10.9.2",
75
97
  "typescript": "^5.9.3"
76
98
  }