codeant-cli 0.3.1 → 0.3.3

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 (69) hide show
  1. package/dist/178.index.js +1 -1
  2. package/dist/239.index.js +1 -1
  3. package/dist/701.index.js +1 -1
  4. package/dist/880.index.js +1 -1
  5. package/dist/935.index.js +1 -1
  6. package/dist/966.index.js +1 -1
  7. package/dist/index.js +5 -5
  8. package/dist/review/239.index.js +1 -0
  9. package/dist/review/index.js +8 -0
  10. package/dist/review/package.json +3 -0
  11. package/package.json +5 -6
  12. package/src/commands/getBaseUrl.js +0 -21
  13. package/src/commands/login.js +0 -125
  14. package/src/commands/logout.js +0 -30
  15. package/src/commands/review.js +0 -294
  16. package/src/commands/secrets.js +0 -148
  17. package/src/commands/setBaseUrl.js +0 -37
  18. package/src/components/ReviewUI.js +0 -410
  19. package/src/components/SecretsUI.js +0 -366
  20. package/src/components/Welcome.js +0 -144
  21. package/src/index.js +0 -349
  22. package/src/reviewHeadless.js +0 -198
  23. package/src/scm/azure/client.js +0 -21
  24. package/src/scm/azure/getCodeReview.js +0 -20
  25. package/src/scm/azure/getPullRequest.js +0 -30
  26. package/src/scm/azure/index.js +0 -6
  27. package/src/scm/azure/listCodeReviews.js +0 -17
  28. package/src/scm/azure/listPullRequestComments.js +0 -41
  29. package/src/scm/azure/listPullRequests.js +0 -26
  30. package/src/scm/azure/searchComments.js +0 -35
  31. package/src/scm/bitbucket/client.js +0 -18
  32. package/src/scm/bitbucket/getCodeReview.js +0 -3
  33. package/src/scm/bitbucket/getPullRequest.js +0 -26
  34. package/src/scm/bitbucket/index.js +0 -6
  35. package/src/scm/bitbucket/listCodeReviews.js +0 -17
  36. package/src/scm/bitbucket/listPullRequestComments.js +0 -33
  37. package/src/scm/bitbucket/listPullRequests.js +0 -33
  38. package/src/scm/bitbucket/searchComments.js +0 -29
  39. package/src/scm/github/client.js +0 -17
  40. package/src/scm/github/getCodeReview.js +0 -26
  41. package/src/scm/github/getPullRequest.js +0 -27
  42. package/src/scm/github/index.js +0 -6
  43. package/src/scm/github/listCodeReviews.js +0 -17
  44. package/src/scm/github/listPullRequestComments.js +0 -51
  45. package/src/scm/github/listPullRequests.js +0 -32
  46. package/src/scm/github/searchComments.js +0 -30
  47. package/src/scm/gitlab/client.js +0 -16
  48. package/src/scm/gitlab/getCodeReview.js +0 -18
  49. package/src/scm/gitlab/getPullRequest.js +0 -24
  50. package/src/scm/gitlab/index.js +0 -6
  51. package/src/scm/gitlab/listCodeReviews.js +0 -21
  52. package/src/scm/gitlab/listPullRequestComments.js +0 -44
  53. package/src/scm/gitlab/listPullRequests.js +0 -26
  54. package/src/scm/gitlab/searchComments.js +0 -28
  55. package/src/scm/index.js +0 -96
  56. package/src/tools/executeTool.js +0 -31
  57. package/src/tools/globTool.js +0 -9
  58. package/src/tools/grepTool.js +0 -18
  59. package/src/tools/lsTool.js +0 -7
  60. package/src/tools/readTool.js +0 -12
  61. package/src/utils/analytics.js +0 -45
  62. package/src/utils/baseUrl.js +0 -11
  63. package/src/utils/commonApiHelper.js +0 -143
  64. package/src/utils/config.js +0 -42
  65. package/src/utils/fetchApi.js +0 -39
  66. package/src/utils/gitDiffHelper.js +0 -1203
  67. package/src/utils/reviewApiHelper.js +0 -275
  68. package/src/utils/scmAuth.js +0 -136
  69. package/src/utils/secretsApiHelper.js +0 -242
