korekt-cli 0.7.0 → 0.8.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "korekt-cli",
3
- "version": "0.7.0",
3
+ "version": "0.8.2",
4
4
  "description": "AI-powered code review CLI - Keep your kode korekt",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/git-logic.js CHANGED
@@ -238,10 +238,12 @@ export async function runUncommittedReview(mode = 'unstaged') {
238
238
  */
239
239
  export async function getContributors(diffRange, repoRootPath) {
240
240
  try {
241
- // Get all commit authors with email and name
242
- const { stdout: authorOutput } = await execa('git', ['log', '--format=%ae|%an', diffRange], {
243
- cwd: repoRootPath,
244
- });
241
+ // Get all commit authors with email and name (exclude merge commits)
242
+ const { stdout: authorOutput } = await execa(
243
+ 'git',
244
+ ['log', '--no-merges', '--format=%ae|%an', diffRange],
245
+ { cwd: repoRootPath }
246
+ );
245
247
 
246
248
  if (!authorOutput.trim()) {
247
249
  return { author_email: null, author_name: null, contributors: [] };
@@ -398,9 +400,11 @@ export async function runLocalReview(targetBranch = null, ignorePatterns = null)
398
400
  console.error(chalk.gray(`Analyzing commits from ${mergeBase.substring(0, 7)} to HEAD...`));
399
401
 
400
402
  // 3. Get Commit Messages with proper delimiter
401
- const { stdout: logOutput } = await execa('git', ['log', '--pretty=%B---EOC---', diffRange], {
402
- cwd: repoRootPath,
403
- });
403
+ const { stdout: logOutput } = await execa(
404
+ 'git',
405
+ ['log', '--no-merges', '--pretty=%B---EOC---', diffRange],
406
+ { cwd: repoRootPath }
407
+ );
404
408
  const commitMessages = logOutput
405
409
  .split('---EOC---')
406
410
  .map((msg) => msg.trim())
@@ -288,7 +288,10 @@ describe('runLocalReview - fork point detection', () => {
288
288
  'abc123def456 feature-branch@{0}: commit: latest\nfedcba654321 feature-branch@{1}: commit: middle\n510572bc5197788770004d0d0585822adab0128f feature-branch@{2}: branch: Created from master',
289
289
  };
290
290
  }
291
- if (command.includes('log --pretty=%B---EOC---') && command.includes('510572bc')) {
291
+ if (
292
+ command.includes('log --no-merges --pretty=%B---EOC---') &&
293
+ command.includes('510572bc')
294
+ ) {
292
295
  return { stdout: 'feat: add feature---EOC---' };
293
296
  }
294
297
  if (command.includes('diff --name-status') && command.includes('510572bc')) {
@@ -338,7 +341,7 @@ describe('runLocalReview - fork point detection', () => {
338
341
  if (command.includes('merge-base origin/main HEAD')) {
339
342
  return { stdout: 'abc123' };
340
343
  }
341
- if (command.includes('log --pretty=%B---EOC---')) {
344
+ if (command.includes('log --no-merges --pretty=%B---EOC---')) {
342
345
  return { stdout: 'feat: add feature---EOC---' };
343
346
  }
344
347
  if (command.includes('diff --name-status')) {
@@ -542,7 +545,7 @@ describe('getContributors', () => {
542
545
  it('should extract single contributor with commit count', async () => {
543
546
  vi.mocked(execa).mockImplementation(async (cmd, args) => {
544
547
  const command = [cmd, ...args].join(' ');
545
- if (command.includes('log --format=%ae|%an')) {
548
+ if (command.includes('log --no-merges --format=%ae|%an')) {
546
549
  return {
547
550
  stdout: 'john@example.com|John Doe\njohn@example.com|John Doe\njohn@example.com|John Doe',
548
551
  };
@@ -565,7 +568,7 @@ describe('getContributors', () => {
565
568
  it('should identify author as contributor with most commits', async () => {
566
569
  vi.mocked(execa).mockImplementation(async (cmd, args) => {
567
570
  const command = [cmd, ...args].join(' ');
568
- if (command.includes('log --format=%ae|%an')) {
571
+ if (command.includes('log --no-merges --format=%ae|%an')) {
569
572
  return {
570
573
  stdout: [
571
574
  'alice@example.com|Alice Smith',
@@ -595,7 +598,7 @@ describe('getContributors', () => {
595
598
  it('should handle empty commit range', async () => {
596
599
  vi.mocked(execa).mockImplementation(async (cmd, args) => {
597
600
  const command = [cmd, ...args].join(' ');
598
- if (command.includes('log --format=%ae|%an')) {
601
+ if (command.includes('log --no-merges --format=%ae|%an')) {
599
602
  return { stdout: '' };
600
603
  }
601
604
  throw new Error(`Unmocked command: ${command}`);
@@ -623,7 +626,7 @@ describe('getContributors', () => {
623
626
  it('should handle missing name in git log', async () => {
624
627
  vi.mocked(execa).mockImplementation(async (cmd, args) => {
625
628
  const command = [cmd, ...args].join(' ');
626
- if (command.includes('log --format=%ae|%an')) {
629
+ if (command.includes('log --no-merges --format=%ae|%an')) {
627
630
  return { stdout: 'user@example.com|' };
628
631
  }
629
632
  throw new Error(`Unmocked command: ${command}`);
@@ -639,7 +642,7 @@ describe('getContributors', () => {
639
642
  it('should sort contributors by commit count descending', async () => {
640
643
  vi.mocked(execa).mockImplementation(async (cmd, args) => {
641
644
  const command = [cmd, ...args].join(' ');
642
- if (command.includes('log --format=%ae|%an')) {
645
+ if (command.includes('log --no-merges --format=%ae|%an')) {
643
646
  return {
644
647
  stdout: [
645
648
  'c@example.com|C User',
package/src/index.js CHANGED
@@ -6,9 +6,11 @@ import chalk from 'chalk';
6
6
  import readline from 'readline';
7
7
  import ora from 'ora';
8
8
  import { createRequire } from 'module';
9
- import { readFileSync } from 'fs';
10
- import { join, dirname } from 'path';
9
+ import { writeFileSync, mkdtempSync, rmSync } from 'fs';
10
+ import { join, dirname, resolve } from 'path';
11
11
  import { fileURLToPath } from 'url';
12
+ import { spawn } from 'child_process';
13
+ import { tmpdir } from 'os';
12
14
  import { runLocalReview } from './git-logic.js';
13
15
  import { getApiKey, setApiKey, getApiEndpoint, setApiEndpoint } from './config.js';
14
16
  import { formatReviewOutput } from './formatter.js';
@@ -71,11 +73,76 @@ async function confirmAction(message) {
71
73
  output: process.stdout,
72
74
  });
73
75
 
74
- return new Promise((resolve) => {
76
+ return new Promise((resolvePromise) => {
75
77
  rl.question(message, (answer) => {
76
78
  rl.close();
77
79
  // Default to 'yes' if the user just presses Enter
78
- resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes' || answer === '');
80
+ resolvePromise(
81
+ answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes' || answer === ''
82
+ );
83
+ });
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Detect CI provider from environment variables
89
+ * @returns {string|null} Provider name or null if not detected
90
+ */
91
+ export function detectCIProvider() {
92
+ if (process.env.GITHUB_TOKEN && process.env.GITHUB_REPOSITORY) {
93
+ return 'github';
94
+ }
95
+ if (process.env.SYSTEM_ACCESSTOKEN && process.env.SYSTEM_PULLREQUEST_PULLREQUESTID) {
96
+ return 'azure';
97
+ }
98
+ if (process.env.BITBUCKET_REPO_SLUG && process.env.BITBUCKET_PR_ID) {
99
+ return 'bitbucket';
100
+ }
101
+ return null;
102
+ }
103
+
104
+ /**
105
+ * Run the CI integration script to post comments
106
+ * @param {string} provider - CI provider (github, azure, bitbucket)
107
+ * @param {Object} results - Review results from API
108
+ * @returns {Promise<void>}
109
+ */
110
+ async function runCIScript(provider, results) {
111
+ const __filename = fileURLToPath(import.meta.url);
112
+ const __dirname = dirname(__filename);
113
+ const scriptPath = resolve(__dirname, '..', 'scripts', `${provider}.sh`);
114
+
115
+ // Create secure temp directory and write results
116
+ const tempDir = mkdtempSync(join(tmpdir(), 'korekt-'));
117
+ const tempFile = join(tempDir, 'results.json');
118
+ writeFileSync(tempFile, JSON.stringify(results, null, 2));
119
+
120
+ return new Promise((resolvePromise, reject) => {
121
+ const cleanup = () => {
122
+ try {
123
+ rmSync(tempDir, { recursive: true, force: true });
124
+ } catch (err) {
125
+ log(chalk.yellow(`Warning: Failed to clean up temp directory: ${err.message}`));
126
+ }
127
+ };
128
+
129
+ const child = spawn('bash', [scriptPath, tempFile], {
130
+ stdio: 'inherit',
131
+ env: process.env,
132
+ });
133
+
134
+ child.on('close', (code) => {
135
+ cleanup();
136
+ if (code === 0) {
137
+ resolvePromise();
138
+ } else {
139
+ reject(new Error(`CI script exited with code ${code}`));
140
+ }
141
+ });
142
+
143
+ child.on('error', (err) => {
144
+ cleanup();
145
+ reject(err);
79
146
  });
80
147
  });
81
148
  }
@@ -93,19 +160,16 @@ Examples:
93
160
  $ kk stg --dry-run Preview staged changes review
94
161
  $ kk diff Review unstaged changes
95
162
  $ kk review main --json Output raw JSON (for CI/CD integration)
163
+ $ kk review main --comment Review and post comments to PR (CI/CD)
96
164
 
97
165
  Common Options:
98
166
  --dry-run Show payload without sending to API
99
167
  --json Output raw API response as JSON
168
+ --comment Post review results as PR comments
100
169
 
101
170
  Configuration:
102
171
  $ kk config --key YOUR_KEY
103
172
  $ kk config --endpoint https://api.korekt.ai/api/review
104
-
105
- CI/CD Integration:
106
- $ kk get-script github Output GitHub Actions integration script
107
- $ kk get-script bitbucket Output Bitbucket Pipelines integration script
108
- $ kk get-script azure Output Azure DevOps integration script
109
173
  `
110
174
  );
111
175
 
@@ -122,6 +186,7 @@ program
122
186
  'Ignore files matching these patterns (e.g., "*.lock" "dist/*")'
123
187
  )
124
188
  .option('--json', 'Output raw API response as JSON')
189
+ .option('--comment', 'Post review results as PR comments (auto-detects CI provider)')
125
190
  .action(async (targetBranch, options) => {
126
191
  const reviewTarget = targetBranch ? `against '${targetBranch}'` : '(auto-detecting fork point)';
127
192
 
@@ -166,8 +231,8 @@ program
166
231
  return;
167
232
  }
168
233
 
169
- // Show summary and ask for confirmation (auto-confirm in JSON mode)
170
- if (!options.json) {
234
+ // Show summary and ask for confirmation (auto-confirm in JSON/comment mode)
235
+ if (!options.json && !options.comment) {
171
236
  log(chalk.yellow('\n📋 Ready to submit for review:\n'));
172
237
  log(` Branch: ${chalk.cyan(payload.source_branch)}`);
173
238
  log(` Commits: ${chalk.cyan(payload.commit_messages.length)}`);
@@ -216,6 +281,32 @@ program
216
281
  const elapsed = Math.floor((Date.now() - startTime) / 1000);
217
282
  spinner.succeed(`Review completed in ${elapsed}s!`);
218
283
 
284
+ // Handle --comment flag: post results to PR
285
+ if (options.comment) {
286
+ const provider = detectCIProvider();
287
+ if (!provider) {
288
+ log(
289
+ chalk.red(
290
+ 'Could not detect CI provider. Make sure required environment variables are set:'
291
+ )
292
+ );
293
+ log(chalk.gray(' GitHub: GITHUB_TOKEN, GITHUB_REPOSITORY, PR_NUMBER, COMMIT_HASH'));
294
+ log(chalk.gray(' Azure: SYSTEM_ACCESSTOKEN, SYSTEM_PULLREQUEST_PULLREQUESTID'));
295
+ log(chalk.gray(' Bitbucket: BITBUCKET_REPO_SLUG, BITBUCKET_PR_ID'));
296
+ process.exit(1);
297
+ }
298
+
299
+ log(chalk.blue(`Posting review comments to ${provider}...`));
300
+ try {
301
+ await runCIScript(provider, response.data);
302
+ log(chalk.green('Successfully posted review comments!'));
303
+ } catch (err) {
304
+ log(chalk.red(`Failed to post comments: ${err.message}`));
305
+ process.exit(1);
306
+ }
307
+ return;
308
+ }
309
+
219
310
  // Output results to stdout
220
311
  if (options.json) {
221
312
  output(JSON.stringify(response.data, null, 2));
@@ -424,49 +515,6 @@ program
424
515
  }
425
516
  });
426
517
 
427
- program
428
- .command('get-script <provider>')
429
- .description('Output a CI/CD integration script for a specific provider')
430
- .addHelpText(
431
- 'after',
432
- `
433
- Providers:
434
- github GitHub Actions integration script
435
- bitbucket Bitbucket Pipelines integration script
436
- azure Azure DevOps integration script
437
-
438
- Usage:
439
- kk get-script github | bash -s results.json
440
- kk get-script bitbucket > bitbucket.sh && chmod +x bitbucket.sh
441
- kk get-script azure > azure.sh
442
- `
443
- )
444
- .action((provider) => {
445
- const validProviders = ['github', 'bitbucket', 'azure'];
446
-
447
- if (!validProviders.includes(provider.toLowerCase())) {
448
- console.error(chalk.red(`Invalid provider: ${provider}`));
449
- console.error(chalk.gray(`Valid providers: ${validProviders.join(', ')}`));
450
- process.exit(1);
451
- }
452
-
453
- try {
454
- // Get the directory where this script is located
455
- const __filename = fileURLToPath(import.meta.url);
456
- const __dirname = dirname(__filename);
457
-
458
- // Build path to the script file
459
- const scriptPath = join(__dirname, '..', 'scripts', `${provider.toLowerCase()}.sh`);
460
-
461
- // Read and output the script
462
- const scriptContent = readFileSync(scriptPath, 'utf8');
463
- output(scriptContent);
464
- } catch (error) {
465
- console.error(chalk.red(`Failed to read script: ${error.message}`));
466
- process.exit(1);
467
- }
468
- });
469
-
470
518
  // Only parse arguments if this file is being run directly (not imported)
471
519
  // In tests, we set NODE_ENV to 'test' via vitest
472
520
  if (process.env.NODE_ENV !== 'test') {
package/src/index.test.js CHANGED
@@ -1,8 +1,5 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { truncateFileData, formatErrorOutput } from './index.js';
3
- import { readFileSync } from 'fs';
4
- import { join, dirname } from 'path';
5
- import { fileURLToPath } from 'url';
2
+ import { truncateFileData, formatErrorOutput, detectCIProvider } from './index.js';
6
3
 
7
4
  describe('CLI JSON output mode', () => {
8
5
  let stdoutSpy;
@@ -336,137 +333,108 @@ describe('CLI JSON output mode', () => {
336
333
  });
337
334
  });
338
335
 
339
- describe('get-script command', () => {
340
- let stdoutSpy;
336
+ describe('detectCIProvider', () => {
337
+ const originalEnv = process.env;
341
338
 
342
339
  beforeEach(() => {
343
- stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
340
+ // Reset environment before each test
341
+ process.env = { ...originalEnv };
342
+ // Clear all CI-related env vars
343
+ delete process.env.GITHUB_TOKEN;
344
+ delete process.env.GITHUB_REPOSITORY;
345
+ delete process.env.SYSTEM_ACCESSTOKEN;
346
+ delete process.env.SYSTEM_PULLREQUEST_PULLREQUESTID;
347
+ delete process.env.BITBUCKET_REPO_SLUG;
348
+ delete process.env.BITBUCKET_PR_ID;
344
349
  });
345
350
 
346
351
  afterEach(() => {
347
- vi.restoreAllMocks();
352
+ process.env = originalEnv;
348
353
  });
349
354
 
350
- describe('valid providers', () => {
351
- it('should output github script to stdout', () => {
352
- const output = (msg) => process.stdout.write(msg + '\n');
353
-
354
- // Read actual github.sh script
355
- const __filename = fileURLToPath(import.meta.url);
356
- const __dirname = dirname(__filename);
357
- const scriptPath = join(__dirname, '..', 'scripts', 'github.sh');
358
- const scriptContent = readFileSync(scriptPath, 'utf8');
359
-
360
- // Simulate get-script command output
361
- output(scriptContent);
362
-
363
- // Verify script was output to stdout
364
- expect(stdoutSpy).toHaveBeenCalledWith(scriptContent + '\n');
365
- expect(stdoutSpy).toHaveBeenCalledTimes(1);
366
- });
355
+ it('should detect GitHub when GITHUB_TOKEN and GITHUB_REPOSITORY are set', () => {
356
+ process.env.GITHUB_TOKEN = 'ghp_test123';
357
+ process.env.GITHUB_REPOSITORY = 'owner/repo';
367
358
 
368
- it('should output bitbucket script to stdout', () => {
369
- const output = (msg) => process.stdout.write(msg + '\n');
359
+ expect(detectCIProvider()).toBe('github');
360
+ });
370
361
 
371
- // Read actual bitbucket.sh script
372
- const __filename = fileURLToPath(import.meta.url);
373
- const __dirname = dirname(__filename);
374
- const scriptPath = join(__dirname, '..', 'scripts', 'bitbucket.sh');
375
- const scriptContent = readFileSync(scriptPath, 'utf8');
362
+ it('should detect Azure when SYSTEM_ACCESSTOKEN and SYSTEM_PULLREQUEST_PULLREQUESTID are set', () => {
363
+ process.env.SYSTEM_ACCESSTOKEN = 'azure_token';
364
+ process.env.SYSTEM_PULLREQUEST_PULLREQUESTID = '123';
376
365
 
377
- // Simulate get-script command output
378
- output(scriptContent);
366
+ expect(detectCIProvider()).toBe('azure');
367
+ });
379
368
 
380
- // Verify script was output to stdout
381
- expect(stdoutSpy).toHaveBeenCalledWith(scriptContent + '\n');
382
- expect(stdoutSpy).toHaveBeenCalledTimes(1);
383
- });
369
+ it('should detect Bitbucket when BITBUCKET_REPO_SLUG and BITBUCKET_PR_ID are set', () => {
370
+ process.env.BITBUCKET_REPO_SLUG = 'my-repo';
371
+ process.env.BITBUCKET_PR_ID = '456';
384
372
 
385
- it('should output azure script to stdout', () => {
386
- const output = (msg) => process.stdout.write(msg + '\n');
373
+ expect(detectCIProvider()).toBe('bitbucket');
374
+ });
387
375
 
388
- // Read actual azure.sh script
389
- const __filename = fileURLToPath(import.meta.url);
390
- const __dirname = dirname(__filename);
391
- const scriptPath = join(__dirname, '..', 'scripts', 'azure.sh');
392
- const scriptContent = readFileSync(scriptPath, 'utf8');
376
+ it('should return null when no CI provider env vars are set', () => {
377
+ expect(detectCIProvider()).toBe(null);
378
+ });
393
379
 
394
- // Simulate get-script command output
395
- output(scriptContent);
380
+ it('should return null when only partial GitHub env vars are set', () => {
381
+ process.env.GITHUB_TOKEN = 'ghp_test123';
382
+ // GITHUB_REPOSITORY not set
396
383
 
397
- // Verify script was output to stdout
398
- expect(stdoutSpy).toHaveBeenCalledWith(scriptContent + '\n');
399
- expect(stdoutSpy).toHaveBeenCalledTimes(1);
400
- });
384
+ expect(detectCIProvider()).toBe(null);
401
385
  });
402
386
 
403
- describe('script completeness', () => {
404
- it('should output complete bash scripts with proper structure', () => {
405
- const __filename = fileURLToPath(import.meta.url);
406
- const __dirname = dirname(__filename);
387
+ it('should return null when only partial Azure env vars are set', () => {
388
+ process.env.SYSTEM_ACCESSTOKEN = 'azure_token';
389
+ // SYSTEM_PULLREQUEST_PULLREQUESTID not set
407
390
 
408
- // Test all three scripts
409
- const providers = ['github', 'bitbucket', 'azure'];
410
-
411
- providers.forEach((provider) => {
412
- const scriptPath = join(__dirname, '..', 'scripts', `${provider}.sh`);
413
- const scriptContent = readFileSync(scriptPath, 'utf8');
391
+ expect(detectCIProvider()).toBe(null);
392
+ });
414
393
 
415
- // Verify script starts with shebang
416
- expect(scriptContent).toMatch(/^#!/);
417
- expect(scriptContent).toContain('#!/usr/bin/env bash');
394
+ it('should return null when only partial Bitbucket env vars are set', () => {
395
+ process.env.BITBUCKET_REPO_SLUG = 'my-repo';
396
+ // BITBUCKET_PR_ID not set
418
397
 
419
- // Verify script has substantial content (not truncated)
420
- const lineCount = scriptContent.split('\n').length;
421
- expect(lineCount).toBeGreaterThan(400);
398
+ expect(detectCIProvider()).toBe(null);
399
+ });
422
400
 
423
- // Verify script ends properly (has exit statement)
424
- expect(scriptContent).toContain('exit');
425
- });
426
- });
401
+ it('should prioritize GitHub over other providers when multiple are set', () => {
402
+ // Set all providers
403
+ process.env.GITHUB_TOKEN = 'ghp_test123';
404
+ process.env.GITHUB_REPOSITORY = 'owner/repo';
405
+ process.env.SYSTEM_ACCESSTOKEN = 'azure_token';
406
+ process.env.SYSTEM_PULLREQUEST_PULLREQUESTID = '123';
407
+ process.env.BITBUCKET_REPO_SLUG = 'my-repo';
408
+ process.env.BITBUCKET_PR_ID = '456';
409
+
410
+ // GitHub should be detected first due to check order
411
+ expect(detectCIProvider()).toBe('github');
427
412
  });
413
+ });
428
414
 
429
- describe('error handling', () => {
430
- it('should reject invalid provider', () => {
431
- const invalidProvider = 'gitlab';
432
- const validProviders = ['github', 'bitbucket', 'azure'];
415
+ describe('--comment flag behavior', () => {
416
+ it('should skip confirmation when --comment is set', () => {
417
+ const options = { comment: true };
433
418
 
434
- // Test validation logic from index.js line 513
435
- const isValid = validProviders.includes(invalidProvider.toLowerCase());
419
+ // Logic from index.js: if (!options.json && !options.comment) { confirmAction... }
420
+ const shouldShowConfirmation = !options.json && !options.comment;
436
421
 
437
- expect(isValid).toBe(false);
422
+ expect(shouldShowConfirmation).toBe(false);
423
+ });
438
424
 
439
- // In the actual command, this would trigger process.exit(1)
440
- // We verify the validation logic correctly identifies invalid providers
441
- expect(['github', 'bitbucket', 'azure']).not.toContain(invalidProvider);
442
- });
425
+ it('should skip confirmation when both --json and --comment are set', () => {
426
+ const options = { json: true, comment: true };
443
427
 
444
- it('should accept valid providers case-insensitively', () => {
445
- const validProviders = ['github', 'bitbucket', 'azure'];
428
+ const shouldShowConfirmation = !options.json && !options.comment;
446
429
 
447
- // Test that providers work case-insensitively
448
- expect(validProviders.includes('GitHub'.toLowerCase())).toBe(true);
449
- expect(validProviders.includes('BITBUCKET'.toLowerCase())).toBe(true);
450
- expect(validProviders.includes('Azure'.toLowerCase())).toBe(true);
451
- });
430
+ expect(shouldShowConfirmation).toBe(false);
452
431
  });
453
432
 
454
- describe('bundled script files', () => {
455
- it('should have all provider scripts bundled and readable', () => {
456
- const __filename = fileURLToPath(import.meta.url);
457
- const __dirname = dirname(__filename);
433
+ it('should show confirmation when neither --json nor --comment is set', () => {
434
+ const options = {};
458
435
 
459
- const providers = ['github', 'bitbucket', 'azure'];
436
+ const shouldShowConfirmation = !options.json && !options.comment;
460
437
 
461
- providers.forEach((provider) => {
462
- const scriptPath = join(__dirname, '..', 'scripts', `${provider}.sh`);
463
-
464
- // Should not throw - file exists and is readable
465
- expect(() => {
466
- const content = readFileSync(scriptPath, 'utf8');
467
- expect(content.length).toBeGreaterThan(0);
468
- }).not.toThrow();
469
- });
470
- });
438
+ expect(shouldShowConfirmation).toBe(true);
471
439
  });
472
440
  });