@bragduck/cli 2.28.0 → 2.28.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.
@@ -3653,33 +3653,6 @@ var JiraService = class {
3653
3653
  return null;
3654
3654
  }
3655
3655
  }
3656
- /**
3657
- * Check if a user object matches the given identifier (accountId, email, or username)
3658
- */
3659
- isMatchingUser(candidate, userIdentifier) {
3660
- if (!candidate) return false;
3661
- return candidate.email === userIdentifier || candidate.emailAddress === userIdentifier || candidate.accountId === userIdentifier || candidate.username === userIdentifier || candidate.name === userIdentifier;
3662
- }
3663
- /**
3664
- * Filter issues to only those where the user made contributions within the date range.
3665
- * Excludes issues where the user's only involvement is a static role (creator/assignee)
3666
- * with a date outside the scan period.
3667
- */
3668
- filterIssuesByUserContribution(issues, userIdentifier, sinceDate) {
3669
- const results = [];
3670
- for (const issue of issues) {
3671
- const userChanges = (issue.changelog?.histories || []).filter(
3672
- (h) => this.isMatchingUser(h.author, userIdentifier) && new Date(h.created) >= sinceDate
3673
- );
3674
- const isCreatorInRange = this.isMatchingUser(issue.fields.creator, userIdentifier) && new Date(issue.fields.created) >= sinceDate;
3675
- if (userChanges.length > 0 || isCreatorInRange) {
3676
- results.push({ issue, userChanges });
3677
- } else {
3678
- logger.debug(`Excluding issue ${issue.key} - no user contributions in date range`);
3679
- }
3680
- }
3681
- return results;
3682
- }
3683
3656
  /**
3684
3657
  * Build JQL query from options
3685
3658
  */
@@ -3737,7 +3710,7 @@ var JiraService = class {
3737
3710
  }
3738
3711
  const isAssigned = issue.fields.assignee?.emailAddress === userEmail;
3739
3712
  const isResolved = issue.fields.resolutiondate !== null && issue.fields.resolutiondate !== void 0;
3740
- const userEdits = issue.changelog?.histories?.filter((history) => history.author?.emailAddress === userEmail) || [];
3713
+ const userEdits = issue.changelog?.histories?.filter((history) => history.author.emailAddress === userEmail) || [];
3741
3714
  const hasEdits = userEdits.length > 0;
