@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.
@@ -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
- return allIssues.slice(0, options.limit).map((issue) => this.transformIssueToCommit(issue));
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
- return allIssues.map((issue) => this.transformIssueToCommit(issue));
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 external fields
3570
+ * Transform Jira issue to GitCommit format with contribution-specific data
3511
3571
  */
3512
- transformIssueToCommit(issue, instanceUrl) {
3513
- let message = issue.fields.summary;
3514
- if (issue.fields.description) {
3515
- const truncatedDesc = issue.fields.description.substring(0, this.MAX_DESCRIPTION_LENGTH);
3516
- message = `${issue.fields.summary}
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: this.estimateComplexity(issue),
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
- return allPages.map((page) => this.transformPageToCommit(page));
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 external fields
4004
+ * Transform Confluence page to GitCommit format with contribution-specific data
3850
4005
  */
3851
- transformPageToCommit(page, instanceUrl) {
3852
- const message = `${page.title}
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: page.version.by.displayName,
3872
- authorEmail: page.version.by.email,
3873
- date: page.version.when,
4051
+ author,
4052
+ authorEmail,
4053
+ date,
3874
4054
  url,
3875
4055
  diffStats: {
3876
4056
  filesChanged: 1,
3877
- insertions: this.estimatePageSize(page),
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
  };