gitpadi 2.1.5 → 2.1.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/dist/cli.js +1 -1
- package/dist/commands/drips.js +127 -13
- package/package.json +1 -1
- package/src/cli.ts +1 -1
- package/src/commands/drips.ts +142 -13
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.6';
|
|
30
30
|
let targetConfirmed = false;
|
|
31
31
|
let gitlabProjectConfirmed = false;
|
|
32
32
|
// ── Styling ────────────────────────────────────────────────────────────
|
package/dist/commands/drips.js
CHANGED
|
@@ -456,16 +456,122 @@ 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
|
+
// ── Repo/org search ───────────────────────────────────────────────────────────
|
|
460
|
+
/**
|
|
461
|
+
* Scans the wave program and returns unique repos whose org or repo name
|
|
462
|
+
* contains the search term (case-insensitive).
|
|
463
|
+
*/
|
|
464
|
+
async function searchRepos(program, term) {
|
|
465
|
+
const termLower = term.toLowerCase();
|
|
466
|
+
const repoMap = new Map();
|
|
467
|
+
const MAX_SEARCH_PAGES = 20;
|
|
468
|
+
for (let p = 1; p <= MAX_SEARCH_PAGES; p++) {
|
|
469
|
+
const res = await fetchIssuePage(program.id, p, 50);
|
|
470
|
+
for (const issue of res.data) {
|
|
471
|
+
if (!issue.repo)
|
|
472
|
+
continue;
|
|
473
|
+
const fullName = issue.repo.fullName.toLowerCase();
|
|
474
|
+
const [org, repo] = fullName.split('/');
|
|
475
|
+
if (!org.includes(termLower) && !repo.includes(termLower))
|
|
476
|
+
continue;
|
|
477
|
+
const existing = repoMap.get(fullName);
|
|
478
|
+
if (existing) {
|
|
479
|
+
existing.issueCount++;
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
repoMap.set(fullName, { org, repo, fullName, issueCount: 1 });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (!res.pagination.hasNextPage)
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
return Array.from(repoMap.values()).sort((a, b) => b.issueCount - a.issueCount);
|
|
489
|
+
}
|
|
459
490
|
// ── Browse by org + track ─────────────────────────────────────────────────────
|
|
460
491
|
async function browseByTrack(program, profile) {
|
|
461
|
-
// Step 1: ask for org name
|
|
492
|
+
// Step 1: ask for org/repo name with search fallback
|
|
462
493
|
const { orgInput } = await inquirer.prompt([{
|
|
463
494
|
type: 'input',
|
|
464
495
|
name: 'orgInput',
|
|
465
|
-
message: bold('Enter
|
|
496
|
+
message: bold('Enter org name or repo name to search (e.g. stellar, Inheritx, uniswap):'),
|
|
466
497
|
validate: (v) => v.trim().length > 0 || 'Required',
|
|
467
498
|
}]);
|
|
468
|
-
const
|
|
499
|
+
const term = orgInput.trim();
|
|
500
|
+
// Search for matching repos in the wave program
|
|
501
|
+
const searchSpinner = ora(dim(` Searching for "${term}" in ${program.name} wave…`)).start();
|
|
502
|
+
let matches;
|
|
503
|
+
try {
|
|
504
|
+
matches = await searchRepos(program, term);
|
|
505
|
+
}
|
|
506
|
+
catch (e) {
|
|
507
|
+
searchSpinner.fail(` Search failed: ${e.message}`);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (matches.length === 0) {
|
|
511
|
+
searchSpinner.warn(yellow(` No repos found matching "${term}" in the ${program.name} wave.`));
|
|
512
|
+
console.log(dim(' Try a shorter or different search term.\n'));
|
|
513
|
+
return browseByTrack(program, profile);
|
|
514
|
+
}
|
|
515
|
+
searchSpinner.succeed(` Found ${matches.length} repo(s) matching "${term}"`);
|
|
516
|
+
console.log();
|
|
517
|
+
// If exact single match, use it directly — otherwise let user pick
|
|
518
|
+
// Value is the full repo name (org/repo) so we can filter precisely
|
|
519
|
+
let targetFullName; // e.g. "inheritx/smart-contracts" or "stellar" (org-level)
|
|
520
|
+
let displayLabel;
|
|
521
|
+
const exactOrg = matches.find(m => m.org === term.toLowerCase());
|
|
522
|
+
const exactRepo = matches.find(m => m.fullName === term.toLowerCase());
|
|
523
|
+
if (matches.length === 1) {
|
|
524
|
+
targetFullName = matches[0].fullName;
|
|
525
|
+
displayLabel = matches[0].fullName;
|
|
526
|
+
console.log(dim(` Using repo: ${cyan(displayLabel)}\n`));
|
|
527
|
+
}
|
|
528
|
+
else if (exactRepo) {
|
|
529
|
+
targetFullName = exactRepo.fullName;
|
|
530
|
+
displayLabel = exactRepo.fullName;
|
|
531
|
+
console.log(dim(` Using repo: ${cyan(displayLabel)}\n`));
|
|
532
|
+
}
|
|
533
|
+
else if (exactOrg) {
|
|
534
|
+
// Exact org match — show all repos under that org
|
|
535
|
+
targetFullName = exactOrg.org; // org-level (prefix match)
|
|
536
|
+
displayLabel = exactOrg.org + '/*';
|
|
537
|
+
console.log(dim(` Using org: ${cyan(exactOrg.org)} (all repos)\n`));
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
// Multiple partial matches — let user pick repo or entire org
|
|
541
|
+
const repoChoices = [];
|
|
542
|
+
// Group by org: if org has multiple repos, offer org-level option first
|
|
543
|
+
const orgs = [...new Set(matches.map(m => m.org))];
|
|
544
|
+
for (const org of orgs) {
|
|
545
|
+
const orgRepos = matches.filter(m => m.org === org);
|
|
546
|
+
if (orgRepos.length > 1) {
|
|
547
|
+
const total = orgRepos.reduce((s, r) => s + r.issueCount, 0);
|
|
548
|
+
repoChoices.push({
|
|
549
|
+
name: ` ${cyan(org + '/*')} ${dim(`all ${orgRepos.length} repos · ${total} issues`)}`,
|
|
550
|
+
value: org,
|
|
551
|
+
short: org + '/*',
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
for (const r of orgRepos) {
|
|
555
|
+
repoChoices.push({
|
|
556
|
+
name: ` ${dim('└')} ${cyan(r.fullName)} ${dim(r.issueCount + ' issue(s)')}`,
|
|
557
|
+
value: r.fullName,
|
|
558
|
+
short: r.fullName,
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
repoChoices.push(new inquirer.Separator(dim(' ───────────────────────────')), { name: ` ${dim('🔍 Search again')}`, value: '__search__', short: 'search' });
|
|
563
|
+
const { picked } = await inquirer.prompt([{
|
|
564
|
+
type: 'list',
|
|
565
|
+
name: 'picked',
|
|
566
|
+
message: bold('Pick the org or repo to browse:'),
|
|
567
|
+
choices: repoChoices,
|
|
568
|
+
pageSize: 18,
|
|
569
|
+
}]);
|
|
570
|
+
if (picked === '__search__')
|
|
571
|
+
return browseByTrack(program, profile);
|
|
572
|
+
targetFullName = picked;
|
|
573
|
+
displayLabel = picked.includes('/') && !picked.endsWith('/*') ? picked : picked + '/*';
|
|
574
|
+
}
|
|
469
575
|
// Step 2: ask for track
|
|
470
576
|
const { browseTrack } = await inquirer.prompt([{
|
|
471
577
|
type: 'list',
|
|
@@ -480,17 +586,25 @@ async function browseByTrack(program, profile) {
|
|
|
480
586
|
],
|
|
481
587
|
}]);
|
|
482
588
|
const selectedTrack = browseTrack;
|
|
483
|
-
// Step 3: scan
|
|
484
|
-
const
|
|
589
|
+
// Step 3: scan and collect — match by exact repo fullName or org prefix
|
|
590
|
+
const isOrgLevel = !targetFullName.includes('/'); // "stellar" vs "stellar/js-stellar-sdk"
|
|
591
|
+
const spinner = ora(dim(` Scanning ${program.name} for unassigned ${selectedTrack} issues in ${bold(displayLabel)}…`)).start();
|
|
485
592
|
const collected = [];
|
|
486
593
|
let apiPage = 1;
|
|
487
|
-
const MAX_SCAN_PAGES = 20;
|
|
594
|
+
const MAX_SCAN_PAGES = 20;
|
|
488
595
|
try {
|
|
489
596
|
while (apiPage <= MAX_SCAN_PAGES) {
|
|
490
597
|
const res = await fetchIssuePage(program.id, apiPage, 50);
|
|
491
598
|
for (const issue of res.data) {
|
|
492
|
-
|
|
493
|
-
|
|
599
|
+
if (!issue.repo)
|
|
600
|
+
continue;
|
|
601
|
+
const issueFullName = issue.repo.fullName.toLowerCase();
|
|
602
|
+
const issueOrg = issueFullName.split('/')[0];
|
|
603
|
+
// Match: exact repo OR org-level (all repos under that org)
|
|
604
|
+
const matches = isOrgLevel
|
|
605
|
+
? issueOrg === targetFullName
|
|
606
|
+
: issueFullName === targetFullName;
|
|
607
|
+
if (!matches)
|
|
494
608
|
continue;
|
|
495
609
|
if (issue.assignedApplicant !== null)
|
|
496
610
|
continue;
|
|
@@ -500,7 +614,7 @@ async function browseByTrack(program, profile) {
|
|
|
500
614
|
continue;
|
|
501
615
|
collected.push(issue);
|
|
502
616
|
}
|
|
503
|
-
spinner.text = dim(` Scanning page ${apiPage}/${MAX_SCAN_PAGES} —
|
|
617
|
+
spinner.text = dim(` Scanning page ${apiPage}/${MAX_SCAN_PAGES} — ${collected.length} matching issue(s) so far…`);
|
|
504
618
|
if (!res.pagination.hasNextPage)
|
|
505
619
|
break;
|
|
506
620
|
apiPage++;
|
|
@@ -513,11 +627,11 @@ async function browseByTrack(program, profile) {
|
|
|
513
627
|
// Sort by points descending
|
|
514
628
|
collected.sort((a, b) => (b.points || 0) - (a.points || 0));
|
|
515
629
|
if (collected.length === 0) {
|
|
516
|
-
spinner.warn(yellow(` No unassigned ${selectedTrack} issues found for
|
|
517
|
-
console.log(dim('
|
|
630
|
+
spinner.warn(yellow(` No unassigned ${selectedTrack} issues found for "${displayLabel}" in the ${program.name} wave.`));
|
|
631
|
+
console.log(dim(' Try a different track, lower min points, or search again.\n'));
|
|
518
632
|
return;
|
|
519
633
|
}
|
|
520
|
-
spinner.succeed(` ${bold(collected.length + '')} unassigned ${selectedTrack} issue(s) from ${cyan(
|
|
634
|
+
spinner.succeed(` ${bold(collected.length + '')} unassigned ${selectedTrack} issue(s) from ${cyan(displayLabel)} — sorted by points`);
|
|
521
635
|
// Step 4: paginate through results (20 per page)
|
|
522
636
|
const PAGE_SIZE = 20;
|
|
523
637
|
let viewPage = 0;
|
|
@@ -553,7 +667,7 @@ async function browseByTrack(program, profile) {
|
|
|
553
667
|
const { selected } = await inquirer.prompt([{
|
|
554
668
|
type: 'list',
|
|
555
669
|
name: 'selected',
|
|
556
|
-
message: bold(`${
|
|
670
|
+
message: bold(`${displayLabel} — ${selectedTrack} issues (${collected.length} total, page ${viewPage + 1}/${totalPages}):`),
|
|
557
671
|
choices: [...choices, ...nav],
|
|
558
672
|
pageSize: 20,
|
|
559
673
|
}]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitpadi",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.6",
|
|
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.6';
|
|
39
39
|
let targetConfirmed = false;
|
|
40
40
|
let gitlabProjectConfirmed = false;
|
|
41
41
|
|
package/src/commands/drips.ts
CHANGED
|
@@ -554,18 +554,138 @@ 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
|
+
// ── Repo/org search ───────────────────────────────────────────────────────────
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Scans the wave program and returns unique repos whose org or repo name
|
|
561
|
+
* contains the search term (case-insensitive).
|
|
562
|
+
*/
|
|
563
|
+
async function searchRepos(
|
|
564
|
+
program: DripsProgram,
|
|
565
|
+
term: string,
|
|
566
|
+
): Promise<Array<{ org: string; repo: string; fullName: string; issueCount: number }>> {
|
|
567
|
+
const termLower = term.toLowerCase();
|
|
568
|
+
const repoMap = new Map<string, { org: string; repo: string; fullName: string; issueCount: number }>();
|
|
569
|
+
const MAX_SEARCH_PAGES = 20;
|
|
570
|
+
|
|
571
|
+
for (let p = 1; p <= MAX_SEARCH_PAGES; p++) {
|
|
572
|
+
const res = await fetchIssuePage(program.id, p, 50);
|
|
573
|
+
|
|
574
|
+
for (const issue of res.data) {
|
|
575
|
+
if (!issue.repo) continue;
|
|
576
|
+
const fullName = issue.repo.fullName.toLowerCase();
|
|
577
|
+
const [org, repo] = fullName.split('/');
|
|
578
|
+
if (!org.includes(termLower) && !repo.includes(termLower)) continue;
|
|
579
|
+
|
|
580
|
+
const existing = repoMap.get(fullName);
|
|
581
|
+
if (existing) {
|
|
582
|
+
existing.issueCount++;
|
|
583
|
+
} else {
|
|
584
|
+
repoMap.set(fullName, { org, repo, fullName, issueCount: 1 });
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (!res.pagination.hasNextPage) break;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return Array.from(repoMap.values()).sort((a, b) => b.issueCount - a.issueCount);
|
|
592
|
+
}
|
|
593
|
+
|
|
557
594
|
// ── Browse by org + track ─────────────────────────────────────────────────────
|
|
558
595
|
|
|
559
596
|
async function browseByTrack(program: DripsProgram, profile: DripsProfile): Promise<void> {
|
|
560
|
-
// Step 1: ask for org name
|
|
597
|
+
// Step 1: ask for org/repo name with search fallback
|
|
561
598
|
const { orgInput } = await inquirer.prompt([{
|
|
562
599
|
type: 'input',
|
|
563
600
|
name: 'orgInput',
|
|
564
|
-
message: bold('Enter
|
|
601
|
+
message: bold('Enter org name or repo name to search (e.g. stellar, Inheritx, uniswap):'),
|
|
565
602
|
validate: (v: string) => v.trim().length > 0 || 'Required',
|
|
566
603
|
}]);
|
|
567
604
|
|
|
568
|
-
const
|
|
605
|
+
const term = orgInput.trim();
|
|
606
|
+
|
|
607
|
+
// Search for matching repos in the wave program
|
|
608
|
+
const searchSpinner = ora(dim(` Searching for "${term}" in ${program.name} wave…`)).start();
|
|
609
|
+
let matches: Array<{ org: string; repo: string; fullName: string; issueCount: number }>;
|
|
610
|
+
try {
|
|
611
|
+
matches = await searchRepos(program, term);
|
|
612
|
+
} catch (e: any) {
|
|
613
|
+
searchSpinner.fail(` Search failed: ${e.message}`);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (matches.length === 0) {
|
|
618
|
+
searchSpinner.warn(yellow(` No repos found matching "${term}" in the ${program.name} wave.`));
|
|
619
|
+
console.log(dim(' Try a shorter or different search term.\n'));
|
|
620
|
+
return browseByTrack(program, profile);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
searchSpinner.succeed(` Found ${matches.length} repo(s) matching "${term}"`);
|
|
624
|
+
console.log();
|
|
625
|
+
|
|
626
|
+
// If exact single match, use it directly — otherwise let user pick
|
|
627
|
+
// Value is the full repo name (org/repo) so we can filter precisely
|
|
628
|
+
let targetFullName: string; // e.g. "inheritx/smart-contracts" or "stellar" (org-level)
|
|
629
|
+
let displayLabel: string;
|
|
630
|
+
|
|
631
|
+
const exactOrg = matches.find(m => m.org === term.toLowerCase());
|
|
632
|
+
const exactRepo = matches.find(m => m.fullName === term.toLowerCase());
|
|
633
|
+
|
|
634
|
+
if (matches.length === 1) {
|
|
635
|
+
targetFullName = matches[0].fullName;
|
|
636
|
+
displayLabel = matches[0].fullName;
|
|
637
|
+
console.log(dim(` Using repo: ${cyan(displayLabel)}\n`));
|
|
638
|
+
} else if (exactRepo) {
|
|
639
|
+
targetFullName = exactRepo.fullName;
|
|
640
|
+
displayLabel = exactRepo.fullName;
|
|
641
|
+
console.log(dim(` Using repo: ${cyan(displayLabel)}\n`));
|
|
642
|
+
} else if (exactOrg) {
|
|
643
|
+
// Exact org match — show all repos under that org
|
|
644
|
+
targetFullName = exactOrg.org; // org-level (prefix match)
|
|
645
|
+
displayLabel = exactOrg.org + '/*';
|
|
646
|
+
console.log(dim(` Using org: ${cyan(exactOrg.org)} (all repos)\n`));
|
|
647
|
+
} else {
|
|
648
|
+
// Multiple partial matches — let user pick repo or entire org
|
|
649
|
+
const repoChoices: any[] = [];
|
|
650
|
+
|
|
651
|
+
// Group by org: if org has multiple repos, offer org-level option first
|
|
652
|
+
const orgs = [...new Set(matches.map(m => m.org))];
|
|
653
|
+
for (const org of orgs) {
|
|
654
|
+
const orgRepos = matches.filter(m => m.org === org);
|
|
655
|
+
if (orgRepos.length > 1) {
|
|
656
|
+
const total = orgRepos.reduce((s, r) => s + r.issueCount, 0);
|
|
657
|
+
repoChoices.push({
|
|
658
|
+
name: ` ${cyan(org + '/*')} ${dim(`all ${orgRepos.length} repos · ${total} issues`)}`,
|
|
659
|
+
value: org,
|
|
660
|
+
short: org + '/*',
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
for (const r of orgRepos) {
|
|
664
|
+
repoChoices.push({
|
|
665
|
+
name: ` ${dim('└')} ${cyan(r.fullName)} ${dim(r.issueCount + ' issue(s)')}`,
|
|
666
|
+
value: r.fullName,
|
|
667
|
+
short: r.fullName,
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
repoChoices.push(
|
|
673
|
+
new inquirer.Separator(dim(' ───────────────────────────')) as any,
|
|
674
|
+
{ name: ` ${dim('🔍 Search again')}`, value: '__search__', short: 'search' },
|
|
675
|
+
);
|
|
676
|
+
|
|
677
|
+
const { picked } = await inquirer.prompt([{
|
|
678
|
+
type: 'list',
|
|
679
|
+
name: 'picked',
|
|
680
|
+
message: bold('Pick the org or repo to browse:'),
|
|
681
|
+
choices: repoChoices,
|
|
682
|
+
pageSize: 18,
|
|
683
|
+
}]);
|
|
684
|
+
|
|
685
|
+
if (picked === '__search__') return browseByTrack(program, profile);
|
|
686
|
+
targetFullName = picked;
|
|
687
|
+
displayLabel = picked.includes('/') && !picked.endsWith('/*') ? picked : picked + '/*';
|
|
688
|
+
}
|
|
569
689
|
|
|
570
690
|
// Step 2: ask for track
|
|
571
691
|
const { browseTrack } = await inquirer.prompt([{
|
|
@@ -583,27 +703,36 @@ async function browseByTrack(program: DripsProgram, profile: DripsProfile): Prom
|
|
|
583
703
|
|
|
584
704
|
const selectedTrack: Track = browseTrack;
|
|
585
705
|
|
|
586
|
-
// Step 3: scan
|
|
587
|
-
const
|
|
706
|
+
// Step 3: scan and collect — match by exact repo fullName or org prefix
|
|
707
|
+
const isOrgLevel = !targetFullName.includes('/'); // "stellar" vs "stellar/js-stellar-sdk"
|
|
708
|
+
const spinner = ora(dim(` Scanning ${program.name} for unassigned ${selectedTrack} issues in ${bold(displayLabel)}…`)).start();
|
|
588
709
|
|
|
589
710
|
const collected: DripsIssue[] = [];
|
|
590
711
|
let apiPage = 1;
|
|
591
|
-
const MAX_SCAN_PAGES = 20;
|
|
712
|
+
const MAX_SCAN_PAGES = 20;
|
|
592
713
|
|
|
593
714
|
try {
|
|
594
715
|
while (apiPage <= MAX_SCAN_PAGES) {
|
|
595
716
|
const res = await fetchIssuePage(program.id, apiPage, 50);
|
|
596
717
|
|
|
597
718
|
for (const issue of res.data) {
|
|
598
|
-
|
|
599
|
-
|
|
719
|
+
if (!issue.repo) continue;
|
|
720
|
+
const issueFullName = issue.repo.fullName.toLowerCase();
|
|
721
|
+
const issueOrg = issueFullName.split('/')[0];
|
|
722
|
+
|
|
723
|
+
// Match: exact repo OR org-level (all repos under that org)
|
|
724
|
+
const matches = isOrgLevel
|
|
725
|
+
? issueOrg === targetFullName
|
|
726
|
+
: issueFullName === targetFullName;
|
|
727
|
+
if (!matches) continue;
|
|
728
|
+
|
|
600
729
|
if (issue.assignedApplicant !== null) continue;
|
|
601
730
|
if (!matchesTrack(issue, selectedTrack)) continue;
|
|
602
731
|
if (profile.minPoints > 0 && issue.points !== null && issue.points < profile.minPoints) continue;
|
|
603
732
|
collected.push(issue);
|
|
604
733
|
}
|
|
605
734
|
|
|
606
|
-
spinner.text = dim(` Scanning page ${apiPage}/${MAX_SCAN_PAGES} —
|
|
735
|
+
spinner.text = dim(` Scanning page ${apiPage}/${MAX_SCAN_PAGES} — ${collected.length} matching issue(s) so far…`);
|
|
607
736
|
|
|
608
737
|
if (!res.pagination.hasNextPage) break;
|
|
609
738
|
apiPage++;
|
|
@@ -617,13 +746,13 @@ async function browseByTrack(program: DripsProgram, profile: DripsProfile): Prom
|
|
|
617
746
|
collected.sort((a, b) => (b.points || 0) - (a.points || 0));
|
|
618
747
|
|
|
619
748
|
if (collected.length === 0) {
|
|
620
|
-
spinner.warn(yellow(` No unassigned ${selectedTrack} issues found for
|
|
621
|
-
console.log(dim('
|
|
749
|
+
spinner.warn(yellow(` No unassigned ${selectedTrack} issues found for "${displayLabel}" in the ${program.name} wave.`));
|
|
750
|
+
console.log(dim(' Try a different track, lower min points, or search again.\n'));
|
|
622
751
|
return;
|
|
623
752
|
}
|
|
624
753
|
|
|
625
754
|
spinner.succeed(
|
|
626
|
-
` ${bold(collected.length + '')} unassigned ${selectedTrack} issue(s) from ${cyan(
|
|
755
|
+
` ${bold(collected.length + '')} unassigned ${selectedTrack} issue(s) from ${cyan(displayLabel)} — sorted by points`
|
|
627
756
|
);
|
|
628
757
|
|
|
629
758
|
// Step 4: paginate through results (20 per page)
|
|
@@ -664,7 +793,7 @@ async function browseByTrack(program: DripsProgram, profile: DripsProfile): Prom
|
|
|
664
793
|
const { selected } = await inquirer.prompt([{
|
|
665
794
|
type: 'list',
|
|
666
795
|
name: 'selected',
|
|
667
|
-
message: bold(`${
|
|
796
|
+
message: bold(`${displayLabel} — ${selectedTrack} issues (${collected.length} total, page ${viewPage + 1}/${totalPages}):`),
|
|
668
797
|
choices: [...choices, ...nav],
|
|
669
798
|
pageSize: 20,
|
|
670
799
|
}]);
|