3742
3715
  if (isAssigned && isResolved) {
3743
3716
  return {
@@ -3772,77 +3745,6 @@ var JiraService = class {
3772
3745
  };
3773
3746
  return Math.ceil(baseComplexity * multipliers[contributionType]);
3774
3747
  }
3775
- /**
3776
- * Summarize the user's specific changes from changelog entries into human-readable lines.
3777
- * This enriches the brag message so the AI refinement can generate a more specific brag.
3778
- */
3779
- summarizeUserChanges(userChanges) {
3780
- const MAX_LINES = 10;
3781
- const allItems = [];
3782
- for (const entry of userChanges) {
3783
- for (const item of entry.items) {
3784
- allItems.push(item);
3785
- }
3786
- }
3787
- if (allItems.length === 0) return "";
3788
- const latestByField = /* @__PURE__ */ new Map();
3789
- for (const item of allItems) {
3790
- latestByField.set(item.field, { fromString: item.fromString, toString: item.toString });
3791
- }
3792
- const lines = [];
3793
- for (const [field, change] of latestByField) {
3794
- if (lines.length >= MAX_LINES) break;
3795
- const from = change.fromString || "";
3796
- const to = change.toString || "";
3797
- switch (field.toLowerCase()) {
3798
- case "status":
3799
- lines.push(from ? `Moved status from '${from}' to '${to}'` : `Set status to '${to}'`);
3800
- break;
3801
- case "resolution":
3802
- lines.push(to ? `Resolved as '${to}'` : "Reopened issue");
3803
- break;
3804
- case "assignee":
3805
- lines.push(to ? `Assigned to ${to}` : "Unassigned");
3806
- break;
3807
- case "priority":
3808
- lines.push(
3809
- from ? `Changed priority from '${from}' to '${to}'` : `Set priority to '${to}'`
3810
- );
3811
- break;
3812
- case "summary":
3813
- lines.push("Updated issue title");
3814
- break;
3815
- case "description":
3816
- lines.push("Updated description");
3817
- break;
3818
- case "comment":
3819
- lines.push("Added comment");
3820
- break;
3821
- case "labels":
3822
- lines.push(to ? `Updated labels: ${to}` : "Removed labels");
3823
- break;
3824
- case "fix version":
3825
- case "fixversions":
3826
- lines.push(to ? `Set fix version: ${to}` : "Removed fix version");
3827
- break;
3828
- case "sprint":
3829
- lines.push(to ? `Moved to sprint: ${to}` : "Removed from sprint");
3830
- break;
3831
- case "story points":
3832
- case "story point estimate":
3833
- lines.push(`Set story points to ${to}`);
3834
- break;
3835
- default:
3836
- if (to) {
3837
- lines.push(`Updated ${field}`);
3838
- }
3839
- break;
3840
- }
3841
- }
3842
- if (lines.length === 0) return "";
3843
- return `User changes:
3844
- ${lines.map((l) => `- ${l}`).join("\n")}`;
3845
- }
3846
3748
  /**
3847
3749
  * Fetch issues with optional filtering
3848
3750
  */
@@ -3880,7 +3782,7 @@ ${lines.map((l) => `- ${l}`).join("\n")}`;
3880
3782
  );
3881
3783
  break;
3882
3784
  }
3883
- const endpoint = `/rest/api/3/search/jql?jql=${encodeURIComponent(jql)}&startAt=${startAt}&maxResults=${maxResults}&fields=${fields.join(",")}&expand=changelog`;
3785
+ const endpoint = `/rest/api/3/search/jql?jql=${encodeURIComponent(jql)}&startAt=${startAt}&maxResults=${maxResults}&fields=${fields.join(",")}`;
3884
3786
  try {
3885
3787
  const response = await this.request(endpoint);
3886
3788
  if (response.issues.length === 0) {
@@ -3906,7 +3808,14 @@ ${lines.map((l) => `- ${l}`).join("\n")}`;
3906
3808
  break;
3907
3809
  }
3908
3810
  if (options.limit && allIssues.length >= options.limit) {
3909
- break;
3811
+ const email2 = await this.getCurrentUser();
3812
+ const limitedIssues = allIssues.slice(0, options.limit);
3813
+ const commits2 = [];
3814
+ for (const issue of limitedIssues) {
3815
+ const commit = await this.transformIssueToCommit(issue, void 0, email2 || void 0);
3816
+ commits2.push(commit);
3817
+ }
3818
+ return commits2;
3910
3819
  }
3911
3820
  startAt += maxResults;
