gitpadi 2.1.4 → 2.1.5
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/cli.js +1 -1
- package/dist/commands/drips.js +67 -44
- package/package.json +1 -1
- package/src/cli.ts +1 -1
- package/src/commands/drips.ts +71 -50
package/dist/cli.js
CHANGED
|
@@ -26,7 +26,7 @@ import { dripsMenu } from './commands/drips.js';
|
|
|
26
26
|
import * as gitlabIssues from './commands/gitlab-issues.js';
|
|
27
27
|
import * as gitlabMRs from './commands/gitlab-mrs.js';
|
|
28
28
|
import * as gitlabPipelines from './commands/gitlab-pipelines.js';
|
|
29
|
-
const VERSION = '2.1.
|
|
29
|
+
const VERSION = '2.1.5';
|
|
30
30
|
let targetConfirmed = false;
|
|
31
31
|
let gitlabProjectConfirmed = false;
|
|
32
32
|
// ── Styling ────────────────────────────────────────────────────────────
|
package/dist/commands/drips.js
CHANGED
|
@@ -456,13 +456,21 @@ async function autoApply(program, profile) {
|
|
|
456
456
|
console.log(bold(` Done: ${green(applied + ' applied')}${skipped > 0 ? ', ' + yellow(skipped + ' skipped (org limit)') : ''}${failed > 0 ? ', ' + red(failed + ' failed') : ''}`));
|
|
457
457
|
console.log(dim(`\n Track your applications: ${cyan(DRIPS_WEB + '/wave/' + program.slug)}\n`));
|
|
458
458
|
}
|
|
459
|
-
// ── Browse by track
|
|
459
|
+
// ── Browse by org + track ─────────────────────────────────────────────────────
|
|
460
460
|
async function browseByTrack(program, profile) {
|
|
461
|
-
//
|
|
461
|
+
// Step 1: ask for org name
|
|
462
|
+
const { orgInput } = await inquirer.prompt([{
|
|
463
|
+
type: 'input',
|
|
464
|
+
name: 'orgInput',
|
|
465
|
+
message: bold('Enter GitHub org name (e.g. stellar, uniswap, openzeppelin):'),
|
|
466
|
+
validate: (v) => v.trim().length > 0 || 'Required',
|
|
467
|
+
}]);
|
|
468
|
+
const targetOrg = orgInput.trim().toLowerCase();
|
|
469
|
+
// Step 2: ask for track
|
|
462
470
|
const { browseTrack } = await inquirer.prompt([{
|
|
463
471
|
type: 'list',
|
|
464
472
|
name: 'browseTrack',
|
|
465
|
-
message: bold('
|
|
473
|
+
message: bold('Filter by track:'),
|
|
466
474
|
default: profile.track,
|
|
467
475
|
choices: [
|
|
468
476
|
{ name: ` ${cyan('⚙️')} Backend`, value: 'backend' },
|
|
@@ -472,40 +480,52 @@ async function browseByTrack(program, profile) {
|
|
|
472
480
|
],
|
|
473
481
|
}]);
|
|
474
482
|
const selectedTrack = browseTrack;
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
catch (e) {
|
|
501
|
-
spinner.fail(` ${e.message}`);
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
if (allIssues.length === 0) {
|
|
505
|
-
console.log(yellow(`\n No unassigned ${selectedTrack} issues found on this page. Try next page or change track.\n`));
|
|
483
|
+
// Step 3: scan the wave program and collect ALL unassigned issues from this org
|
|
484
|
+
const spinner = ora(dim(` Scanning for unassigned ${selectedTrack} issues in ${bold(targetOrg)}…`)).start();
|
|
485
|
+
const collected = [];
|
|
486
|
+
let apiPage = 1;
|
|
487
|
+
const MAX_SCAN_PAGES = 20; // scan up to 1000 issues to find the org's full list
|
|
488
|
+
try {
|
|
489
|
+
while (apiPage <= MAX_SCAN_PAGES) {
|
|
490
|
+
const res = await fetchIssuePage(program.id, apiPage, 50);
|
|
491
|
+
for (const issue of res.data) {
|
|
492
|
+
const issueOrg = extractOrg(issue);
|
|
493
|
+
if (issueOrg !== targetOrg)
|
|
494
|
+
continue;
|
|
495
|
+
if (issue.assignedApplicant !== null)
|
|
496
|
+
continue;
|
|
497
|
+
if (!matchesTrack(issue, selectedTrack))
|
|
498
|
+
continue;
|
|
499
|
+
if (profile.minPoints > 0 && issue.points !== null && issue.points < profile.minPoints)
|
|
500
|
+
continue;
|
|
501
|
+
collected.push(issue);
|
|
502
|
+
}
|
|
503
|
+
spinner.text = dim(` Scanning page ${apiPage}/${MAX_SCAN_PAGES} — found ${collected.length} issues in ${targetOrg} so far…`);
|
|
504
|
+
if (!res.pagination.hasNextPage)
|
|
505
|
+
break;
|
|
506
|
+
apiPage++;
|
|
506
507
|
}
|
|
508
|
+
}
|
|
509
|
+
catch (e) {
|
|
510
|
+
spinner.fail(` ${e.message}`);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
// Sort by points descending
|
|
514
|
+
collected.sort((a, b) => (b.points || 0) - (a.points || 0));
|
|
515
|
+
if (collected.length === 0) {
|
|
516
|
+
spinner.warn(yellow(` No unassigned ${selectedTrack} issues found for org "${targetOrg}" in the ${program.name} wave.`));
|
|
517
|
+
console.log(dim(' Check the org name matches exactly (e.g. "stellar" not "Stellar").\n'));
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
spinner.succeed(` ${bold(collected.length + '')} unassigned ${selectedTrack} issue(s) from ${cyan(targetOrg)} — sorted by points`);
|
|
521
|
+
// Step 4: paginate through results (20 per page)
|
|
522
|
+
const PAGE_SIZE = 20;
|
|
523
|
+
let viewPage = 0;
|
|
524
|
+
while (true) {
|
|
525
|
+
const pageSlice = collected.slice(viewPage * PAGE_SIZE, (viewPage + 1) * PAGE_SIZE);
|
|
526
|
+
const totalPages = Math.ceil(collected.length / PAGE_SIZE);
|
|
507
527
|
console.log();
|
|
508
|
-
const choices =
|
|
528
|
+
const choices = pageSlice.map((issue) => {
|
|
509
529
|
const age = diffDays(issue.updatedAt);
|
|
510
530
|
const ageStr = age === 0 ? dim('today') : dim(`${age}d ago`);
|
|
511
531
|
const pts = issue.points ? green(`+${issue.points}pts`) : dim(' —pts');
|
|
@@ -524,26 +544,29 @@ async function browseByTrack(program, profile) {
|
|
|
524
544
|
const nav = [
|
|
525
545
|
new inquirer.Separator(dim(' ─────────────────────────────────────────────────────')),
|
|
526
546
|
];
|
|
527
|
-
if (
|
|
528
|
-
nav.push({ name: ` ${dim(
|
|
529
|
-
if (
|
|
530
|
-
nav.push({ name: ` ${dim(
|
|
547
|
+
if ((viewPage + 1) < totalPages)
|
|
548
|
+
nav.push({ name: ` ${dim(`→ Next page (${viewPage + 2}/${totalPages})`)}`, value: '__next__' });
|
|
549
|
+
if (viewPage > 0)
|
|
550
|
+
nav.push({ name: ` ${dim(`← Previous page (${viewPage}/${totalPages})`)}`, value: '__prev__' });
|
|
551
|
+
nav.push({ name: ` ${dim('🔍 Search a different org')}`, value: '__reorg__' });
|
|
531
552
|
nav.push({ name: ` ${dim('⬅ Back')}`, value: '__back__' });
|
|
532
553
|
const { selected } = await inquirer.prompt([{
|
|
533
554
|
type: 'list',
|
|
534
555
|
name: 'selected',
|
|
535
|
-
message: bold(`${
|
|
536
|
-
choices:
|
|
537
|
-
pageSize:
|
|
556
|
+
message: bold(`${targetOrg} — ${selectedTrack} issues (${collected.length} total, page ${viewPage + 1}/${totalPages}):`),
|
|
557
|
+
choices: [...choices, ...nav],
|
|
558
|
+
pageSize: 20,
|
|
538
559
|
}]);
|
|
539
560
|
if (selected === '__back__')
|
|
540
561
|
return;
|
|
562
|
+
if (selected === '__reorg__')
|
|
563
|
+
return browseByTrack(program, profile);
|
|
541
564
|
if (selected === '__next__') {
|
|
542
|
-
|
|
565
|
+
viewPage++;
|
|
543
566
|
continue;
|
|
544
567
|
}
|
|
545
568
|
if (selected === '__prev__') {
|
|
546
|
-
|
|
569
|
+
viewPage = Math.max(0, viewPage - 1);
|
|
547
570
|
continue;
|
|
548
571
|
}
|
|
549
572
|
await applySingle(program, selected, profile);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitpadi",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "GitPadi — AI-powered GitHub & GitLab management CLI. Fork repos, manage issues & PRs, score contributors, grade assignments, and automate everything. Powered by Anthropic Claude via GitLab Duo Agent Platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/cli.ts
CHANGED
|
@@ -35,7 +35,7 @@ import * as gitlabIssues from './commands/gitlab-issues.js';
|
|
|
35
35
|
import * as gitlabMRs from './commands/gitlab-mrs.js';
|
|
36
36
|
import * as gitlabPipelines from './commands/gitlab-pipelines.js';
|
|
37
37
|
|
|
38
|
-
const VERSION = '2.1.
|
|
38
|
+
const VERSION = '2.1.5';
|
|
39
39
|
let targetConfirmed = false;
|
|
40
40
|
let gitlabProjectConfirmed = false;
|
|
41
41
|
|
package/src/commands/drips.ts
CHANGED
|
@@ -554,70 +554,89 @@ async function autoApply(program: DripsProgram, profile: DripsProfile): Promise<
|
|
|
554
554
|
console.log(dim(`\n Track your applications: ${cyan(DRIPS_WEB + '/wave/' + program.slug)}\n`));
|
|
555
555
|
}
|
|
556
556
|
|
|
557
|
-
// ── Browse by track
|
|
557
|
+
// ── Browse by org + track ─────────────────────────────────────────────────────
|
|
558
558
|
|
|
559
559
|
async function browseByTrack(program: DripsProgram, profile: DripsProfile): Promise<void> {
|
|
560
|
-
//
|
|
560
|
+
// Step 1: ask for org name
|
|
561
|
+
const { orgInput } = await inquirer.prompt([{
|
|
562
|
+
type: 'input',
|
|
563
|
+
name: 'orgInput',
|
|
564
|
+
message: bold('Enter GitHub org name (e.g. stellar, uniswap, openzeppelin):'),
|
|
565
|
+
validate: (v: string) => v.trim().length > 0 || 'Required',
|
|
566
|
+
}]);
|
|
567
|
+
|
|
568
|
+
const targetOrg = orgInput.trim().toLowerCase();
|
|
569
|
+
|
|
570
|
+
// Step 2: ask for track
|
|
561
571
|
const { browseTrack } = await inquirer.prompt([{
|
|
562
572
|
type: 'list',
|
|
563
573
|
name: 'browseTrack',
|
|
564
|
-
message: bold('
|
|
574
|
+
message: bold('Filter by track:'),
|
|
565
575
|
default: profile.track,
|
|
566
576
|
choices: [
|
|
567
|
-
{ name: ` ${cyan('⚙️')} Backend`,
|
|
568
|
-
{ name: ` ${magenta('🎨')} Frontend`,
|
|
577
|
+
{ name: ` ${cyan('⚙️')} Backend`, value: 'backend' },
|
|
578
|
+
{ name: ` ${magenta('🎨')} Frontend`, value: 'frontend' },
|
|
569
579
|
{ name: ` ${yellow('📜')} Smart Contract`, value: 'contract' },
|
|
570
|
-
{ name: ` ${green('🌐')} All tracks`,
|
|
580
|
+
{ name: ` ${green('🌐')} All tracks`, value: 'all' },
|
|
571
581
|
],
|
|
572
582
|
}]);
|
|
573
583
|
|
|
574
584
|
const selectedTrack: Track = browseTrack;
|
|
575
|
-
let page = 1;
|
|
576
585
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
let allIssues: DripsIssue[] = [];
|
|
580
|
-
let hasNextPage = false;
|
|
581
|
-
let totalFetched = 0;
|
|
586
|
+
// Step 3: scan the wave program and collect ALL unassigned issues from this org
|
|
587
|
+
const spinner = ora(dim(` Scanning for unassigned ${selectedTrack} issues in ${bold(targetOrg)}…`)).start();
|
|
582
588
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
const batchStart = (page - 1) * 2 + 1;
|
|
587
|
-
const pageA = await fetchIssuePage(program.id, batchStart, 50);
|
|
588
|
-
const pageB = pageA.pagination.hasNextPage
|
|
589
|
-
? await fetchIssuePage(program.id, batchStart + 1, 50)
|
|
590
|
-
: { data: [], pagination: { ...pageA.pagination, hasNextPage: false } };
|
|
591
|
-
|
|
592
|
-
const raw = [...pageA.data, ...pageB.data];
|
|
593
|
-
totalFetched = raw.length;
|
|
594
|
-
|
|
595
|
-
// Filter: unassigned + track + min points
|
|
596
|
-
allIssues = raw.filter(i =>
|
|
597
|
-
i.assignedApplicant === null &&
|
|
598
|
-
matchesTrack(i, selectedTrack) &&
|
|
599
|
-
(profile.minPoints === 0 || i.points === null || i.points >= profile.minPoints)
|
|
600
|
-
);
|
|
589
|
+
const collected: DripsIssue[] = [];
|
|
590
|
+
let apiPage = 1;
|
|
591
|
+
const MAX_SCAN_PAGES = 20; // scan up to 1000 issues to find the org's full list
|
|
601
592
|
|
|
602
|
-
|
|
603
|
-
|
|
593
|
+
try {
|
|
594
|
+
while (apiPage <= MAX_SCAN_PAGES) {
|
|
595
|
+
const res = await fetchIssuePage(program.id, apiPage, 50);
|
|
596
|
+
|
|
597
|
+
for (const issue of res.data) {
|
|
598
|
+
const issueOrg = extractOrg(issue);
|
|
599
|
+
if (issueOrg !== targetOrg) continue;
|
|
600
|
+
if (issue.assignedApplicant !== null) continue;
|
|
601
|
+
if (!matchesTrack(issue, selectedTrack)) continue;
|
|
602
|
+
if (profile.minPoints > 0 && issue.points !== null && issue.points < profile.minPoints) continue;
|
|
603
|
+
collected.push(issue);
|
|
604
|
+
}
|
|
604
605
|
|
|
605
|
-
|
|
606
|
+
spinner.text = dim(` Scanning page ${apiPage}/${MAX_SCAN_PAGES} — found ${collected.length} issues in ${targetOrg} so far…`);
|
|
606
607
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
);
|
|
610
|
-
} catch (e: any) {
|
|
611
|
-
spinner.fail(` ${e.message}`);
|
|
612
|
-
return;
|
|
608
|
+
if (!res.pagination.hasNextPage) break;
|
|
609
|
+
apiPage++;
|
|
613
610
|
}
|
|
611
|
+
} catch (e: any) {
|
|
612
|
+
spinner.fail(` ${e.message}`);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
614
615
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
616
|
+
// Sort by points descending
|
|
617
|
+
collected.sort((a, b) => (b.points || 0) - (a.points || 0));
|
|
618
|
+
|
|
619
|
+
if (collected.length === 0) {
|
|
620
|
+
spinner.warn(yellow(` No unassigned ${selectedTrack} issues found for org "${targetOrg}" in the ${program.name} wave.`));
|
|
621
|
+
console.log(dim(' Check the org name matches exactly (e.g. "stellar" not "Stellar").\n'));
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
spinner.succeed(
|
|
626
|
+
` ${bold(collected.length + '')} unassigned ${selectedTrack} issue(s) from ${cyan(targetOrg)} — sorted by points`
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
// Step 4: paginate through results (20 per page)
|
|
630
|
+
const PAGE_SIZE = 20;
|
|
631
|
+
let viewPage = 0;
|
|
632
|
+
|
|
633
|
+
while (true) {
|
|
634
|
+
const pageSlice = collected.slice(viewPage * PAGE_SIZE, (viewPage + 1) * PAGE_SIZE);
|
|
635
|
+
const totalPages = Math.ceil(collected.length / PAGE_SIZE);
|
|
618
636
|
|
|
619
637
|
console.log();
|
|
620
|
-
|
|
638
|
+
|
|
639
|
+
const choices = pageSlice.map((issue) => {
|
|
621
640
|
const age = diffDays(issue.updatedAt);
|
|
622
641
|
const ageStr = age === 0 ? dim('today') : dim(`${age}d ago`);
|
|
623
642
|
const pts = issue.points ? green(`+${issue.points}pts`) : dim(' —pts');
|
|
@@ -637,21 +656,23 @@ async function browseByTrack(program: DripsProgram, profile: DripsProfile): Prom
|
|
|
637
656
|
const nav: any[] = [
|
|
638
657
|
new inquirer.Separator(dim(' ─────────────────────────────────────────────────────')),
|
|
639
658
|
];
|
|
640
|
-
if (
|
|
641
|
-
if (
|
|
659
|
+
if ((viewPage + 1) < totalPages) nav.push({ name: ` ${dim(`→ Next page (${viewPage + 2}/${totalPages})`)}`, value: '__next__' });
|
|
660
|
+
if (viewPage > 0) nav.push({ name: ` ${dim(`← Previous page (${viewPage}/${totalPages})`)}`, value: '__prev__' });
|
|
661
|
+
nav.push({ name: ` ${dim('🔍 Search a different org')}`, value: '__reorg__' });
|
|
642
662
|
nav.push({ name: ` ${dim('⬅ Back')}`, value: '__back__' });
|
|
643
663
|
|
|
644
664
|
const { selected } = await inquirer.prompt([{
|
|
645
665
|
type: 'list',
|
|
646
666
|
name: 'selected',
|
|
647
|
-
message: bold(`${
|
|
648
|
-
choices:
|
|
649
|
-
pageSize:
|
|
667
|
+
message: bold(`${targetOrg} — ${selectedTrack} issues (${collected.length} total, page ${viewPage + 1}/${totalPages}):`),
|
|
668
|
+
choices: [...choices, ...nav],
|
|
669
|
+
pageSize: 20,
|
|
650
670
|
}]);
|
|
651
671
|
|
|
652
672
|
if (selected === '__back__') return;
|
|
653
|
-
if (selected === '
|
|
654
|
-
if (selected === '
|
|
673
|
+
if (selected === '__reorg__') return browseByTrack(program, profile);
|
|
674
|
+
if (selected === '__next__') { viewPage++; continue; }
|
|
675
|
+
if (selected === '__prev__') { viewPage = Math.max(0, viewPage - 1); continue; }
|
|
655
676
|
|
|
656
677
|
await applySingle(program, selected as DripsIssue, profile);
|
|
657
678
|
}
|