gitpadi 2.1.3 → 2.1.4
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 +33 -11
- package/package.json +1 -1
- package/src/cli.ts +1 -1
- package/src/commands/drips.ts +36 -11
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.4';
|
|
30
30
|
let targetConfirmed = false;
|
|
31
31
|
let gitlabProjectConfirmed = false;
|
|
32
32
|
// ── Styling ────────────────────────────────────────────────────────────
|
package/dist/commands/drips.js
CHANGED
|
@@ -96,6 +96,11 @@ function parseSlug(input) {
|
|
|
96
96
|
function issueLabels(issue) {
|
|
97
97
|
return issue.labels.map(l => l.name.toLowerCase());
|
|
98
98
|
}
|
|
99
|
+
const ORG_APPLICATION_LIMIT = 4;
|
|
100
|
+
/** Extract the org/owner from a repo fullName like "myorg/myrepo" */
|
|
101
|
+
function extractOrg(issue) {
|
|
102
|
+
return issue.repo?.fullName?.split('/')[0]?.toLowerCase() || '__unknown__';
|
|
103
|
+
}
|
|
99
104
|
/** Detect which track an issue belongs to based on its labels and title */
|
|
100
105
|
function detectTrack(issue) {
|
|
101
106
|
const haystack = [...issueLabels(issue), issue.title.toLowerCase()].join(' ');
|
|
@@ -265,13 +270,12 @@ async function setupProfile(existing) {
|
|
|
265
270
|
type: 'list',
|
|
266
271
|
name: 'minPoints',
|
|
267
272
|
message: bold('Minimum points to apply for:'),
|
|
268
|
-
default: existing?.minPoints
|
|
273
|
+
default: existing?.minPoints ?? 100,
|
|
269
274
|
choices: [
|
|
270
|
-
{ name: ` Any
|
|
271
|
-
{ name: `
|
|
272
|
-
{ name: `
|
|
273
|
-
{ name: ` 200
|
|
274
|
-
{ name: ` 500+ pts`, value: 500 },
|
|
275
|
+
{ name: ` Any (including unspecified)`, value: 0 },
|
|
276
|
+
{ name: ` 100 pts`, value: 100 },
|
|
277
|
+
{ name: ` 150 pts`, value: 150 },
|
|
278
|
+
{ name: ` 200 pts`, value: 200 },
|
|
275
279
|
],
|
|
276
280
|
}]);
|
|
277
281
|
const techStack = stackInput.split(',').map((s) => s.trim()).filter(Boolean);
|
|
@@ -344,15 +348,23 @@ async function autoApply(program, profile) {
|
|
|
344
348
|
}
|
|
345
349
|
console.log();
|
|
346
350
|
// Checkbox selection
|
|
351
|
+
// Pre-compute org counts to show how many slots remain per org
|
|
352
|
+
const previewOrgCounts = new Map();
|
|
347
353
|
const checkboxChoices = top.map(({ issue, score, matchReasons }) => {
|
|
348
354
|
const pts = issue.points ? green(`+${issue.points}pts`) : dim('—pts');
|
|
349
355
|
const applicants = issue.pendingApplicationsCount > 0 ? yellow(`${issue.pendingApplicationsCount}▲`) : dim('0▲');
|
|
350
356
|
const match = matchReasons.length > 0 ? cyan(`[${matchReasons.slice(0, 3).join(', ')}]`) : '';
|
|
351
|
-
const
|
|
357
|
+
const org = extractOrg(issue);
|
|
358
|
+
const orgSlots = ORG_APPLICATION_LIMIT - (previewOrgCounts.get(org) || 0);
|
|
359
|
+
const orgTag = dim(`(${org} ${orgSlots}/${ORG_APPLICATION_LIMIT} slots)`);
|
|
360
|
+
const title = truncate(issue.title, 40);
|
|
361
|
+
const autoCheck = score >= 1 && orgSlots > 0;
|
|
362
|
+
if (autoCheck)
|
|
363
|
+
previewOrgCounts.set(org, (previewOrgCounts.get(org) || 0) + 1);
|
|
352
364
|
return {
|
|
353
|
-
name: ` ${pts} ${bold(title)} ${match} ${applicants}`,
|
|
365
|
+
name: ` ${pts} ${bold(title)} ${match} ${applicants} ${orgTag}`,
|
|
354
366
|
value: issue,
|
|
355
|
-
checked:
|
|
367
|
+
checked: autoCheck,
|
|
356
368
|
};
|
|
357
369
|
});
|
|
358
370
|
const { selectedIssues } = await inquirer.prompt([{
|
|
@@ -413,11 +425,21 @@ async function autoApply(program, profile) {
|
|
|
413
425
|
console.log();
|
|
414
426
|
let applied = 0;
|
|
415
427
|
let failed = 0;
|
|
428
|
+
let skipped = 0;
|
|
429
|
+
const orgCounts = new Map();
|
|
416
430
|
for (const issue of selectedIssues) {
|
|
431
|
+
const org = extractOrg(issue);
|
|
432
|
+
const orgCount = orgCounts.get(org) || 0;
|
|
433
|
+
if (orgCount >= ORG_APPLICATION_LIMIT) {
|
|
434
|
+
console.log(yellow(` ⚠ Skipped: ${truncate(issue.title, 50)} — already applied to ${ORG_APPLICATION_LIMIT} issues from ${org}`));
|
|
435
|
+
skipped++;
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
417
438
|
const spinner = ora(` Applying to: ${truncate(issue.title, 50)}…`).start();
|
|
418
439
|
try {
|
|
419
440
|
await dripsPost(`/api/wave-programs/${program.id}/issues/${issue.id}/applications`, { applicationText: baseMessage }, token);
|
|
420
|
-
|
|
441
|
+
orgCounts.set(org, orgCount + 1);
|
|
442
|
+
spinner.succeed(green(` ✅ ${truncate(issue.title, 55)}`) + (issue.points ? dim(` (+${issue.points}pts)`) : '') + dim(` [${org}: ${orgCount + 1}/${ORG_APPLICATION_LIMIT}]`));
|
|
421
443
|
applied++;
|
|
422
444
|
}
|
|
423
445
|
catch (e) {
|
|
@@ -431,7 +453,7 @@ async function autoApply(program, profile) {
|
|
|
431
453
|
}
|
|
432
454
|
}
|
|
433
455
|
console.log();
|
|
434
|
-
console.log(bold(` Done: ${green(applied + ' applied')}${failed > 0 ? ', ' + red(failed + ' failed') : ''}`));
|
|
456
|
+
console.log(bold(` Done: ${green(applied + ' applied')}${skipped > 0 ? ', ' + yellow(skipped + ' skipped (org limit)') : ''}${failed > 0 ? ', ' + red(failed + ' failed') : ''}`));
|
|
435
457
|
console.log(dim(`\n Track your applications: ${cyan(DRIPS_WEB + '/wave/' + program.slug)}\n`));
|
|
436
458
|
}
|
|
437
459
|
// ── Browse by track ───────────────────────────────────────────────────────────
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitpadi",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
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.4';
|
|
39
39
|
let targetConfirmed = false;
|
|
40
40
|
let gitlabProjectConfirmed = false;
|
|
41
41
|
|
package/src/commands/drips.ts
CHANGED
|
@@ -154,6 +154,13 @@ function issueLabels(issue: DripsIssue): string[] {
|
|
|
154
154
|
return issue.labels.map(l => l.name.toLowerCase());
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
const ORG_APPLICATION_LIMIT = 4;
|
|
158
|
+
|
|
159
|
+
/** Extract the org/owner from a repo fullName like "myorg/myrepo" */
|
|
160
|
+
function extractOrg(issue: DripsIssue): string {
|
|
161
|
+
return issue.repo?.fullName?.split('/')[0]?.toLowerCase() || '__unknown__';
|
|
162
|
+
}
|
|
163
|
+
|
|
157
164
|
/** Detect which track an issue belongs to based on its labels and title */
|
|
158
165
|
function detectTrack(issue: DripsIssue): Exclude<Track, 'all'> | null {
|
|
159
166
|
const haystack = [...issueLabels(issue), issue.title.toLowerCase()].join(' ');
|
|
@@ -335,13 +342,12 @@ async function setupProfile(existing?: DripsProfile): Promise<DripsProfile> {
|
|
|
335
342
|
type: 'list',
|
|
336
343
|
name: 'minPoints',
|
|
337
344
|
message: bold('Minimum points to apply for:'),
|
|
338
|
-
default: existing?.minPoints
|
|
345
|
+
default: existing?.minPoints ?? 100,
|
|
339
346
|
choices: [
|
|
340
|
-
{ name: ` Any
|
|
341
|
-
{ name: `
|
|
342
|
-
{ name: `
|
|
343
|
-
{ name: ` 200
|
|
344
|
-
{ name: ` 500+ pts`, value: 500 },
|
|
347
|
+
{ name: ` Any (including unspecified)`, value: 0 },
|
|
348
|
+
{ name: ` 100 pts`, value: 100 },
|
|
349
|
+
{ name: ` 150 pts`, value: 150 },
|
|
350
|
+
{ name: ` 200 pts`, value: 200 },
|
|
345
351
|
],
|
|
346
352
|
}]);
|
|
347
353
|
|
|
@@ -427,15 +433,22 @@ async function autoApply(program: DripsProgram, profile: DripsProfile): Promise<
|
|
|
427
433
|
console.log();
|
|
428
434
|
|
|
429
435
|
// Checkbox selection
|
|
436
|
+
// Pre-compute org counts to show how many slots remain per org
|
|
437
|
+
const previewOrgCounts = new Map<string, number>();
|
|
430
438
|
const checkboxChoices = top.map(({ issue, score, matchReasons }) => {
|
|
431
439
|
const pts = issue.points ? green(`+${issue.points}pts`) : dim('—pts');
|
|
432
440
|
const applicants = issue.pendingApplicationsCount > 0 ? yellow(`${issue.pendingApplicationsCount}▲`) : dim('0▲');
|
|
433
441
|
const match = matchReasons.length > 0 ? cyan(`[${matchReasons.slice(0, 3).join(', ')}]`) : '';
|
|
434
|
-
const
|
|
442
|
+
const org = extractOrg(issue);
|
|
443
|
+
const orgSlots = ORG_APPLICATION_LIMIT - (previewOrgCounts.get(org) || 0);
|
|
444
|
+
const orgTag = dim(`(${org} ${orgSlots}/${ORG_APPLICATION_LIMIT} slots)`);
|
|
445
|
+
const title = truncate(issue.title, 40);
|
|
446
|
+
const autoCheck = score >= 1 && orgSlots > 0;
|
|
447
|
+
if (autoCheck) previewOrgCounts.set(org, (previewOrgCounts.get(org) || 0) + 1);
|
|
435
448
|
return {
|
|
436
|
-
name: ` ${pts} ${bold(title)} ${match} ${applicants}`,
|
|
449
|
+
name: ` ${pts} ${bold(title)} ${match} ${applicants} ${orgTag}`,
|
|
437
450
|
value: issue,
|
|
438
|
-
checked:
|
|
451
|
+
checked: autoCheck,
|
|
439
452
|
};
|
|
440
453
|
});
|
|
441
454
|
|
|
@@ -502,8 +515,19 @@ async function autoApply(program: DripsProgram, profile: DripsProfile): Promise<
|
|
|
502
515
|
console.log();
|
|
503
516
|
let applied = 0;
|
|
504
517
|
let failed = 0;
|
|
518
|
+
let skipped = 0;
|
|
519
|
+
const orgCounts = new Map<string, number>();
|
|
505
520
|
|
|
506
521
|
for (const issue of selectedIssues as DripsIssue[]) {
|
|
522
|
+
const org = extractOrg(issue);
|
|
523
|
+
const orgCount = orgCounts.get(org) || 0;
|
|
524
|
+
|
|
525
|
+
if (orgCount >= ORG_APPLICATION_LIMIT) {
|
|
526
|
+
console.log(yellow(` ⚠ Skipped: ${truncate(issue.title, 50)} — already applied to ${ORG_APPLICATION_LIMIT} issues from ${org}`));
|
|
527
|
+
skipped++;
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
|
|
507
531
|
const spinner = ora(` Applying to: ${truncate(issue.title, 50)}…`).start();
|
|
508
532
|
try {
|
|
509
533
|
await dripsPost(
|
|
@@ -511,7 +535,8 @@ async function autoApply(program: DripsProgram, profile: DripsProfile): Promise<
|
|
|
511
535
|
{ applicationText: baseMessage },
|
|
512
536
|
token,
|
|
513
537
|
);
|
|
514
|
-
|
|
538
|
+
orgCounts.set(org, orgCount + 1);
|
|
539
|
+
spinner.succeed(green(` ✅ ${truncate(issue.title, 55)}`) + (issue.points ? dim(` (+${issue.points}pts)`) : '') + dim(` [${org}: ${orgCount + 1}/${ORG_APPLICATION_LIMIT}]`));
|
|
515
540
|
applied++;
|
|
516
541
|
} catch (e: any) {
|
|
517
542
|
spinner.fail(red(` ✗ ${truncate(issue.title, 55)} — ${e.message}`));
|
|
@@ -525,7 +550,7 @@ async function autoApply(program: DripsProgram, profile: DripsProfile): Promise<
|
|
|
525
550
|
}
|
|
526
551
|
|
|
527
552
|
console.log();
|
|
528
|
-
console.log(bold(` Done: ${green(applied + ' applied')}${failed > 0 ? ', ' + red(failed + ' failed') : ''}`));
|
|
553
|
+
console.log(bold(` Done: ${green(applied + ' applied')}${skipped > 0 ? ', ' + yellow(skipped + ' skipped (org limit)') : ''}${failed > 0 ? ', ' + red(failed + ' failed') : ''}`));
|
|
529
554
|
console.log(dim(`\n Track your applications: ${cyan(DRIPS_WEB + '/wave/' + program.slug)}\n`));
|
|
530
555
|
}
|
|
531
556
|
|