3912
3821
  } catch (error) {
@@ -3930,23 +3839,9 @@ ${lines.map((l) => `- ${l}`).join("\n")}`;
3930
3839
  throw error;
3931
3840
  }
3932
3841
  }
3933
- const issuesToProcess = options.limit ? allIssues.slice(0, options.limit) : allIssues;
3934
3842
  const email = await this.getCurrentUser();
3935
- const sinceDate = options.days ? new Date(Date.now() - options.days * 24 * 60 * 60 * 1e3) : void 0;
3936
- if (sinceDate && email) {
3937
- const filtered = this.filterIssuesByUserContribution(issuesToProcess, email, sinceDate);
3938
- logger.debug(
3939
- `Date-scoped filtering: ${issuesToProcess.length} issues -> ${filtered.length} with user contributions in range`
3940
- );
3941
- const commits2 = [];
3942
- for (const { issue, userChanges } of filtered) {
3943
- const commit = await this.transformIssueToCommit(issue, void 0, email, userChanges);
3944
- commits2.push(commit);
3945
- }
3946
- return commits2;
3947
- }
3948
3843
  const commits = [];
3949
- for (const issue of issuesToProcess) {
3844
+ for (const issue of allIssues) {
3950
3845
  const commit = await this.transformIssueToCommit(issue, void 0, email || void 0);
3951
3846
  commits.push(commit);
3952
3847
  }
@@ -3968,7 +3863,7 @@ ${lines.map((l) => `- ${l}`).join("\n")}`;
3968
3863
  /**
3969
3864
  * Transform Jira issue to GitCommit format with contribution-specific data
3970
3865
  */
3971
- async transformIssueToCommit(issue, instanceUrl, userEmail, userChanges) {
3866
+ async transformIssueToCommit(issue, instanceUrl, userEmail) {
3972
3867
  let contribution = null;
3973
3868
  if (userEmail) {
3974
3869
  contribution = await this.determineJiraContributionType(issue, userEmail);
@@ -4005,21 +3900,11 @@ ${contribution.details}`;
4005
3900
  ${truncatedDesc}`;
4006
3901
  }
4007
3902
  }
4008
- if (userChanges && userChanges.length > 0) {
4009
- const changeSummary = this.summarizeUserChanges(userChanges);
4010
- if (changeSummary) {
4011
- message += `
4012
-
4013
- ${changeSummary}`;
4014
- }
4015
- }
4016
3903
  let date;
4017
3904
  if (contribution?.type === "created" || contribution?.type === "reported") {
4018
3905
  date = issue.fields.created;
4019
3906
  } else if (contribution?.type === "assigned-resolved" && issue.fields.resolutiondate) {
4020
3907
  date = issue.fields.resolutiondate;
4021
- } else if (userChanges && userChanges.length > 0) {
4022
- date = userChanges[userChanges.length - 1].created;
4023
3908
  } else {
4024
3909
  date = issue.fields.updated;
4025
3910
  }
@@ -4209,60 +4094,6 @@ var ConfluenceService = class {
4209
4094
  return null;
4210
4095
  }
4211
4096
  }
4212
- /**
4213
- * Check if a user object matches the given identifier (accountId, email, or username)
4214
- */
4215
- isMatchingUser(candidate, userIdentifier) {
4216
- if (!candidate) return false;
4217
- return candidate.email === userIdentifier || candidate.emailAddress === userIdentifier || candidate.accountId === userIdentifier || candidate.username === userIdentifier || candidate.name === userIdentifier;
4218
- }
4219
- /**
4220
- * Fetch full version history for a page
4221
- */
4222
- async getPageVersionHistory(pageId) {
4223
- const allVersions = [];
4224
- let start = 0;
4225
- const limit = 50;
4226
- while (true) {
4227
- const response = await this.request(
4228
- `/wiki/rest/api/content/${pageId}/version?start=${start}&limit=${limit}`
4229
- );
4230
- allVersions.push(...response.results);
4231
- if (response.size < limit) break;
4232
- start += limit;
4233
- }
4234
- return allVersions;
4235
- }
4236
- /**
4237
- * Filter pages to only those where the user made contributions within the date range.
4238
- * Fetches version history per page and checks if the user has versions in range.
4239
- */
4240
- async filterPagesByUserContribution(pages, userIdentifier, sinceDate) {
4241
- const results = [];
4242
- for (let i = 0; i < pages.length; i++) {
4243
- const page = pages[i];
4244
- if (i > 0) {
4245
- await new Promise((resolve) => globalThis.setTimeout(resolve, 100));
4246
- }
4247
- try {
4248
- const versions = await this.getPageVersionHistory(page.id);
4249
- const userVersions = versions.filter(
4250
- (v) => this.isMatchingUser(v.by, userIdentifier) && new Date(v.when) >= sinceDate
4251
- );
4252
- const userCommentsInRange = page.children?.comment?.results?.filter(
4253
- (comment) => this.isMatchingUser(comment.version?.by || {}, userIdentifier) && new Date(comment.version?.when || 0) >= sinceDate
4254
- ) || [];
4255
- if (userVersions.length > 0 || userCommentsInRange.length > 0) {
4256
- results.push({ page, userVersions });
4257
- } else {
4258
- logger.debug(`Excluding page "${page.title}" - no user contributions in date range`);
4259
- }
4260
- } catch (error) {
4261
- logger.debug(`Skipping version history for page ${page.id}: ${error}`);
4262
- }
4263
- }
4264
- return results;
4265
- }
4266
4097
  /**
4267
4098
  * Build CQL query from options
4268
4099
  * Returns empty string if no filters need CQL (will use simple endpoint instead)
@@ -4298,31 +4129,28 @@ var ConfluenceService = class {
4298
4129
  * Determine the type of contribution the current user made to a page
4299
4130
  * Returns: 'created' | 'edited' | 'commented'
4300
4131
  */
4301
- async determineContributionType(page, userEmail, userVersions) {
4302
- const createdByUser = page.history?.createdBy ? this.isMatchingUser(page.history.createdBy, userEmail) : false;
4303
- if (createdByUser) {
4132
+ async determineContributionType(page, userEmail) {
4133
+ if (page.history?.createdBy?.email === userEmail) {
4304
4134
  return {
4305
4135
  type: "created",
4306
4136
  details: `Created page with ${page.version?.number || 1} version${(page.version?.number || 1) > 1 ? "s" : ""}`
4307
4137
  };
4308
4138
  }
4309
- const hasEdits = userVersions ? userVersions.some((v) => !v.minorEdit || v.number > 1) : page.version?.by ? this.isMatchingUser(page.version.by, userEmail) && (page.version?.number || 0) > 1 : false;
4139
+ const hasEdits = page.version?.by?.email === userEmail && (page.version?.number || 0) > 1;
4310
4140
  const userComments = page.children?.comment?.results?.filter(
4311
- (comment) => comment.version?.by ? this.isMatchingUser(comment.version.by, userEmail) : false
4141
+ (comment) => comment.version?.by?.email === userEmail
4312
4142
  ) || [];
4313
4143
  const hasComments = userComments.length > 0;
4314
4144
  if (hasEdits && hasComments) {
4315
- const editCount = userVersions?.length || 1;
4316
4145
  return {
4317
4146
  type: "edited",
4318
- details: `Edited page (${editCount} edit${editCount > 1 ? "s" : ""}) and added ${userComments.length} comment${userComments.length > 1 ? "s" : ""}`
4147
+ details: `Edited page (v${page.version?.number || 1}) and added ${userComments.length} comment${userComments.length > 1 ? "s" : ""}`
4319
4148
  };
4320
4149
  }
4321
4150
  if (hasEdits) {
4322
- const editCount = userVersions?.length || 1;
4323
4151
  return {
4324
4152
  type: "edited",
4325
- details: `Edited page (${editCount} edit${editCount > 1 ? "s" : ""})`
4153
+ details: `Edited page to version ${page.version?.number || 1}`
4326
4154
  };
4327
4155
  }
4328
4156
  if (hasComments) {
@@ -4350,31 +4178,6 @@ var ConfluenceService = class {
4350
4178
  };
4351
4179
  return Math.ceil(baseSize * multipliers[contributionType]);
4352
4180
  }
4353
- /**
4354
- * Summarize the user's version edits into human-readable lines.
4355
- * This enriches the brag message so the AI refinement can generate a more specific brag.
4356
- */
4357
- summarizeUserVersions(userVersions) {
4358
- const MAX_ENTRIES = 5;
4359
- const versionsWithMessages = userVersions.filter((v) => v.message && v.message.trim());
4360
- if (versionsWithMessages.length > 0) {
4361
- const lines = versionsWithMessages.slice(0, MAX_ENTRIES).map((v) => {
4362
- const suffix = v.minorEdit ? " (minor edit)" : "";
4363
- return `- v${v.number}: ${v.message.trim()}${suffix}`;
4364
- });
4365
- return `Edit notes:
4366
- ${lines.join("\n")}`;
4367
- }
4368
- if (userVersions.length > 0) {
4369
- const major = userVersions.filter((v) => !v.minorEdit).length;
4370
- const minor = userVersions.filter((v) => v.minorEdit).length;
4371
- const parts = [];
4372
- if (major > 0) parts.push(`${major} major`);
4373
- if (minor > 0) parts.push(`${minor} minor`);
4374
- return `Made ${userVersions.length} edit${userVersions.length > 1 ? "s" : ""} to this page (${parts.join(", ")})`;
4375
- }
4376
- return "";
4377
- }
4378
4181
  /**
4379
4182
  * Fetch pages with optional filtering
4380
4183
  */
@@ -4445,7 +4248,14 @@ ${lines.join("\n")}`;
4445
4248
  break;
4446
4249
  }
4447
4250
  if (options.limit && allPages.length >= options.limit) {
4448
- break;
4251
+ const email2 = await this.getCurrentUser();
4252
+ const limitedPages = allPages.slice(0, options.limit);
4253
+ const commits2 = [];
4254
+ for (const page of limitedPages) {
4255
+ const commit = await this.transformPageToCommit(page, void 0, email2 || void 0);
4256
+ commits2.push(commit);
4257
+ }
4258
+ return commits2;
4449
4259
  }
4450
4260
  start += limit;
4451
4261
  } catch (error) {
@@ -4469,23 +4279,9 @@ ${lines.join("\n")}`;
4469
4279
  throw error;
4470
4280
  }
4471
4281
  }
4472
- const pagesToProcess = options.limit ? allPages.slice(0, options.limit) : allPages;
4473
4282
  const email = await this.getCurrentUser();
4474
- const sinceDate = options.days ? new Date(Date.now() - options.days * 24 * 60 * 60 * 1e3) : void 0;
4475
- if (sinceDate && email) {
4476
- const filtered = await this.filterPagesByUserContribution(pagesToProcess, email, sinceDate);
4477
- logger.debug(
4478
- `Date-scoped filtering: ${pagesToProcess.length} pages -> ${filtered.length} with user contributions in range`
4479
- );
4480
- const commits2 = [];
4481
- for (const { page, userVersions } of filtered) {
4482
- const commit = await this.transformPageToCommit(page, void 0, email, userVersions);
4483
- commits2.push(commit);
4484
- }
4485
- return commits2;
4486
- }
4487
4283
  const commits = [];
4488
- for (const page of pagesToProcess) {
4284
+ for (const page of allPages) {
4489
4285
  const commit = await this.transformPageToCommit(page, void 0, email || void 0);
4490
4286
  commits.push(commit);
4491
4287
  }
@@ -4507,10 +4303,10 @@ ${lines.join("\n")}`;
4507
4303
  /**
4508
4304
  * Transform Confluence page to GitCommit format with contribution-specific data
4509
4305
  */
4510
- async transformPageToCommit(page, instanceUrl, userEmail, userVersions) {
4306
+ async transformPageToCommit(page, instanceUrl, userEmail) {
4511
4307
  let contribution = null;
4512
4308
  if (userEmail) {
4513
- contribution = await this.determineContributionType(page, userEmail, userVersions);
4309
+ contribution = await this.determineContributionType(page, userEmail);
4514
4310
  }
4515
4311
  let message;
4516
4312
  let contributionPrefix = "";
@@ -4531,14 +4327,6 @@ ${contribution.details}
4531
4327
 
4532
4328
  [Confluence Page v${page.version?.number || 1}]`;
4533
4329
  }
4534
- if (userVersions && userVersions.length > 0) {
4535
- const versionSummary = this.summarizeUserVersions(userVersions);
4536
- if (versionSummary) {
4537
- message += `
4538
-
4539
- ${versionSummary}`;
4540
- }
4541
- }
4542
4330
  let baseUrl = "https://confluence.atlassian.net";
4543
4331
  if (instanceUrl) {
4544
4332
  baseUrl = instanceUrl.startsWith("http") ? instanceUrl : `https://${instanceUrl}`;
@@ -4556,14 +4344,7 @@ ${versionSummary}`;
4556
4344
  const impactScore = contribution ? this.calculateContributionImpact(contribution.type, baseSize) : baseSize;
4557
4345
  const author = page.history?.createdBy?.displayName || page.version?.by?.displayName || "Unknown Author";
4558
4346
  const authorEmail = page.history?.createdBy?.email || page.version?.by?.email || "unknown@example.com";
4559
- let date;
4560
- if (contribution?.type === "created") {
4561
- date = page.history?.createdDate || page.version?.when;
4562
- } else if (userVersions && userVersions.length > 0) {
4563
- date = userVersions[userVersions.length - 1].when;
4564
- } else {
4565
- date = page.version?.when || (/* @__PURE__ */ new Date()).toISOString();
4566
- }
4347
+ const date = contribution?.type === "created" ? page.history?.createdDate || page.version?.when : page.version?.when || (/* @__PURE__ */ new Date()).toISOString();
4567
4348
  return {
4568
4349
  sha: page.id,
4569
4350
  message,