@bragduck/cli 2.10.2 → 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
  };
@@ -3712,23 +3808,22 @@ var ConfluenceService = class {
3712
3808
  }
3713
3809
  const queries = [];
3714
3810
  queries.push("type = page");
3715
- queries.push("status = current");
3716
3811
  if (options.days) {
3717
- queries.push(`lastModified >= -${options.days}d`);
3812
+ queries.push(`lastmodified > startOfDay("-${options.days}d")`);
3718
3813
  }
3719
3814
  if (options.author) {
3720
3815
  if (options.author.includes("@")) {
3721
- queries.push("(creator = currentUser() OR contributor = currentUser())");
3816
+ queries.push("(creator = currentUser() or contributor = currentUser())");
3722
3817
  } else {
3723
- queries.push(`(creator = "${options.author}" OR contributor = "${options.author}")`);
3818
+ queries.push(`(creator = "${options.author}" or contributor = "${options.author}")`);
3724
3819
  }
3725
3820
  } else {
3726
- queries.push("(creator = currentUser() OR contributor = currentUser())");
3821
+ queries.push("(creator = currentUser() or contributor = currentUser())");
3727
3822
  }
3728
3823
  if (options.cql) {
3729
3824
  queries.push(`(${options.cql})`);
3730
3825
  }
3731
- return queries.join(" AND ");
3826
+ return queries.join(" and ");
3732
3827
  }
3733
3828
  /**
3734
3829
  * Estimate page size for impact scoring
@@ -3737,6 +3832,59 @@ var ConfluenceService = class {
3737
3832
  const content = page.body?.storage?.value || "";
3738
3833
  return Math.ceil(content.length / 80);
3739
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
+ }
3740
3888
  /**
3741
3889
  * Fetch pages with optional filtering
3742
3890
  */
@@ -3766,7 +3914,7 @@ var ConfluenceService = class {
3766
3914
  cql,
3767
3915
  start: start.toString(),
3768
3916
  limit: limit.toString(),
3769
- expand: "version,body.storage"
3917
+ expand: "version,history,children.comment,body.storage"
3770
3918
  };
3771
3919
  const queryString = Object.entries(params).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
3772
3920
  endpoint = `/wiki/rest/api/content/search?${queryString}`;
@@ -3777,7 +3925,7 @@ var ConfluenceService = class {
3777
3925
  status: "current",
3778
3926
  start: start.toString(),
3779
3927
  limit: limit.toString(),
3780
- expand: "version,body.storage"
3928
+ expand: "version,history,children.comment,body.storage"
3781
3929
  };
3782
3930
  const queryString = Object.entries(params).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
3783
3931
  endpoint = `/wiki/rest/api/content?${queryString}`;
@@ -3831,7 +3979,13 @@ var ConfluenceService = class {
3831
3979
  throw error;
3832
3980
  }
3833
3981
  }
3834
- 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;
3835
3989
  }
3836
3990
  /**
3837
3991
  * Fetch pages for the current authenticated user (created or contributed to)
@@ -3847,12 +4001,32 @@ var ConfluenceService = class {
3847
4001
  });
3848
4002
  }
3849
4003
  /**
3850
- * Transform Confluence page to GitCommit format with external fields
4004
+ * Transform Confluence page to GitCommit format with contribution-specific data
3851
4005
  */
3852
- transformPageToCommit(page, instanceUrl) {
3853
- 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}
3854
4022
 
4023
+ ${contribution.details}
3855
4024
  [Confluence Page v${page.version.number}]`;
4025
+ } else {
4026
+ message = `${page.title}
4027
+
4028
+ [Confluence Page v${page.version.number}]`;
4029
+ }
3856
4030
  let baseUrl = "https://confluence.atlassian.net";
3857
4031
  if (instanceUrl) {
3858
4032
  baseUrl = instanceUrl.startsWith("http") ? instanceUrl : `https://${instanceUrl}`;
@@ -3866,20 +4040,25 @@ var ConfluenceService = class {
3866
4040
  }
3867
4041
  }
3868
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;
3869
4048
  return {
3870
4049
  sha: page.id,
3871
4050
  message,
3872
- author: page.version.by.displayName,
3873
- authorEmail: page.version.by.email,
3874
- date: page.version.when,
4051
+ author,
4052
+ authorEmail,
4053
+ date,
3875
4054
  url,
3876
4055
  diffStats: {
3877
4056
  filesChanged: 1,
3878
- insertions: this.estimatePageSize(page),
4057
+ insertions: impactScore,
3879
4058
  deletions: 0
3880
4059
  },
3881
4060
  externalId: page.id,
3882
- externalType: "page",
4061
+ externalType: contribution?.type || "page",
3883
4062
  externalSource: "confluence",
3884
4063
  externalUrl: url
3885
4064
  };