@bragduck/cli 2.10.3 → 2.12.1
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/bin/bragduck.js +203 -23
- package/dist/bin/bragduck.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/bragduck.js
CHANGED
|
@@ -3411,6 +3411,52 @@ var JiraService = class {
|
|
|
3411
3411
|
};
|
|
3412
3412
|
return typeScores[issue.fields.issuetype.name] || 100;
|
|
3413
3413
|
}
|
|
3414
|
+
/**
|
|
3415
|
+
* Determine the type of contribution the current user made to an issue
|
|
3416
|
+
* Returns: 'created' | 'assigned-resolved' | 'edited'
|
|
3417
|
+
*/
|
|
3418
|
+
async determineJiraContributionType(issue, userEmail) {
|
|
3419
|
+
if (issue.fields.creator.emailAddress === userEmail) {
|
|
3420
|
+
return {
|
|
3421
|
+
type: "created",
|
|
3422
|
+
details: `Created ${issue.fields.issuetype.name}: ${issue.fields.status.name}`
|
|
3423
|
+
};
|
|
3424
|
+
}
|
|
3425
|
+
const isAssigned = issue.fields.assignee?.emailAddress === userEmail;
|
|
3426
|
+
const isResolved = issue.fields.resolutiondate !== null && issue.fields.resolutiondate !== void 0;
|
|
3427
|
+
const userEdits = issue.changelog?.histories?.filter((history) => history.author.emailAddress === userEmail) || [];
|
|
3428
|
+
const hasEdits = userEdits.length > 0;
|
|
3429
|
+
if (isAssigned && isResolved) {
|
|
3430
|
+
return {
|
|
3431
|
+
type: "assigned-resolved",
|
|
3432
|
+
details: `Assigned and resolved ${issue.fields.issuetype.name}${hasEdits ? ` with ${userEdits.length} update${userEdits.length > 1 ? "s" : ""}` : ""}`
|
|
3433
|
+
};
|
|
3434
|
+
}
|
|
3435
|
+
if (hasEdits) {
|
|
3436
|
+
return {
|
|
3437
|
+
type: "edited",
|
|
3438
|
+
details: `Updated ${issue.fields.issuetype.name} (${userEdits.length} change${userEdits.length > 1 ? "s" : ""})`
|
|
3439
|
+
};
|
|
3440
|
+
}
|
|
3441
|
+
return {
|
|
3442
|
+
type: "edited",
|
|
3443
|
+
details: `Contributed to ${issue.fields.issuetype.name}`
|
|
3444
|
+
};
|
|
3445
|
+
}
|
|
3446
|
+
/**
|
|
3447
|
+
* Calculate impact score based on contribution type
|
|
3448
|
+
*/
|
|
3449
|
+
calculateJiraContributionImpact(contributionType, baseComplexity) {
|
|
3450
|
+
const multipliers = {
|
|
3451
|
+
created: 1,
|
|
3452
|
+
// 100% - highest impact (created the issue)
|
|
3453
|
+
"assigned-resolved": 0.8,
|
|
3454
|
+
// 80% - high impact (owned and resolved)
|
|
3455
|
+
edited: 0.4
|
|
3456
|
+
// 40% - medium impact (contributed updates)
|
|
3457
|
+
};
|
|
3458
|
+
return Math.ceil(baseComplexity * multipliers[contributionType]);
|
|
3459
|
+
}
|
|
3414
3460
|
/**
|
|
3415
3461
|
* Fetch issues with optional filtering
|
|
3416
3462
|
*/
|
|
@@ -3424,6 +3470,7 @@ var JiraService = class {
|
|
|
3424
3470
|
"updated",
|
|
3425
3471
|
"resolutiondate",
|
|
3426
3472
|
"creator",
|
|
3473
|
+
"assignee",
|
|
3427
3474
|
"status",
|
|
3428
3475
|
"issuetype"
|
|
3429
3476
|
];
|
|
@@ -3441,7 +3488,7 @@ var JiraService = class {
|
|
|
3441
3488
|
);
|
|
3442
3489
|
break;
|
|
3443
3490
|
}
|
|
3444
|
-
const endpoint = `/rest/api/2/search?jql=${encodeURIComponent(jql)}&startAt=${startAt}&maxResults=${maxResults}&fields=${fields.join(",")}`;
|
|
3491
|
+
const endpoint = `/rest/api/2/search?jql=${encodeURIComponent(jql)}&startAt=${startAt}&maxResults=${maxResults}&fields=${fields.join(",")}&expand=changelog`;
|
|
3445
3492
|
try {
|
|
3446
3493
|
const response = await this.request(endpoint);
|
|
3447
3494
|
if (response.issues.length === 0) {
|
|
@@ -3467,7 +3514,14 @@ var JiraService = class {
|
|
|
3467
3514
|
break;
|
|
3468
3515
|
}
|
|
3469
3516
|
if (options.limit && allIssues.length >= options.limit) {
|
|
3470
|
-
|
|
3517
|
+
const email2 = await this.getCurrentUser();
|
|
3518
|
+
const limitedIssues = allIssues.slice(0, options.limit);
|
|
3519
|
+
const commits2 = [];
|
|
3520
|
+
for (const issue of limitedIssues) {
|
|
3521
|
+
const commit = await this.transformIssueToCommit(issue, void 0, email2);
|
|
3522
|
+
commits2.push(commit);
|
|
3523
|
+
}
|
|
3524
|
+
return commits2;
|
|
3471
3525
|
}
|
|
3472
3526
|
startAt += maxResults;
|
|
3473
3527
|
} catch (error) {
|
|
@@ -3491,7 +3545,13 @@ var JiraService = class {
|
|
|
3491
3545
|
throw error;
|
|
3492
3546
|
}
|
|
3493
3547
|
}
|
|
3494
|
-
|
|
3548
|
+
const email = await this.getCurrentUser();
|
|
3549
|
+
const commits = [];
|
|
3550
|
+
for (const issue of allIssues) {
|
|
3551
|
+
const commit = await this.transformIssueToCommit(issue, void 0, email);
|
|
3552
|
+
commits.push(commit);
|
|
3553
|
+
}
|
|
3554
|
+
return commits;
|
|
3495
3555
|
}
|
|
3496
3556
|
/**
|
|
3497
3557
|
* Fetch issues for the current authenticated user
|
|
@@ -3507,17 +3567,51 @@ var JiraService = class {
|
|
|
3507
3567
|
});
|
|
3508
3568
|
}
|
|
3509
3569
|
/**
|
|
3510
|
-
* Transform Jira issue to GitCommit format with
|
|
3570
|
+
* Transform Jira issue to GitCommit format with contribution-specific data
|
|
3511
3571
|
*/
|
|
3512
|
-
transformIssueToCommit(issue, instanceUrl) {
|
|
3513
|
-
let
|
|
3514
|
-
if (
|
|
3515
|
-
|
|
3516
|
-
|
|
3572
|
+
async transformIssueToCommit(issue, instanceUrl, userEmail) {
|
|
3573
|
+
let contribution = null;
|
|
3574
|
+
if (userEmail) {
|
|
3575
|
+
contribution = await this.determineJiraContributionType(issue, userEmail);
|
|
3576
|
+
}
|
|
3577
|
+
let message;
|
|
3578
|
+
let contributionPrefix = "";
|
|
3579
|
+
if (contribution) {
|
|
3580
|
+
if (contribution.type === "created") {
|
|
3581
|
+
contributionPrefix = "\u{1F3AB} Created: ";
|
|
3582
|
+
} else if (contribution.type === "assigned-resolved") {
|
|
3583
|
+
contributionPrefix = "\u2705 Resolved: ";
|
|
3584
|
+
} else {
|
|
3585
|
+
contributionPrefix = "\u270F\uFE0F Updated: ";
|
|
3586
|
+
}
|
|
3587
|
+
message = `${contributionPrefix}${issue.fields.summary}
|
|
3588
|
+
|
|
3589
|
+
${contribution.details}`;
|
|
3590
|
+
if (contribution.type === "created" && issue.fields.description) {
|
|
3591
|
+
const truncatedDesc = issue.fields.description.substring(0, this.MAX_DESCRIPTION_LENGTH);
|
|
3592
|
+
message = `${contributionPrefix}${issue.fields.summary}
|
|
3593
|
+
|
|
3594
|
+
${truncatedDesc}
|
|
3595
|
+
|
|
3596
|
+
${contribution.details}`;
|
|
3597
|
+
}
|
|
3598
|
+
} else {
|
|
3599
|
+
message = issue.fields.summary;
|
|
3600
|
+
if (issue.fields.description) {
|
|
3601
|
+
const truncatedDesc = issue.fields.description.substring(0, this.MAX_DESCRIPTION_LENGTH);
|
|
3602
|
+
message = `${issue.fields.summary}
|
|
3517
3603
|
|
|
3518
3604
|
${truncatedDesc}`;
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
let date;
|
|
3608
|
+
if (contribution?.type === "created") {
|
|
3609
|
+
date = issue.fields.created;
|
|
3610
|
+
} else if (contribution?.type === "assigned-resolved" && issue.fields.resolutiondate) {
|
|
3611
|
+
date = issue.fields.resolutiondate;
|
|
3612
|
+
} else {
|
|
3613
|
+
date = issue.fields.updated;
|
|
3519
3614
|
}
|
|
3520
|
-
const date = issue.fields.resolutiondate || issue.fields.updated;
|
|
3521
3615
|
let baseUrl = "https://jira.atlassian.net";
|
|
3522
3616
|
if (instanceUrl) {
|
|
3523
3617
|
baseUrl = instanceUrl.startsWith("http") ? instanceUrl : `https://${instanceUrl}`;
|
|
@@ -3531,6 +3625,8 @@ ${truncatedDesc}`;
|
|
|
3531
3625
|
}
|
|
3532
3626
|
}
|
|
3533
3627
|
const url = `${baseUrl}/browse/${issue.key}`;
|
|
3628
|
+
const baseComplexity = this.estimateComplexity(issue);
|
|
3629
|
+
const impactScore = contribution ? this.calculateJiraContributionImpact(contribution.type, baseComplexity) : baseComplexity;
|
|
3534
3630
|
return {
|
|
3535
3631
|
sha: issue.key,
|
|
3536
3632
|
message,
|
|
@@ -3540,11 +3636,11 @@ ${truncatedDesc}`;
|
|
|
3540
3636
|
url,
|
|
3541
3637
|
diffStats: {
|
|
3542
3638
|
filesChanged: 0,
|
|
3543
|
-
insertions:
|
|
3639
|
+
insertions: impactScore,
|
|
3544
3640
|
deletions: 0
|
|
3545
3641
|
},
|
|
3546
3642
|
externalId: issue.key,
|
|
3547
|
-
externalType: "issue",
|
|
3643
|
+
externalType: contribution?.type || "issue",
|
|
3548
3644
|
externalSource: "jira",
|
|
3549
3645
|
externalUrl: url
|
|
3550
3646
|
};
|
|
@@ -3736,6 +3832,59 @@ var ConfluenceService = class {
|
|
|
3736
3832
|
const content = page.body?.storage?.value || "";
|
|
3737
3833
|
return Math.ceil(content.length / 80);
|
|
3738
3834
|
}
|
|
3835
|
+
/**
|
|
3836
|
+
* Determine the type of contribution the current user made to a page
|
|
3837
|
+
* Returns: 'created' | 'edited' | 'commented'
|
|
3838
|
+
*/
|
|
3839
|
+
async determineContributionType(page, userEmail) {
|
|
3840
|
+
if (page.history?.createdBy?.email === userEmail) {
|
|
3841
|
+
return {
|
|
3842
|
+
type: "created",
|
|
3843
|
+
details: `Created page with ${page.version.number} version${page.version.number > 1 ? "s" : ""}`
|
|
3844
|
+
};
|
|
3845
|
+
}
|
|
3846
|
+
const hasEdits = page.version.by.email === userEmail && page.version.number > 1;
|
|
3847
|
+
const userComments = page.children?.comment?.results?.filter(
|
|
3848
|
+
(comment) => comment.version.by.email === userEmail
|
|
3849
|
+
) || [];
|
|
3850
|
+
const hasComments = userComments.length > 0;
|
|
3851
|
+
if (hasEdits && hasComments) {
|
|
3852
|
+
return {
|
|
3853
|
+
type: "edited",
|
|
3854
|
+
details: `Edited page (v${page.version.number}) and added ${userComments.length} comment${userComments.length > 1 ? "s" : ""}`
|
|
3855
|
+
};
|
|
3856
|
+
}
|
|
3857
|
+
if (hasEdits) {
|
|
3858
|
+
return {
|
|
3859
|
+
type: "edited",
|
|
3860
|
+
details: `Edited page to version ${page.version.number}`
|
|
3861
|
+
};
|
|
3862
|
+
}
|
|
3863
|
+
if (hasComments) {
|
|
3864
|
+
return {
|
|
3865
|
+
type: "commented",
|
|
3866
|
+
details: `Added ${userComments.length} comment${userComments.length > 1 ? "s" : ""}`
|
|
3867
|
+
};
|
|
3868
|
+
}
|
|
3869
|
+
return {
|
|
3870
|
+
type: "edited",
|
|
3871
|
+
details: "Contributed to page"
|
|
3872
|
+
};
|
|
3873
|
+
}
|
|
3874
|
+
/**
|
|
3875
|
+
* Calculate impact score based on contribution type
|
|
3876
|
+
*/
|
|
3877
|
+
calculateContributionImpact(contributionType, baseSize) {
|
|
3878
|
+
const multipliers = {
|
|
3879
|
+
created: 1,
|
|
3880
|
+
// 100% - highest impact
|
|
3881
|
+
edited: 0.6,
|
|
3882
|
+
// 60% - medium impact
|
|
3883
|
+
commented: 0.3
|
|
3884
|
+
// 30% - lowest impact
|
|
3885
|
+
};
|
|
3886
|
+
return Math.ceil(baseSize * multipliers[contributionType]);
|
|
3887
|
+
}
|
|
3739
3888
|
/**
|
|
3740
3889
|
* Fetch pages with optional filtering
|
|
3741
3890
|
*/
|
|
@@ -3765,7 +3914,7 @@ var ConfluenceService = class {
|
|
|
3765
3914
|
cql,
|
|
3766
3915
|
start: start.toString(),
|
|
3767
3916
|
limit: limit.toString(),
|
|
3768
|
-
expand: "version,body.storage"
|
|
3917
|
+
expand: "version,history,children.comment,body.storage"
|
|
3769
3918
|
};
|
|
3770
3919
|
const queryString = Object.entries(params).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
|
|
3771
3920
|
endpoint = `/wiki/rest/api/content/search?${queryString}`;
|
|
@@ -3776,7 +3925,7 @@ var ConfluenceService = class {
|
|
|
3776
3925
|
status: "current",
|
|
3777
3926
|
start: start.toString(),
|
|
3778
3927
|
limit: limit.toString(),
|
|
3779
|
-
expand: "version,body.storage"
|
|
3928
|
+
expand: "version,history,children.comment,body.storage"
|
|
3780
3929
|
};
|
|
3781
3930
|
const queryString = Object.entries(params).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
|
|
3782
3931
|
endpoint = `/wiki/rest/api/content?${queryString}`;
|
|
@@ -3830,7 +3979,13 @@ var ConfluenceService = class {
|
|
|
3830
3979
|
throw error;
|
|
3831
3980
|
}
|
|
3832
3981
|
}
|
|
3833
|
-
|
|
3982
|
+
const email = await this.getCurrentUser();
|
|
3983
|
+
const commits = [];
|
|
3984
|
+
for (const page of allPages) {
|
|
3985
|
+
const commit = await this.transformPageToCommit(page, void 0, email);
|
|
3986
|
+
commits.push(commit);
|
|
3987
|
+
}
|
|
3988
|
+
return commits;
|
|
3834
3989
|
}
|
|
3835
3990
|
/**
|
|
3836
3991
|
* Fetch pages for the current authenticated user (created or contributed to)
|
|
@@ -3846,12 +4001,32 @@ var ConfluenceService = class {
|
|
|
3846
4001
|
});
|
|
3847
4002
|
}
|
|
3848
4003
|
/**
|
|
3849
|
-
* Transform Confluence page to GitCommit format with
|
|
4004
|
+
* Transform Confluence page to GitCommit format with contribution-specific data
|
|
3850
4005
|
*/
|
|
3851
|
-
transformPageToCommit(page, instanceUrl) {
|
|
3852
|
-
|
|
4006
|
+
async transformPageToCommit(page, instanceUrl, userEmail) {
|
|
4007
|
+
let contribution = null;
|
|
4008
|
+
if (userEmail) {
|
|
4009
|
+
contribution = await this.determineContributionType(page, userEmail);
|
|
4010
|
+
}
|
|
4011
|
+
let message;
|
|
4012
|
+
let contributionPrefix = "";
|
|
4013
|
+
if (contribution) {
|
|
4014
|
+
if (contribution.type === "created") {
|
|
4015
|
+
contributionPrefix = "\u{1F4DD} Created: ";
|
|
4016
|
+
} else if (contribution.type === "edited") {
|
|
4017
|
+
contributionPrefix = "\u270F\uFE0F Edited: ";
|
|
4018
|
+
} else {
|
|
4019
|
+
contributionPrefix = "\u{1F4AC} Commented on: ";
|
|
4020
|
+
}
|
|
4021
|
+
message = `${contributionPrefix}${page.title}
|
|
3853
4022
|
|
|
4023
|
+
${contribution.details}
|
|
3854
4024
|
[Confluence Page v${page.version.number}]`;
|
|
4025
|
+
} else {
|
|
4026
|
+
message = `${page.title}
|
|
4027
|
+
|
|
4028
|
+
[Confluence Page v${page.version.number}]`;
|
|
4029
|
+
}
|
|
3855
4030
|
let baseUrl = "https://confluence.atlassian.net";
|
|
3856
4031
|
if (instanceUrl) {
|
|
3857
4032
|
baseUrl = instanceUrl.startsWith("http") ? instanceUrl : `https://${instanceUrl}`;
|
|
@@ -3865,20 +4040,25 @@ var ConfluenceService = class {
|
|
|
3865
4040
|
}
|
|
3866
4041
|
}
|
|
3867
4042
|
const url = `${baseUrl}/wiki${page._links.webui}`;
|
|
4043
|
+
const baseSize = this.estimatePageSize(page);
|
|
4044
|
+
const impactScore = contribution ? this.calculateContributionImpact(contribution.type, baseSize) : baseSize;
|
|
4045
|
+
const author = page.history?.createdBy?.displayName || page.version.by.displayName;
|
|
4046
|
+
const authorEmail = page.history?.createdBy?.email || page.version.by.email;
|
|
4047
|
+
const date = contribution?.type === "created" ? page.history?.createdDate || page.version.when : page.version.when;
|
|
3868
4048
|
return {
|
|
3869
4049
|
sha: page.id,
|
|
3870
4050
|
message,
|
|
3871
|
-
author
|
|
3872
|
-
authorEmail
|
|
3873
|
-
date
|
|
4051
|
+
author,
|
|
4052
|
+
authorEmail,
|
|
4053
|
+
date,
|
|
3874
4054
|
url,
|
|
3875
4055
|
diffStats: {
|
|
3876
4056
|
filesChanged: 1,
|
|
3877
|
-
insertions:
|
|
4057
|
+
insertions: impactScore,
|
|
3878
4058
|
deletions: 0
|
|
3879
4059
|
},
|
|
3880
4060
|
externalId: page.id,
|
|
3881
|
-
externalType: "page",
|
|
4061
|
+
externalType: contribution?.type || "page",
|
|
3882
4062
|
externalSource: "confluence",
|
|
3883
4063
|
externalUrl: url
|
|
3884
4064
|
};
|