korekt-cli 0.13.4 → 0.13.6
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/README.md +1 -1
- package/package.json +1 -1
- package/src/git-logic.js +25 -4
- package/src/git-logic.test.js +68 -0
- package/src/index.js +4 -1
- package/src/index.test.js +4 -4
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ kk review -m gemini-3-flash-preview # Direct selection
|
|
|
38
38
|
Available models (ranked by recommendation):
|
|
39
39
|
|
|
40
40
|
1. **gemini-3-flash-preview** - Most efficient, recommended for daily use
|
|
41
|
-
2. **gemini-3-pro-preview** - Best quality for complex reviews
|
|
41
|
+
2. **gemini-3.1-pro-preview** - Best quality for complex reviews
|
|
42
42
|
3. **gemini-2.5-pro** - High quality alternative
|
|
43
43
|
4. **gemini-2.5-flash** - Legacy, avoid
|
|
44
44
|
|
package/package.json
CHANGED
package/src/git-logic.js
CHANGED
|
@@ -31,33 +31,54 @@ export function truncateContent(content, maxLines = 2000) {
|
|
|
31
31
|
*/
|
|
32
32
|
export function normalizeRepoUrl(url) {
|
|
33
33
|
// Handle Azure DevOps SSH format: git@ssh.dev.azure.com:v3/org/project/repo
|
|
34
|
-
|
|
34
|
+
// [^:]* allows SSH config aliases like git@ssh.dev.azure.com-work:v3/...
|
|
35
|
+
const azureDevOpsSshMatch = url.match(
|
|
36
|
+
/git@ssh\.dev\.azure\.com[^:]*:v3\/([^/]+)\/([^/]+)\/(.+?)(?:\.git)?$/
|
|
37
|
+
);
|
|
35
38
|
if (azureDevOpsSshMatch) {
|
|
36
39
|
const [, org, project, repo] = azureDevOpsSshMatch;
|
|
37
40
|
return `https://dev.azure.com/${org}/${project}/_git/${repo}`;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
// Handle GitHub SSH format: git@github.com:user/repo.git
|
|
41
|
-
|
|
44
|
+
// [^:]* allows SSH config aliases like git@github.com-personal:user/repo.git
|
|
45
|
+
const githubSshMatch = url.match(/git@github\.com[^:]*:([^/]+)\/(.+?)(?:\.git)?$/);
|
|
42
46
|
if (githubSshMatch) {
|
|
43
47
|
const [, user, repo] = githubSshMatch;
|
|
44
48
|
return `https://github.com/${user}/${repo}`;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
// Handle GitLab SSH format: git@gitlab.com:user/repo.git
|
|
48
|
-
|
|
52
|
+
// [^:]* allows SSH config aliases like git@gitlab.com-work:user/repo.git
|
|
53
|
+
const gitlabSshMatch = url.match(/git@gitlab\.com[^:]*:([^/]+)\/(.+?)(?:\.git)?$/);
|
|
49
54
|
if (gitlabSshMatch) {
|
|
50
55
|
const [, user, repo] = gitlabSshMatch;
|
|
51
56
|
return `https://gitlab.com/${user}/${repo}`;
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
// Handle Bitbucket SSH format: git@bitbucket.org:user/repo.git
|
|
55
|
-
|
|
60
|
+
// [^:]* allows SSH config aliases like git@bitbucket.org-astoisavljevic:user/repo.git
|
|
61
|
+
const bitbucketSshMatch = url.match(/git@bitbucket\.org[^:]*:([^/]+)\/(.+?)(?:\.git)?$/);
|
|
56
62
|
if (bitbucketSshMatch) {
|
|
57
63
|
const [, user, repo] = bitbucketSshMatch;
|
|
58
64
|
return `https://bitbucket.org/${user}/${repo}`;
|
|
59
65
|
}
|
|
60
66
|
|
|
67
|
+
// Generic SSH fallback for self-hosted/unknown providers
|
|
68
|
+
// SCP-style: git@host:path or user@host:path (negative lookahead excludes URLs with ://)
|
|
69
|
+
const scpMatch = url.match(/^(?!.*:\/\/)([^@]+)@([^:]+):(.+?)(?:\.git)?$/);
|
|
70
|
+
if (scpMatch) {
|
|
71
|
+
const [, , host, path] = scpMatch;
|
|
72
|
+
return `https://${host}/${path}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// SSH protocol: ssh://user@host/path or ssh://user@host:port/path
|
|
76
|
+
const sshProtoMatch = url.match(/^ssh:\/\/(?:[^@]+@)?([^/:]+)(?::\d+)?\/(.+?)(?:\.git)?$/);
|
|
77
|
+
if (sshProtoMatch) {
|
|
78
|
+
const [, host, path] = sshProtoMatch;
|
|
79
|
+
return `https://${host}/${path}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
61
82
|
// If already HTTPS or other format, return as-is (possibly removing .git suffix)
|
|
62
83
|
return url.replace(/\.git$/, '');
|
|
63
84
|
}
|
package/src/git-logic.test.js
CHANGED
|
@@ -477,6 +477,12 @@ describe('normalizeRepoUrl', () => {
|
|
|
477
477
|
expect(normalizeRepoUrl(sshUrl)).toBe(expected);
|
|
478
478
|
});
|
|
479
479
|
|
|
480
|
+
it('should strip .git suffix from Azure DevOps SSH URL', () => {
|
|
481
|
+
const sshUrl = 'git@ssh.dev.azure.com:v3/MyOrg/MyProject/MyRepo.git';
|
|
482
|
+
const expected = 'https://dev.azure.com/MyOrg/MyProject/_git/MyRepo';
|
|
483
|
+
expect(normalizeRepoUrl(sshUrl)).toBe(expected);
|
|
484
|
+
});
|
|
485
|
+
|
|
480
486
|
it('should normalize GitHub SSH URL to HTTPS', () => {
|
|
481
487
|
const sshUrl = 'git@github.com:user/repo.git';
|
|
482
488
|
const expected = 'https://github.com/user/repo';
|
|
@@ -522,6 +528,68 @@ describe('normalizeRepoUrl', () => {
|
|
|
522
528
|
const adoUrl = 'https://dev.azure.com/VanillaSoftCollection/VanillaLand/_git/VanillaLand';
|
|
523
529
|
expect(normalizeRepoUrl(adoUrl)).toBe(adoUrl);
|
|
524
530
|
});
|
|
531
|
+
|
|
532
|
+
// SSH config alias tests (e.g., multiple SSH keys per provider)
|
|
533
|
+
it('should normalize Bitbucket SSH URL with config alias', () => {
|
|
534
|
+
const sshUrl = 'git@bitbucket.org-astoisavljevic:jatheon/audit.git';
|
|
535
|
+
const expected = 'https://bitbucket.org/jatheon/audit';
|
|
536
|
+
expect(normalizeRepoUrl(sshUrl)).toBe(expected);
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it('should normalize GitHub SSH URL with config alias', () => {
|
|
540
|
+
const sshUrl = 'git@github.com-personal:user/repo.git';
|
|
541
|
+
const expected = 'https://github.com/user/repo';
|
|
542
|
+
expect(normalizeRepoUrl(sshUrl)).toBe(expected);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it('should normalize GitLab SSH URL with config alias', () => {
|
|
546
|
+
const sshUrl = 'git@gitlab.com-work:team/project.git';
|
|
547
|
+
const expected = 'https://gitlab.com/team/project';
|
|
548
|
+
expect(normalizeRepoUrl(sshUrl)).toBe(expected);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it('should normalize Azure DevOps SSH URL with config alias', () => {
|
|
552
|
+
const sshUrl = 'git@ssh.dev.azure.com-work:v3/MyOrg/MyProject/MyRepo';
|
|
553
|
+
const expected = 'https://dev.azure.com/MyOrg/MyProject/_git/MyRepo';
|
|
554
|
+
expect(normalizeRepoUrl(sshUrl)).toBe(expected);
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// Generic SSH fallback for self-hosted/unknown providers
|
|
558
|
+
it('should normalize generic SCP-style SSH URL with IP address', () => {
|
|
559
|
+
const sshUrl = 'git@192.168.1.1:org/repo.git';
|
|
560
|
+
const expected = 'https://192.168.1.1/org/repo';
|
|
561
|
+
expect(normalizeRepoUrl(sshUrl)).toBe(expected);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it('should normalize generic SCP-style SSH URL with hostname', () => {
|
|
565
|
+
const sshUrl = 'git@selfhosted.example.com:team/repo.git';
|
|
566
|
+
const expected = 'https://selfhosted.example.com/team/repo';
|
|
567
|
+
expect(normalizeRepoUrl(sshUrl)).toBe(expected);
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('should normalize generic SCP-style SSH URL without .git suffix', () => {
|
|
571
|
+
const sshUrl = 'git@selfhosted.example.com:team/repo';
|
|
572
|
+
const expected = 'https://selfhosted.example.com/team/repo';
|
|
573
|
+
expect(normalizeRepoUrl(sshUrl)).toBe(expected);
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
it('should normalize ssh:// protocol URL', () => {
|
|
577
|
+
const sshUrl = 'ssh://git@selfhosted.example.com/team/repo.git';
|
|
578
|
+
const expected = 'https://selfhosted.example.com/team/repo';
|
|
579
|
+
expect(normalizeRepoUrl(sshUrl)).toBe(expected);
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it('should normalize ssh:// protocol URL with port (port dropped)', () => {
|
|
583
|
+
const sshUrl = 'ssh://git@myhost:2222/org/repo.git';
|
|
584
|
+
const expected = 'https://myhost/org/repo';
|
|
585
|
+
expect(normalizeRepoUrl(sshUrl)).toBe(expected);
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it('should normalize ssh:// protocol URL without username', () => {
|
|
589
|
+
const sshUrl = 'ssh://myhost/org/repo.git';
|
|
590
|
+
const expected = 'https://myhost/org/repo';
|
|
591
|
+
expect(normalizeRepoUrl(sshUrl)).toBe(expected);
|
|
592
|
+
});
|
|
525
593
|
});
|
|
526
594
|
|
|
527
595
|
describe('shouldIgnoreFile', () => {
|
package/src/index.js
CHANGED
|
@@ -99,7 +99,10 @@ export function handleSkippedResponse(response, options, spinner) {
|
|
|
99
99
|
const GEMINI_MODELS = [
|
|
100
100
|
{ value: 'gemini-2.5-pro', label: 'gemini-2.5-pro (high quality)' },
|
|
101
101
|
{ value: 'gemini-2.5-flash', label: 'gemini-2.5-flash (avoid, the worst quality)' },
|
|
102
|
-
{
|
|
102
|
+
{
|
|
103
|
+
value: 'gemini-3.1-pro-preview',
|
|
104
|
+
label: 'gemini-3.1-pro-preview (experimental - the best model)',
|
|
105
|
+
},
|
|
103
106
|
{
|
|
104
107
|
value: 'gemini-3-flash-preview',
|
|
105
108
|
label: 'gemini-3-flash-preview (experimental - the most efficient model)',
|
package/src/index.test.js
CHANGED
|
@@ -544,7 +544,7 @@ describe('--model flag behavior', () => {
|
|
|
544
544
|
const validModels = [
|
|
545
545
|
'gemini-2.5-pro',
|
|
546
546
|
'gemini-2.5-flash',
|
|
547
|
-
'gemini-3-pro-preview',
|
|
547
|
+
'gemini-3.1-pro-preview',
|
|
548
548
|
'gemini-3-flash-preview',
|
|
549
549
|
];
|
|
550
550
|
|
|
@@ -564,8 +564,8 @@ describe('--model flag behavior', () => {
|
|
|
564
564
|
expect(resolveModel('2')).toBe('gemini-2.5-flash');
|
|
565
565
|
});
|
|
566
566
|
|
|
567
|
-
it('should resolve "3" to gemini-3-pro-preview', () => {
|
|
568
|
-
expect(resolveModel('3')).toBe('gemini-3-pro-preview');
|
|
567
|
+
it('should resolve "3" to gemini-3.1-pro-preview', () => {
|
|
568
|
+
expect(resolveModel('3')).toBe('gemini-3.1-pro-preview');
|
|
569
569
|
});
|
|
570
570
|
|
|
571
571
|
it('should resolve "4" to gemini-3-flash-preview', () => {
|
|
@@ -573,7 +573,7 @@ describe('--model flag behavior', () => {
|
|
|
573
573
|
});
|
|
574
574
|
|
|
575
575
|
it('should pass through full model names unchanged', () => {
|
|
576
|
-
expect(resolveModel('gemini-3-pro-preview')).toBe('gemini-3-pro-preview');
|
|
576
|
+
expect(resolveModel('gemini-3.1-pro-preview')).toBe('gemini-3.1-pro-preview');
|
|
577
577
|
});
|
|
578
578
|
|
|
579
579
|
it('should pass through invalid input unchanged', () => {
|