@@ -1,366 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Box, Text } from 'ink';
3
-
4
- // ── Constants ────────────────────────────────────────────────────────────────
5
-
6
- const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
7
- const DIVIDER = '─'.repeat(55);
8
- const STEPS = ['Init', 'Fetch', 'Scan'];
9
-
10
- const CONFIDENCE_COLORS = {
11
- HIGH: 'red',
12
- MEDIUM: 'yellow',
13
- LOW: 'cyan',
14
- FALSE_POSITIVE: 'gray',
15
- };
16
-
17
- // ── Helpers ──────────────────────────────────────────────────────────────────
18
-
19
- function getConfidenceColor(score) {
20
- return CONFIDENCE_COLORS[score?.toUpperCase()] || 'gray';
21
- }
22
-
23
- function padEnd(str, len) {
24
- return str + ' '.repeat(Math.max(0, len - str.length));
25
- }
26
-
27
- function formatElapsed(startTime) {
28
- return ((Date.now() - startTime) / 1000).toFixed(1) + 's';
29
- }
30
-
31
- // ── Animated Components ──────────────────────────────────────────────────────
32
-
33
- function Spinner({ color = 'cyan' }) {
34
- const [frame, setFrame] = useState(0);
35
- useEffect(() => {
36
- const timer = setInterval(() => {
37
- setFrame(f => (f + 1) % SPINNER_FRAMES.length);
38
- }, 80);
39
- return () => clearInterval(timer);
40
- }, []);
41
- return React.createElement(Text, { color }, SPINNER_FRAMES[frame]);
42
- }
43
-
44
- function ElapsedTime({ startTime }) {
45
- const [, setTick] = useState(0);
46
- useEffect(() => {
47
- const timer = setInterval(() => setTick(t => t + 1), 100);
48
- return () => clearInterval(timer);
49
- }, []);
50
- return React.createElement(Text, { color: 'gray' }, formatElapsed(startTime));
51
- }
52
-
53
- function StepProgress({ currentStep }) {
54
- const items = STEPS.map((step, i) => {
55
- if (i < currentStep) {
56
- return React.createElement(Text, { key: step, color: 'green' }, `✓ ${step}`);
57
- } else if (i === currentStep) {
58
- return React.createElement(
59
- Box, { key: step },
60
- React.createElement(Spinner, { color: 'cyan' }),
61
- React.createElement(Text, { color: 'cyan' }, ` ${step}`),
62
- );
63
- } else {
64
- return React.createElement(Text, { key: step, color: 'gray', dimColor: true }, `· ${step}`);
65
- }
66
- });
67
- return React.createElement(Box, { gap: 2 }, ...items);
68
- }
69
-
70
- // ── Loading Screen ───────────────────────────────────────────────────────────
71
-
72
- function SecretsLoading({ message, step, startTime, fileCount, meta }) {
73
- const els = [
74
- React.createElement(
75
- Box, { key: 'status', gap: 1 },
76
- React.createElement(Spinner, {}),
77
- React.createElement(Text, { color: 'white', bold: true }, message),
78
- startTime != null && React.createElement(ElapsedTime, { startTime }),
79
- ),
80
- React.createElement(
81
- Box, { key: 'steps', marginTop: 1 },
82
- React.createElement(StepProgress, { currentStep: step }),
83
- ),
84
- ];
85
-
86
- // Show file list and coverage info when we have metadata (during scan step)
87
- if (meta && step >= 2) {
88
- const scanned = meta.scanned_files || [];
89
- const total = meta.total_changed || 0;
90
- const skipped = meta.skipped || [];
91
-
92
- els.push(React.createElement(Text, { key: 'meta-sp' }, ''));
93
- els.push(React.createElement(
94
- Text, { key: 'meta-hdr', color: 'gray', dimColor: true }, DIVIDER,
95
- ));
96
-
97
- // Coverage line
98
- if (total > 0) {
99
- const coverageColor = scanned.length === total ? 'green' : 'yellow';
100
- els.push(React.createElement(
101
- Text, { key: 'coverage', color: coverageColor },
102
- `Scanning ${scanned.length} of ${total} changed file${total !== 1 ? 's' : ''}`,
103
- ));
104
- }
105
-
106
- // File list
107
- if (scanned.length > 0) {
108
- els.push(React.createElement(Text, { key: 'files-sp' }, ''));
109
- scanned.forEach((file, i) => {
110
- const isLast = i === scanned.length - 1;
111
- const branch = isLast ? '└─' : '├─';
112
- els.push(React.createElement(
113
- Text, { key: `file-${i}`, color: 'white' },
114
- ` ${branch} ${file}`,
115
- ));
116
- });
117
- }
118
-
119
- // Skipped summary
120
- if (skipped.length > 0) {
121
- els.push(React.createElement(Text, { key: 'skip-sp' }, ''));
122
- const reasons = {};
123
- skipped.forEach(s => {
124
- const r = s.reason.split(' (')[0];
125
- reasons[r] = (reasons[r] || 0) + 1;
126
- });
127
- const parts = Object.entries(reasons).map(([r, n]) => `${n} ${r}`);
128
- els.push(React.createElement(
129
- Text, { key: 'skipped', color: 'gray', dimColor: true },
130
- `Skipped: ${parts.join(' · ')}`,
131
- ));
132
- }
133
- }
134
-
135
- return React.createElement(
136
- Box, { flexDirection: 'column', paddingX: 1, paddingY: 1 }, ...els,
137
- );
138
- }
139
-
140
- // ── Public Render Functions ──────────────────────────────────────────────────
141
-
142
- export function renderInitializing(startTime) {
143
- return React.createElement(SecretsLoading, {
144
- message: 'Initializing...',
145
- step: 0,
146
- startTime,
147
- });
148
- }
149
-
150
- export function renderFetchingDiff(startTime) {
151
- return React.createElement(SecretsLoading, {
152
- message: 'Fetching changes...',
153
- step: 1,
154
- startTime,
155
- });
156
- }
157
-
158
- export function renderScanning(startTime, fileCount, meta) {
159
- return React.createElement(SecretsLoading, {
160
- message: 'Scanning for secrets...',
161
- step: 2,
162
- startTime,
163
- fileCount,
164
- meta,
165
- });
166
- }
167
-
168
- export function renderNoFiles(scanType, lastNCommits, baseBranch, baseCommit) {
169
- const hints = {
170
- 'staged-only': 'Stage some changes first with "git add".',
171
- 'branch-diff': 'No changes found compared to the base branch.',
172
- 'uncommitted': 'No uncommitted changes found.',
173
- 'committed': 'No unpushed commits found.',
174
- 'all': 'No changes found between your branch and the base.',
175
- 'last-commit': 'No files found in the last commit.',
176
- 'last-n-commits': `No changes found in the last ${lastNCommits} commit(s).`,
177
- 'base-branch': `No changes found compared to ${baseBranch}.`,
178
- 'base-commit': `No changes found compared to commit ${baseCommit}.`,
179
- };
180
- return React.createElement(
181
- Box, { flexDirection: 'column', paddingX: 1, paddingY: 1 },
182
- React.createElement(Text, { color: 'cyan', bold: true }, 'CodeAnt Secrets'),
183
- React.createElement(Text, { color: 'gray', dimColor: true }, DIVIDER),
184
- React.createElement(Text, {}, ''),
185
- React.createElement(Text, { color: 'yellow' }, ' No changes to scan.'),
186
- React.createElement(Text, { color: 'gray' }, ` ${hints[scanType] || 'No files found.'}`),
187
- React.createElement(Text, {}, ''),
188
- );
189
- }
190
-
191
- export function renderError(error) {
192
- return React.createElement(
193
- Box, { flexDirection: 'column', paddingX: 1, paddingY: 1 },
194
- React.createElement(Text, { color: 'cyan', bold: true }, 'CodeAnt Secrets'),
195
- React.createElement(Text, { color: 'gray', dimColor: true }, DIVIDER),
196
- React.createElement(Text, {}, ''),
197
- React.createElement(Text, { color: 'red', bold: true }, ` ✗ ${error}`),
198
- React.createElement(Text, {}, ''),
199
- );
200
- }
201
-
202
- export function renderNotLoggedIn() {
203
- return React.createElement(
204
- Box, { flexDirection: 'column', paddingX: 1, paddingY: 1 },
205
- React.createElement(Text, { color: 'cyan', bold: true }, 'CodeAnt Secrets'),
206
- React.createElement(Text, { color: 'gray', dimColor: true }, DIVIDER),
207
- React.createElement(Text, {}, ''),
208
- React.createElement(Text, { color: 'red', bold: true }, ' ✗ Not logged in'),
209
- React.createElement(Text, { color: 'gray' }, ' Run "codeant login" to authenticate.'),
210
- React.createElement(Text, {}, ''),
211
- );
212
- }
213
-
214
- export function renderDone(secrets, failOn, shouldFailOn, startTime, fileCount, meta) {
215
- const allSecrets = secrets.flatMap(file =>
216
- file.secrets.map(s => ({ ...s, file_path: file.file_path }))
217
- );
218
-
219
- const blockingSecrets = allSecrets.filter(s => shouldFailOn(s.confidence_score));
220
- const hasBlocking = blockingSecrets.length > 0;
221
- const elapsed = startTime ? formatElapsed(startTime) : null;
222
-
223
- // Count by confidence
224
- const counts = {};
225
- allSecrets.forEach(s => {
226
- const conf = s.confidence_score?.toUpperCase() || 'UNKNOWN';
227
- counts[conf] = (counts[conf] || 0) + 1;
228
- });
229
-
230
- // Group by file
231
- const grouped = {};
232
- allSecrets.forEach(s => {
233
- const file = s.file_path || 'Unknown';
234
- (grouped[file] ??= []).push(s);
235
- });
236
- const sortedFiles = Object.keys(grouped).sort();
237
- sortedFiles.forEach(f =>
238
- grouped[f].sort((a, b) => (a.line_number || 0) - (b.line_number || 0))
239
- );
240
-
241
- const els = [];
242
-
243
- // ── Header ──
244
- els.push(React.createElement(
245
- Box, { key: 'hdr', gap: 1 },
246
- React.createElement(Text, { color: 'cyan', bold: true }, 'CodeAnt Secrets'),
247
- elapsed && React.createElement(Text, { color: 'gray' }, elapsed),
248
- ));
249
- els.push(React.createElement(
250
- Text, { key: 'div1', color: 'gray', dimColor: true }, DIVIDER,
251
- ));
252
-
253
- if (allSecrets.length === 0) {
254
- // ── Clean pass ──
255
- els.push(React.createElement(Text, { key: 'sp' }, ''));
256
- els.push(React.createElement(
257
- Text, { key: 'ok', color: 'green', bold: true }, ' ✓ No secrets found',
258
- ));
259
- els.push(React.createElement(Text, { key: 'sp2' }, ''));
260
- } else {
261
- // ── Secrets grouped by file ──
262
- els.push(React.createElement(Text, { key: 'sp1' }, ''));
263
-
264
- sortedFiles.forEach((file, fi) => {
265
- const fileSecrets = grouped[file];
266
-
267
- els.push(React.createElement(
268
- Text, { key: `f-${fi}`, color: 'white', bold: true }, ` ${file}`,
269
- ));
270
-
271
- fileSecrets.forEach((secret, si) => {
272
- const conf = secret.confidence_score?.toUpperCase() || 'UNKNOWN';
273
- const confColor = getConfidenceColor(conf);
274
- const isBlocking = shouldFailOn(secret.confidence_score);
275
- const isLast = si === fileSecrets.length - 1;
276
- const branch = isLast ? '└─' : '├─';
277
- const lineStr = secret.line_number ? `L${secret.line_number}` : '';
278
-
279
- els.push(React.createElement(
280
- Box, { key: `s-${fi}-${si}` },
281
- React.createElement(Text, { color: 'gray' }, ` ${branch} `),
282
- React.createElement(Text, {
283
- color: confColor,
284
- bold: isBlocking,
285
- }, padEnd(conf, 16)),
286
- React.createElement(Text, { color: 'gray' }, padEnd(lineStr, 6)),
287
- React.createElement(Text, { color: isBlocking ? 'white' : 'gray', dimColor: !isBlocking }, secret.type || ''),
288
- ));
289
- });
290
-
291
- els.push(React.createElement(Text, { key: `g-${fi}` }, ''));
292
- });
293
- }
294
-
295
- // ── Footer ──
296
- els.push(React.createElement(
297
- Text, { key: 'div2', color: 'gray', dimColor: true }, DIVIDER,
298
- ));
299
-
300
- // Coverage line
301
- const totalChanged = meta?.total_changed || 0;
302
- const scannedCount = meta?.scanned_files?.length || fileCount;
303
- if (totalChanged > 0) {
304
- const coverageColor = scannedCount === totalChanged ? 'green' : 'yellow';
305
- els.push(React.createElement(
306
- Text, { key: 'coverage', color: coverageColor },
307
- `Scanned ${scannedCount} of ${totalChanged} changed file${totalChanged !== 1 ? 's' : ''}`,
308
- ));
309
- } else if (fileCount) {
310
- els.push(React.createElement(
311
- Text, { key: 'coverage', color: 'gray' },
312
- `${fileCount} file${fileCount !== 1 ? 's' : ''} scanned`,
313
- ));
314
- }
315
-
316
- // Skipped files summary
317
- const skipped = meta?.skipped || [];
318
- if (skipped.length > 0) {
319
- const reasons = {};
320
- skipped.forEach(s => {
321
- const r = s.reason.split(' (')[0];
322
- reasons[r] = (reasons[r] || 0) + 1;
323
- });
324
- const parts = Object.entries(reasons).map(([r, n]) => `${n} ${r}`);
325
- els.push(React.createElement(
326
- Text, { key: 'skipped', color: 'gray', dimColor: true },
327
- `Skipped: ${parts.join(' · ')}`,
328
- ));
329
- }
330
-
331
- // Secret stats line
332
- if (allSecrets.length > 0) {
333
- const stats = [`${allSecrets.length} secret${allSecrets.length !== 1 ? 's' : ''} found`];
334
- for (const conf of ['HIGH', 'MEDIUM', 'LOW', 'FALSE_POSITIVE']) {
335
- if (counts[conf]) {
336
- const label = conf === 'FALSE_POSITIVE' ? 'false positive' : conf.toLowerCase();
337
- stats.push(`${counts[conf]} ${label}`);
338
- }
339
- }
340
- els.push(React.createElement(
341
- Text, { key: 'stats', color: 'gray' }, stats.join(' · '),
342
- ));
343
- }
344
-
345
- // Status line
346
- if (hasBlocking) {
347
- els.push(React.createElement(
348
- Text, { key: 'status', color: 'red', bold: true },
349
- `✗ ${blockingSecrets.length} blocking secret${blockingSecrets.length !== 1 ? 's' : ''} — remove before committing`,
350
- ));
351
- } else if (allSecrets.length > 0) {
352
- els.push(React.createElement(
353
- Text, { key: 'status', color: 'green', bold: true },
354
- '✓ No blocking secrets found',
355
- ));
356
- } else {
357
- els.push(React.createElement(
358
- Text, { key: 'status', color: 'green', bold: true },
359
- '✓ All clear',
360
- ));
361
- }
362
-
363
- return React.createElement(
364
- Box, { flexDirection: 'column', paddingX: 1, paddingY: 1 }, ...els,
365
- );
366
- }
@@ -1,144 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Text, Box, useApp } from 'ink';
3
-
4
- const logo = `
5
- ___ _ _ _
6
- / __\\___ __| | ___ / \\ _ __ | |_
7
- / / / _ \\ / _\` |/ _ \\/ /\\ | '_ \\| __|
8
- / /__| (_) | (_| | __/\\_/ \\ | | | | |_
9
- \\____/\\___/ \\__,_|\\___\\___/\\_/_| |_|\\__|
10
- `;
11
-
12
- const tagline = "Your AI-powered code review companion";
13
-
14
- const commands = [
15
- { cmd: 'codeant review', desc: 'Run AI-powered code review' },
16
- { cmd: 'codeant secrets', desc: 'Scan for secrets in your code' },
17
- { cmd: 'codeant login', desc: 'Login to CodeAnt' },
18
- { cmd: 'codeant --help', desc: 'Show all commands' },
19
- ];
20
-
21
- export default function Welcome({ version }) {
22
- const { exit } = useApp();
23
- const [displayedLogo, setDisplayedLogo] = useState('');
24
- const [displayedTagline, setDisplayedTagline] = useState('');
25
- const [showCursor, setShowCursor] = useState(true);
26
- const [phase, setPhase] = useState('logo');
27
- const [showCommands, setShowCommands] = useState(false);
28
-
29
- // Typing effect for logo (fast)
30
- useEffect(() => {
31
- if (phase !== 'logo') return;
32
-
33
- let i = 0;
34
- const chars = logo.split('');
35
- const interval = setInterval(() => {
36
- if (i < chars.length) {
37
- setDisplayedLogo(prev => prev + chars[i]);
38
- i++;
39
- } else {
40
- clearInterval(interval);
41
- setPhase('tagline');
42
- }
43
- }, 2);
44
-
45
- return () => clearInterval(interval);
46
- }, [phase]);
47
-
48
- // Typing effect for tagline (slower, more dramatic)
49
- useEffect(() => {
50
- if (phase !== 'tagline') return;
51
-
52
- let i = 0;
53
- const chars = tagline.split('');
54
- const interval = setInterval(() => {
55
- if (i < chars.length) {
56
- setDisplayedTagline(prev => prev + chars[i]);
57
- i++;
58
- } else {
59
- clearInterval(interval);
60
- setPhase('commands');
61
- setShowCommands(true);
62
- }
63
- }, 25);
64
-
65
- return () => clearInterval(interval);
66
- }, [phase]);
67
-
68
- // Blinking cursor
69
- useEffect(() => {
70
- if (phase === 'commands') {
71
- setShowCursor(false);
72
- return;
73
- }
74
- const interval = setInterval(() => {
75
- setShowCursor(prev => !prev);
76
- }, 400);
77
- return () => clearInterval(interval);
78
- }, [phase]);
79
-
80
- // Exit after animation
81
- useEffect(() => {
82
- if (showCommands) {
83
- const timeout = setTimeout(() => exit(), 100);
84
- return () => clearTimeout(timeout);
85
- }
86
- }, [showCommands, exit]);
87
-
88
- const cursor = showCursor ? '|' : ' ';
89
-
90
- const elements = [
91
- React.createElement(Text, { key: 'logo', color: 'cyan', bold: true }, displayedLogo)
92
- ];
93
-
94
- if (phase !== 'logo') {
95
- elements.push(
96
- React.createElement(
97
- Box,
98
- { key: 'tagline-box', marginTop: 0 },
99
- React.createElement(
100
- Text,
101
- { color: 'magenta', italic: true },
102
- displayedTagline,
103
- React.createElement(Text, { color: 'gray' }, cursor)
104
- )
105
- )
106
- );
107
- }
108
-
109
- if (showCommands) {
110
- elements.push(
111
- React.createElement(Text, { key: 'divider', color: 'gray' }, '─────────────────────────────────────────')
112
- );
113
-
114
- elements.push(
115
- React.createElement(
116
- Box,
117
- { key: 'commands-box', marginTop: 1, flexDirection: 'column' },
118
- React.createElement(Text, { color: 'yellow', bold: true }, 'Quick Start:'),
119
- ...commands.map((item, idx) =>
120
- React.createElement(
121
- Box,
122
- { key: `cmd-${idx}`, marginLeft: 2 },
123
- React.createElement(Text, { color: 'green' }, `$ ${item.cmd}`),
124
- React.createElement(Text, { color: 'gray' }, ` ${item.desc}`)
125
- )
126
- )
127
- )
128
- );
129
-
130
- elements.push(
131
- React.createElement(
132
- Box,
133
- { key: 'version-box', marginTop: 1 },
134
- React.createElement(Text, { color: 'gray' }, `v${version || '0.0.0'}`)
135
- )
136
- );
137
- }
138
-
139
- return React.createElement(
140
- Box,
141
- { flexDirection: 'column', padding: 1 },
142
- ...elements
143
- );
144
- }