quackscore 0.2.5 → 0.4.0

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.
Files changed (137) hide show
  1. package/README.md +20 -1
  2. package/dist/commands/create.d.ts +1 -0
  3. package/dist/commands/create.d.ts.map +1 -1
  4. package/dist/commands/create.js +28 -12
  5. package/dist/commands/create.js.map +1 -1
  6. package/dist/commands/init.d.ts.map +1 -1
  7. package/dist/commands/init.js +19 -0
  8. package/dist/commands/init.js.map +1 -1
  9. package/dist/commands/leaderboard.d.ts +5 -1
  10. package/dist/commands/leaderboard.d.ts.map +1 -1
  11. package/dist/commands/leaderboard.js +57 -29
  12. package/dist/commands/leaderboard.js.map +1 -1
  13. package/dist/commands/mock-report.js +21 -21
  14. package/dist/commands/mock-report.js.map +1 -1
  15. package/dist/commands/profile-options.d.ts +24 -0
  16. package/dist/commands/profile-options.d.ts.map +1 -0
  17. package/dist/commands/profile-options.js +38 -0
  18. package/dist/commands/profile-options.js.map +1 -0
  19. package/dist/commands/remove.d.ts +5 -1
  20. package/dist/commands/remove.d.ts.map +1 -1
  21. package/dist/commands/remove.js +14 -5
  22. package/dist/commands/remove.js.map +1 -1
  23. package/dist/commands/show.d.ts +5 -1
  24. package/dist/commands/show.d.ts.map +1 -1
  25. package/dist/commands/show.js +12 -3
  26. package/dist/commands/show.js.map +1 -1
  27. package/dist/commands/stats.d.ts +7 -0
  28. package/dist/commands/stats.d.ts.map +1 -0
  29. package/dist/commands/stats.js +59 -0
  30. package/dist/commands/stats.js.map +1 -0
  31. package/dist/commands/update-summary.d.ts +2 -0
  32. package/dist/commands/update-summary.d.ts.map +1 -1
  33. package/dist/commands/update-summary.js +12 -3
  34. package/dist/commands/update-summary.js.map +1 -1
  35. package/dist/commands/update.d.ts.map +1 -1
  36. package/dist/commands/update.js +148 -68
  37. package/dist/commands/update.js.map +1 -1
  38. package/dist/config/types.d.ts +1 -1
  39. package/dist/config/types.d.ts.map +1 -1
  40. package/dist/config/types.js +14 -0
  41. package/dist/config/types.js.map +1 -1
  42. package/dist/github/client.d.ts +5 -5
  43. package/dist/github/client.d.ts.map +1 -1
  44. package/dist/github/client.js +157 -57
  45. package/dist/github/client.js.map +1 -1
  46. package/dist/github/index.d.ts +2 -0
  47. package/dist/github/index.d.ts.map +1 -1
  48. package/dist/github/index.js +1 -0
  49. package/dist/github/index.js.map +1 -1
  50. package/dist/github/repository-stats.d.ts +8 -0
  51. package/dist/github/repository-stats.d.ts.map +1 -0
  52. package/dist/github/repository-stats.js +96 -0
  53. package/dist/github/repository-stats.js.map +1 -0
  54. package/dist/index.js +48 -7
  55. package/dist/index.js.map +1 -1
  56. package/dist/llm/analyzer.d.ts +3 -3
  57. package/dist/llm/analyzer.d.ts.map +1 -1
  58. package/dist/llm/analyzer.js +5 -5
  59. package/dist/llm/analyzer.js.map +1 -1
  60. package/dist/llm/character.d.ts +2 -2
  61. package/dist/llm/character.d.ts.map +1 -1
  62. package/dist/llm/character.js +3 -3
  63. package/dist/llm/character.js.map +1 -1
  64. package/dist/llm/client.d.ts +2 -2
  65. package/dist/llm/client.d.ts.map +1 -1
  66. package/dist/llm/client.js +1 -1
  67. package/dist/llm/client.js.map +1 -1
  68. package/dist/llm/prompts.d.ts.map +1 -1
  69. package/dist/llm/prompts.js +13 -1
  70. package/dist/llm/prompts.js.map +1 -1
  71. package/dist/llm/providers.d.ts +3 -3
  72. package/dist/llm/providers.d.ts.map +1 -1
  73. package/dist/llm/providers.js +25 -6
  74. package/dist/llm/providers.js.map +1 -1
  75. package/dist/report/generate.d.ts.map +1 -1
  76. package/dist/report/generate.js +9 -7
  77. package/dist/report/generate.js.map +1 -1
  78. package/dist/report/index.d.ts +1 -0
  79. package/dist/report/index.d.ts.map +1 -1
  80. package/dist/report/index.js +1 -0
  81. package/dist/report/index.js.map +1 -1
  82. package/dist/report/leaderboard.d.ts +5 -1
  83. package/dist/report/leaderboard.d.ts.map +1 -1
  84. package/dist/report/leaderboard.js +611 -865
  85. package/dist/report/leaderboard.js.map +1 -1
  86. package/dist/report/repository-stats.d.ts +3 -0
  87. package/dist/report/repository-stats.d.ts.map +1 -0
  88. package/dist/report/repository-stats.js +650 -0
  89. package/dist/report/repository-stats.js.map +1 -0
  90. package/dist/scoring/index.d.ts +1 -0
  91. package/dist/scoring/index.d.ts.map +1 -1
  92. package/dist/scoring/index.js +1 -0
  93. package/dist/scoring/index.js.map +1 -1
  94. package/dist/scoring/points.d.ts.map +1 -1
  95. package/dist/scoring/points.js +5 -10
  96. package/dist/scoring/points.js.map +1 -1
  97. package/dist/scoring/repository-stats.d.ts +7 -0
  98. package/dist/scoring/repository-stats.d.ts.map +1 -0
  99. package/dist/scoring/repository-stats.js +120 -0
  100. package/dist/scoring/repository-stats.js.map +1 -0
  101. package/dist/shared/profile-scope.d.ts +19 -0
  102. package/dist/shared/profile-scope.d.ts.map +1 -0
  103. package/dist/shared/profile-scope.js +101 -0
  104. package/dist/shared/profile-scope.js.map +1 -0
  105. package/dist/shared/profile-summary.js +6 -6
  106. package/dist/shared/profile-summary.js.map +1 -1
  107. package/dist/shared/repository-stats.d.ts +51 -0
  108. package/dist/shared/repository-stats.d.ts.map +1 -0
  109. package/dist/shared/repository-stats.js +2 -0
  110. package/dist/shared/repository-stats.js.map +1 -0
  111. package/dist/shared/types.d.ts +29 -1
  112. package/dist/shared/types.d.ts.map +1 -1
  113. package/dist/shared/ui.d.ts +1 -0
  114. package/dist/shared/ui.d.ts.map +1 -1
  115. package/dist/shared/ui.js +3 -0
  116. package/dist/shared/ui.js.map +1 -1
  117. package/dist/storage/index.d.ts +2 -2
  118. package/dist/storage/index.d.ts.map +1 -1
  119. package/dist/storage/index.js +2 -2
  120. package/dist/storage/index.js.map +1 -1
  121. package/dist/storage/leaderboard.d.ts +3 -2
  122. package/dist/storage/leaderboard.d.ts.map +1 -1
  123. package/dist/storage/leaderboard.js +80 -9
  124. package/dist/storage/leaderboard.js.map +1 -1
  125. package/dist/storage/paths.d.ts +6 -0
  126. package/dist/storage/paths.d.ts.map +1 -1
  127. package/dist/storage/paths.js +21 -0
  128. package/dist/storage/paths.js.map +1 -1
  129. package/dist/storage/report.d.ts +5 -2
  130. package/dist/storage/report.d.ts.map +1 -1
  131. package/dist/storage/report.js +38 -8
  132. package/dist/storage/report.js.map +1 -1
  133. package/dist/storage/user.d.ts +2 -1
  134. package/dist/storage/user.d.ts.map +1 -1
  135. package/dist/storage/user.js +27 -10
  136. package/dist/storage/user.js.map +1 -1
  137. package/package.json +13 -6
@@ -0,0 +1,24 @@
1
+ import type { ProfileSelector } from '../shared/profile-scope.js';
2
+ import type { ProfileScope } from '../shared/types.js';
3
+ export interface ScopedProfileOptions {
4
+ org?: string;
5
+ repository?: string;
6
+ weeks?: number;
7
+ teams?: string[];
8
+ }
9
+ export interface ScopeTargetOptions {
10
+ org?: string;
11
+ repository?: string;
12
+ }
13
+ export interface ResolvedScopedProfileOptions {
14
+ org?: string;
15
+ repository?: string;
16
+ weeks?: number;
17
+ teams?: string[];
18
+ selector: ProfileSelector;
19
+ scope: ProfileScope;
20
+ }
21
+ export declare function weeksToSinceISO(weeks: number): string;
22
+ export declare function resolveScopeTarget(username: string, options?: ScopeTargetOptions): ProfileSelector;
23
+ export declare function resolveScopedProfileOptions(username: string, options: ScopedProfileOptions): ResolvedScopedProfileOptions;
24
+ //# sourceMappingURL=profile-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-options.d.ts","sourceRoot":"","sources":["../../src/commands/profile-options.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,oBAAoB;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,4BAA4B;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,eAAe,CAAC;IAC1B,KAAK,EAAE,YAAY,CAAC;CACrB;AAOD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,eAAe,CAYtG;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,4BAA4B,CAqBzH"}
@@ -0,0 +1,38 @@
1
+ import { normalizeTeams, profileSelector, scopeFromSelector } from '../shared/profile-scope.js';
2
+ function cleanOption(value) {
3
+ const trimmed = value?.trim();
4
+ return trimmed ? trimmed : undefined;
5
+ }
6
+ export function weeksToSinceISO(weeks) {
7
+ return new Date(Date.now() - weeks * 7 * 24 * 60 * 60 * 1000).toISOString();
8
+ }
9
+ export function resolveScopeTarget(username, options = {}) {
10
+ const org = cleanOption(options.org);
11
+ const repository = cleanOption(options.repository);
12
+ if (repository && !org) {
13
+ throw new Error('--repository requires --organisation.');
14
+ }
15
+ return profileSelector(username, {
16
+ organisation: org,
17
+ repository,
18
+ });
19
+ }
20
+ export function resolveScopedProfileOptions(username, options) {
21
+ if (options.weeks != null && (!Number.isInteger(options.weeks) || options.weeks <= 0)) {
22
+ throw new Error('--weeks must be a positive integer.');
23
+ }
24
+ const selector = resolveScopeTarget(username, options);
25
+ if ((options.teams ?? []).some((team) => team.trim().length === 0)) {
26
+ throw new Error('--team cannot be empty.');
27
+ }
28
+ const teams = options.teams == null ? undefined : normalizeTeams(options.teams);
29
+ return {
30
+ org: selector.organisation,
31
+ repository: selector.repository,
32
+ weeks: options.weeks,
33
+ teams,
34
+ selector,
35
+ scope: scopeFromSelector(selector, options.weeks),
36
+ };
37
+ }
38
+ //# sourceMappingURL=profile-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-options.js","sourceRoot":"","sources":["../../src/commands/profile-options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAyBhG,SAAS,WAAW,CAAC,KAAc;IACjC,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IAC9B,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,UAA8B,EAAE;IACnF,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAEnD,IAAI,UAAU,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,eAAe,CAAC,QAAQ,EAAE;QAC/B,YAAY,EAAE,GAAG;QACjB,UAAU;KACX,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,QAAgB,EAAE,OAA6B;IACzF,IAAI,OAAO,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC;QACtF,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAEvD,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAEhF,OAAO;QACL,GAAG,EAAE,QAAQ,CAAC,YAAY;QAC1B,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK;QACL,QAAQ;QACR,KAAK,EAAE,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC;KAClD,CAAC;AACJ,CAAC"}
@@ -1,2 +1,6 @@
1
- export declare function runRemove(username: string): Promise<void>;
1
+ export interface RemoveOptions {
2
+ org?: string;
3
+ repository?: string;
4
+ }
5
+ export declare function runRemove(username: string, options?: RemoveOptions): Promise<void>;
2
6
  //# sourceMappingURL=remove.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../src/commands/remove.ts"],"names":[],"mappings":"AAKA,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgC/D"}
1
+ {"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../src/commands/remove.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwC5F"}
@@ -1,12 +1,21 @@
1
1
  import fs from 'fs';
2
- import { userFilePath, userReportPath } from '../storage/paths.js';
2
+ import { profileFilePath, profileReportPath } from '../storage/paths.js';
3
3
  import { rpgHeader, rpgSuccess, rpgInfo, rpgError } from '../shared/ui.js';
4
4
  import { diagLog } from '../shared/diagnostics.js';
5
- export async function runRemove(username) {
6
- diagLog('Starting remove command', { username });
5
+ import { resolveScopeTarget } from './profile-options.js';
6
+ export async function runRemove(username, options = {}) {
7
+ let selector;
8
+ try {
9
+ selector = resolveScopeTarget(username, options);
10
+ }
11
+ catch (err) {
12
+ rpgError(err.message);
13
+ process.exit(1);
14
+ }
15
+ diagLog('Starting remove command', { username, selector });
7
16
  rpgHeader(`Quackscore Remove — ${username}`);
8
- const dataPath = userFilePath(username);
9
- const reportPath = userReportPath(username);
17
+ const dataPath = profileFilePath(selector);
18
+ const reportPath = profileReportPath(selector);
10
19
  let removed = 0;
11
20
  if (fs.existsSync(dataPath)) {
12
21
  fs.unlinkSync(dataPath);
@@ -1 +1 @@
1
- {"version":3,"file":"remove.js","sourceRoot":"","sources":["../../src/commands/remove.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB;IAC9C,OAAO,CAAC,yBAAyB,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IACjD,SAAS,CAAC,uBAAuB,QAAQ,EAAE,CAAC,CAAC;IAE7C,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC5C,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACxB,UAAU,CAAC,sBAAsB,QAAQ,EAAE,CAAC,CAAC;QAC7C,OAAO,CAAC,wBAAwB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACtD,OAAO,EAAE,CAAC;IACZ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,uBAAuB,QAAQ,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAC1B,UAAU,CAAC,wBAAwB,UAAU,EAAE,CAAC,CAAC;QACjD,OAAO,CAAC,0BAA0B,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;QAC1D,OAAO,EAAE,CAAC;IACZ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,yBAAyB,UAAU,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;QAClB,QAAQ,CAAC,uBAAuB,QAAQ,uBAAuB,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,WAAW,OAAO,iBAAiB,QAAQ,IAAI,CAAC,CAAC;AAC3D,CAAC"}
1
+ {"version":3,"file":"remove.js","sourceRoot":"","sources":["../../src/commands/remove.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAO1D,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB,EAAE,UAAyB,EAAE;IAC3E,IAAI,QAAQ,CAAC;IACb,IAAI,CAAC;QACH,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,QAAQ,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;QACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,yBAAyB,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3D,SAAS,CAAC,uBAAuB,QAAQ,EAAE,CAAC,CAAC;IAE7C,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACxB,UAAU,CAAC,sBAAsB,QAAQ,EAAE,CAAC,CAAC;QAC7C,OAAO,CAAC,wBAAwB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACtD,OAAO,EAAE,CAAC;IACZ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,uBAAuB,QAAQ,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAC1B,UAAU,CAAC,wBAAwB,UAAU,EAAE,CAAC,CAAC;QACjD,OAAO,CAAC,0BAA0B,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;QAC1D,OAAO,EAAE,CAAC;IACZ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,yBAAyB,UAAU,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;QAClB,QAAQ,CAAC,uBAAuB,QAAQ,uBAAuB,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,WAAW,OAAO,iBAAiB,QAAQ,IAAI,CAAC,CAAC;AAC3D,CAAC"}
@@ -1,2 +1,6 @@
1
- export declare function runShow(username: string): Promise<void>;
1
+ export interface ShowOptions {
2
+ org?: string;
3
+ repository?: string;
4
+ }
5
+ export declare function runShow(username: string, options?: ShowOptions): Promise<void>;
2
6
  //# sourceMappingURL=show.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"show.d.ts","sourceRoot":"","sources":["../../src/commands/show.ts"],"names":[],"mappings":"AAMA,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU7D"}
1
+ {"version":3,"file":"show.d.ts","sourceRoot":"","sources":["../../src/commands/show.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBxF"}
@@ -3,9 +3,18 @@ import { rpgError, rpgLog } from '../shared/ui.js';
3
3
  import { diagLog } from '../shared/diagnostics.js';
4
4
  import open from 'open';
5
5
  import fs from 'fs';
6
- export async function runShow(username) {
7
- const reportPath = getUserReportPath(username);
8
- diagLog('Resolved report path for show command', { username, reportPath });
6
+ import { resolveScopeTarget } from './profile-options.js';
7
+ export async function runShow(username, options = {}) {
8
+ let selector;
9
+ try {
10
+ selector = resolveScopeTarget(username, options);
11
+ }
12
+ catch (err) {
13
+ rpgError(err.message);
14
+ process.exit(1);
15
+ }
16
+ const reportPath = getUserReportPath(selector);
17
+ diagLog('Resolved report path for show command', { username, selector, reportPath });
9
18
  if (!fs.existsSync(reportPath)) {
10
19
  rpgError(`No report found for ${username}. Run 'quackscore create ${username}' first.`);
11
20
  process.exit(1);
@@ -1 +1 @@
1
- {"version":3,"file":"show.js","sourceRoot":"","sources":["../../src/commands/show.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,QAAgB;IAC5C,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,OAAO,CAAC,uCAAuC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IAC3E,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,QAAQ,CAAC,uBAAuB,QAAQ,4BAA4B,QAAQ,UAAU,CAAC,CAAC;QACxF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,CAAC,sBAAsB,QAAQ,KAAK,CAAC,CAAC;IAC5C,OAAO,CAAC,4CAA4C,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IAChF,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"show.js","sourceRoot":"","sources":["../../src/commands/show.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAO1D,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,QAAgB,EAAE,UAAuB,EAAE;IACvE,IAAI,QAAQ,CAAC;IACb,IAAI,CAAC;QACH,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,QAAQ,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;QACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,OAAO,CAAC,uCAAuC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IACrF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,QAAQ,CAAC,uBAAuB,QAAQ,4BAA4B,QAAQ,UAAU,CAAC,CAAC;QACxF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,CAAC,sBAAsB,QAAQ,KAAK,CAAC,CAAC;IAC5C,OAAO,CAAC,4CAA4C,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IAChF,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC;AACzB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface StatsOptions {
2
+ organisation?: string;
3
+ team?: string;
4
+ githubToken?: string;
5
+ }
6
+ export declare function runStats(repo: string, options: StatsOptions): Promise<void>;
7
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/commands/stats.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAwDjF"}
@@ -0,0 +1,59 @@
1
+ import open from 'open';
2
+ import { fetchRepositoryStatsPRs } from '../github/index.js';
3
+ import { aggregateRepositoryStats, formatDuration } from '../scoring/index.js';
4
+ import { generateRepositoryStatsHTML } from '../report/index.js';
5
+ import { saveRepositoryStatsReport } from '../storage/index.js';
6
+ import { rpgError, rpgHeader, rpgInfo, rpgLog, rpgSuccess } from '../shared/ui.js';
7
+ import { diagError, diagLog } from '../shared/diagnostics.js';
8
+ export async function runStats(repo, options) {
9
+ const org = options.organisation;
10
+ if (!org) {
11
+ rpgError('Organisation is required. Use: quackscore stats <repo> --organisation <org>');
12
+ process.exit(1);
13
+ }
14
+ const token = options.githubToken ?? process.env['QUACKSCORE_GH_TOKEN'];
15
+ if (!token) {
16
+ rpgError('GitHub token not set. Use --github-token or set QUACKSCORE_GH_TOKEN.');
17
+ process.exit(1);
18
+ }
19
+ diagLog('Starting repository stats command', {
20
+ org,
21
+ repo,
22
+ team: options.team,
23
+ githubTokenPresent: Boolean(token),
24
+ });
25
+ rpgHeader(`Quackscore Stats - ${org}/${repo}`);
26
+ if (options.team) {
27
+ rpgInfo(`Filtering to PRs authored by team "${options.team}"...`);
28
+ }
29
+ rpgLog('Fetching repository pull requests and review activity...');
30
+ let prs;
31
+ try {
32
+ prs = await fetchRepositoryStatsPRs(token, {
33
+ org,
34
+ repo,
35
+ team: options.team,
36
+ });
37
+ }
38
+ catch (err) {
39
+ diagError('Repository stats command failed while fetching GitHub data', err, {
40
+ org,
41
+ repo,
42
+ team: options.team,
43
+ });
44
+ rpgError(`Failed to fetch repository stats: ${err.message}`);
45
+ process.exit(1);
46
+ }
47
+ rpgSuccess(`Found ${prs.length} merged PR${prs.length === 1 ? '' : 's'}`);
48
+ rpgLog('Computing repository stats...');
49
+ const reportData = aggregateRepositoryStats(org, repo, prs, { team: options.team });
50
+ const html = generateRepositoryStatsHTML(reportData);
51
+ const reportPath = saveRepositoryStatsReport(org, repo, options.team, html);
52
+ rpgSuccess(`Repository stats saved: ${reportPath}`);
53
+ rpgInfo(`Average approval: ${formatDuration(reportData.summary.averageApprovalHours)}`);
54
+ rpgInfo(`Average merge: ${formatDuration(reportData.summary.averageMergeHours)}`);
55
+ rpgInfo(`Average comments: ${reportData.summary.averageComments.toFixed(1)}`);
56
+ rpgLog('Opening repository stats report...');
57
+ await open(reportPath);
58
+ }
59
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/commands/stats.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
1
  export interface UpdateSummaryOptions {
2
+ org?: string;
3
+ repository?: string;
2
4
  llmApiKey?: string;
3
5
  }
4
6
  export declare function runUpdateSummary(username: string, options?: UpdateSummaryOptions): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"update-summary.d.ts","sourceRoot":"","sources":["../../src/commands/update-summary.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiE1G"}
1
+ {"version":3,"file":"update-summary.d.ts","sourceRoot":"","sources":["../../src/commands/update-summary.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,oBAAoB;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyE1G"}
@@ -5,8 +5,17 @@ import { loadConfig } from '../config/index.js';
5
5
  import { rpgHeader, rpgLog, rpgSuccess, rpgError } from '../shared/ui.js';
6
6
  import { diagError, diagLog } from '../shared/diagnostics.js';
7
7
  import open from 'open';
8
+ import { resolveScopeTarget } from './profile-options.js';
8
9
  export async function runUpdateSummary(username, options = {}) {
9
- diagLog('Starting update-summary command', { username });
10
+ let selector;
11
+ try {
12
+ selector = resolveScopeTarget(username, options);
13
+ }
14
+ catch (err) {
15
+ rpgError(err.message);
16
+ process.exit(1);
17
+ }
18
+ diagLog('Starting update-summary command', { username, selector });
10
19
  let model;
11
20
  try {
12
21
  const config = loadConfig();
@@ -20,7 +29,7 @@ export async function runUpdateSummary(username, options = {}) {
20
29
  process.exit(1);
21
30
  }
22
31
  rpgHeader(`Quackscore Update Summary — ${username}`);
23
- const existing = loadUserData(username);
32
+ const existing = loadUserData(selector);
24
33
  if (!existing) {
25
34
  rpgError(`No existing profile found for "${username}". Run quackscore create first.`);
26
35
  process.exit(1);
@@ -52,7 +61,7 @@ export async function runUpdateSummary(username, options = {}) {
52
61
  rpgLog('Forging updated Quack-Card...');
53
62
  const reportStartedAt = Date.now();
54
63
  const html = generateHTML(userData);
55
- const reportPath = saveReport(username, html);
64
+ const reportPath = saveReport(selector, html);
56
65
  diagLog('Generated and saved report for update-summary', {
57
66
  durationMs: Date.now() - reportStartedAt,
58
67
  reportPath,
@@ -1 +1 @@
1
- {"version":3,"file":"update-summary.js","sourceRoot":"","sources":["../../src/commands/update-summary.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"update-summary.js","sourceRoot":"","sources":["../../src/commands/update-summary.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAmMvF"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAyBjD,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA+PvF"}
@@ -4,14 +4,49 @@ import { calculatePRPoints, aggregateStats } from '../scoring/index.js';
4
4
  import { loadUserData, saveUserData, saveReport } from '../storage/index.js';
5
5
  import { generateHTML } from '../report/index.js';
6
6
  import { loadConfig } from '../config/index.js';
7
+ import { mergeTeamDisplay, prIdentity, teamKey } from '../shared/profile-scope.js';
7
8
  import { rpgHeader, rpgLog, rpgInfo, rpgProgress, rpgSuccess, rpgError } from '../shared/ui.js';
8
9
  import { diagError, diagLog } from '../shared/diagnostics.js';
9
10
  import { setupEscCancel } from '../shared/cancel.js';
10
11
  import open from 'open';
12
+ import { resolveScopedProfileOptions, weeksToSinceISO } from './profile-options.js';
13
+ function sameTeamSet(left = [], right = []) {
14
+ return left.length === right.length && left.every((team, index) => teamKey(team) === teamKey(right[index] ?? ''));
15
+ }
16
+ function samePRSet(left, right) {
17
+ if (left.length !== right.length)
18
+ return false;
19
+ const rightKeys = new Set(right.map(prIdentity));
20
+ return left.every((pr) => rightKeys.has(prIdentity(pr)));
21
+ }
22
+ function mergeWithCachedAnalysis(freshPR, cachedPR) {
23
+ if (!cachedPR)
24
+ return freshPR;
25
+ return {
26
+ ...freshPR,
27
+ analysis: cachedPR.analysis,
28
+ points: cachedPR.points,
29
+ diff: freshPR.diff ?? cachedPR.diff,
30
+ rawFileStats: freshPR.rawFileStats ?? cachedPR.rawFileStats,
31
+ };
32
+ }
11
33
  export async function runUpdate(username, options) {
34
+ let resolvedOptions;
35
+ try {
36
+ resolvedOptions = resolveScopedProfileOptions(username, options);
37
+ }
38
+ catch (err) {
39
+ rpgError(err.message);
40
+ process.exit(1);
41
+ }
12
42
  diagLog('Starting update command', {
13
43
  username,
14
- options: { org: options.org, repository: options.repository },
44
+ options: {
45
+ org: resolvedOptions.org,
46
+ repository: resolvedOptions.repository,
47
+ weeks: resolvedOptions.weeks,
48
+ teams: resolvedOptions.teams,
49
+ },
15
50
  githubTokenPresent: Boolean(options.githubToken ?? process.env['QUACKSCORE_GH_TOKEN']),
16
51
  });
17
52
  const token = options.githubToken ?? process.env['QUACKSCORE_GH_TOKEN'];
@@ -32,41 +67,54 @@ export async function runUpdate(username, options) {
32
67
  process.exit(1);
33
68
  }
34
69
  rpgHeader(`Quackscore Update — ${username}`);
35
- const existing = loadUserData(username);
70
+ const existing = loadUserData(resolvedOptions.selector);
36
71
  if (!existing) {
37
- diagLog('No existing user data found during update; delegating to create', { username });
72
+ diagLog('No existing user data found during update; delegating to create', {
73
+ username,
74
+ selector: resolvedOptions.selector,
75
+ });
38
76
  rpgInfo('No existing data found. Running full analysis...');
39
77
  const { runCreate } = await import('./create.js');
40
78
  await runCreate(username, options);
41
79
  return;
42
80
  }
81
+ const effectiveWeeks = resolvedOptions.weeks ?? existing.scope?.windowWeeks;
82
+ const effectiveTeams = resolvedOptions.teams == null
83
+ ? existing.teams ?? []
84
+ : mergeTeamDisplay(existing.teams, resolvedOptions.teams);
43
85
  diagLog('Loaded existing user data for update', {
44
86
  username,
45
87
  lastUpdated: existing.lastUpdated,
46
88
  existingPRs: existing.prs.length,
47
89
  totalPoints: existing.stats.totalPoints,
48
90
  level: existing.stats.level,
91
+ effectiveWeeks,
92
+ effectiveTeams,
49
93
  });
50
94
  rpgLog(`Last updated: ${existing.lastUpdated}`);
51
- rpgInfo('Fetching new PR scrolls...');
52
- const since = options.weeks != null
53
- ? new Date(Date.now() - options.weeks * 7 * 24 * 60 * 60 * 1000).toISOString()
95
+ rpgInfo('Fetching PR scrolls...');
96
+ const since = effectiveWeeks != null
97
+ ? weeksToSinceISO(effectiveWeeks)
54
98
  : existing.lastUpdated;
55
- if (options.weeks != null) {
56
- rpgInfo(`Limiting to PRs from the last ${options.weeks} week${options.weeks === 1 ? '' : 's'} (since ${new Date(since).toLocaleDateString()})...`);
99
+ if (effectiveWeeks != null) {
100
+ rpgInfo(`Synchronizing the last ${effectiveWeeks} week${effectiveWeeks === 1 ? '' : 's'} of PRs (since ${new Date(since).toLocaleDateString()})...`);
57
101
  }
58
- let newPRs;
102
+ if (resolvedOptions.teams != null) {
103
+ const teamText = effectiveTeams.length > 0 ? effectiveTeams.join(', ') : 'none';
104
+ rpgInfo(`Updating team tags: ${teamText}`);
105
+ }
106
+ let fetchedPRs;
59
107
  try {
60
108
  const fetchStartedAt = Date.now();
61
- newPRs = await fetchUserPRs(username, token, {
62
- org: options.org,
63
- repo: options.repository,
109
+ fetchedPRs = await fetchUserPRs(username, token, {
110
+ org: resolvedOptions.org,
111
+ repo: resolvedOptions.repository,
64
112
  since,
65
113
  });
66
114
  diagLog('Fetched candidate PRs for update', {
67
115
  username,
68
116
  durationMs: Date.now() - fetchStartedAt,
69
- candidateCount: newPRs.length,
117
+ candidateCount: fetchedPRs.length,
70
118
  since,
71
119
  });
72
120
  }
@@ -74,73 +122,93 @@ export async function runUpdate(username, options) {
74
122
  diagError('Update command failed while fetching PRs', err, {
75
123
  username,
76
124
  options,
77
- since: existing.lastUpdated,
125
+ since,
78
126
  });
79
127
  rpgError(`Failed to fetch PRs: ${err.message}`);
80
128
  process.exit(1);
81
129
  }
82
- const existingIds = new Set(existing.prs.map((p) => p.id));
83
- const freshPRs = newPRs.filter((p) => !existingIds.has(p.id));
84
- diagLog('Filtered fresh PRs during update', {
85
- existingCount: existingIds.size,
86
- candidateCount: newPRs.length,
87
- freshCount: freshPRs.length,
88
- freshPRNumbers: freshPRs.map((pr) => pr.number),
89
- });
90
- rpgSuccess(`Found ${freshPRs.length} new PRs since last update`);
91
- if (freshPRs.length === 0) {
130
+ const existingByIdentity = new Map(existing.prs.map((pr) => [prIdentity(pr), pr]));
131
+ let allPRs;
132
+ let freshPRs;
133
+ let agedOutCount = 0;
134
+ if (effectiveWeeks != null) {
135
+ allPRs = fetchedPRs.map((pr) => mergeWithCachedAnalysis(pr, existingByIdentity.get(prIdentity(pr))));
136
+ freshPRs = allPRs.filter((pr) => {
137
+ const cached = existingByIdentity.get(prIdentity(pr));
138
+ return !cached || !cached.analysis || cached.points == null;
139
+ });
140
+ const fetchedKeys = new Set(allPRs.map(prIdentity));
141
+ agedOutCount = existing.prs.filter((pr) => !fetchedKeys.has(prIdentity(pr))).length;
142
+ rpgSuccess(`Window contains ${allPRs.length} PRs${freshPRs.length > 0 ? `, ${freshPRs.length} new to analyze` : ''}${agedOutCount > 0 ? `, ${agedOutCount} aged out` : ''}.`);
143
+ }
144
+ else {
145
+ freshPRs = fetchedPRs.filter((pr) => !existingByIdentity.has(prIdentity(pr)));
146
+ allPRs = [...existing.prs, ...freshPRs];
147
+ rpgSuccess(`Found ${freshPRs.length} new PRs since last update`);
148
+ }
149
+ const teamsChanged = !sameTeamSet(existing.teams ?? [], effectiveTeams);
150
+ const windowChanged = existing.scope?.windowWeeks !== effectiveWeeks;
151
+ const prSetChanged = effectiveWeeks != null ? !samePRSet(allPRs, existing.prs) : freshPRs.length > 0;
152
+ if (freshPRs.length === 0 && !teamsChanged && !windowChanged && !prSetChanged) {
92
153
  rpgInfo('Nothing new to analyze. Your card is up to date.');
93
154
  return;
94
155
  }
95
- rpgLog('Evaluating code complexity...');
96
- rpgInfo('Press ESC to stop scanning and save partial results.');
97
- const { isCancelled, cleanup } = setupEscCancel();
98
- for (let i = 0; i < freshPRs.length; i++) {
99
- if (isCancelled()) {
100
- rpgInfo(`Scan stopped early after ${i} PR${i === 1 ? '' : 's'}. Saving partial results...`);
101
- freshPRs.splice(i);
102
- break;
103
- }
104
- const pr = freshPRs[i];
105
- rpgProgress(i + 1, freshPRs.length, pr.title);
106
- diagLog('Analyzing fresh PR during update', {
107
- index: i + 1,
108
- total: freshPRs.length,
109
- prNumber: pr.number,
110
- repo: pr.repo,
111
- title: pr.title,
112
- mergedAt: pr.mergedAt,
113
- additions: pr.additions,
114
- deletions: pr.deletions,
115
- diffPresent: Boolean(pr.diff),
116
- diffLength: pr.diff?.length ?? 0,
117
- });
118
- try {
119
- const analysisStartedAt = Date.now();
120
- pr.analysis = await analyzePR(pr, model);
121
- pr.points = calculatePRPoints(pr);
122
- diagLog('Completed fresh PR analysis during update', {
123
- prNumber: pr.number,
124
- durationMs: Date.now() - analysisStartedAt,
125
- skipped: pr.analysis.skipped,
126
- complexity: pr.analysis.complexity,
127
- areas: pr.analysis.areas,
128
- types: pr.analysis.types,
129
- technologies: pr.analysis.technologies,
130
- points: pr.points,
131
- });
132
- }
133
- catch (err) {
134
- diagError('PR analysis failed during update', err, {
156
+ if (freshPRs.length > 0) {
157
+ rpgLog('Evaluating effort and complexity...');
158
+ rpgInfo('Press ESC to stop scanning and save partial results.');
159
+ const { isCancelled, cleanup } = setupEscCancel();
160
+ for (let i = 0; i < freshPRs.length; i++) {
161
+ if (isCancelled()) {
162
+ rpgInfo(`Scan stopped early after ${i} PR${i === 1 ? '' : 's'}. Saving partial results...`);
163
+ freshPRs.splice(i);
164
+ allPRs = effectiveWeeks != null
165
+ ? allPRs.filter((pr) => pr.analysis || pr.points != null || freshPRs.includes(pr))
166
+ : [...existing.prs, ...freshPRs];
167
+ break;
168
+ }
169
+ const pr = freshPRs[i];
170
+ rpgProgress(i + 1, freshPRs.length, pr.title);
171
+ diagLog('Analyzing fresh PR during update', {
172
+ index: i + 1,
173
+ total: freshPRs.length,
135
174
  prNumber: pr.number,
136
175
  repo: pr.repo,
137
176
  title: pr.title,
177
+ mergedAt: pr.mergedAt,
178
+ additions: pr.additions,
179
+ deletions: pr.deletions,
180
+ diffPresent: Boolean(pr.diff),
181
+ diffLength: pr.diff?.length ?? 0,
138
182
  });
139
- rpgError(`Skipping PR #${pr.number}: ${err.message}`);
183
+ try {
184
+ const analysisStartedAt = Date.now();
185
+ pr.analysis = await analyzePR(pr, model);
186
+ pr.points = calculatePRPoints(pr);
187
+ diagLog('Completed fresh PR analysis during update', {
188
+ prNumber: pr.number,
189
+ durationMs: Date.now() - analysisStartedAt,
190
+ skipped: pr.analysis.skipped,
191
+ effortScore: pr.analysis.effortScore,
192
+ areas: pr.analysis.areas,
193
+ types: pr.analysis.types,
194
+ technologies: pr.analysis.technologies,
195
+ points: pr.points,
196
+ });
197
+ }
198
+ catch (err) {
199
+ diagError('PR analysis failed during update', err, {
200
+ prNumber: pr.number,
201
+ repo: pr.repo,
202
+ title: pr.title,
203
+ });
204
+ rpgError(`Skipping PR #${pr.number}: ${err.message}`);
205
+ }
140
206
  }
207
+ cleanup();
208
+ }
209
+ else {
210
+ rpgInfo('No new PRs required analysis. Rebuilding the scoped profile from cached results.');
141
211
  }
142
- cleanup();
143
- const allPRs = [...existing.prs, ...freshPRs];
144
212
  const stats = aggregateStats(allPRs);
145
213
  diagLog('Aggregated stats for update', {
146
214
  totalPRsAfterMerge: allPRs.length,
@@ -151,17 +219,27 @@ export async function runUpdate(username, options) {
151
219
  areaCount: Object.keys(stats.areas).length,
152
220
  technologyCount: Object.keys(stats.technologies).length,
153
221
  typeCount: Object.keys(stats.types).length,
222
+ teamsChanged,
223
+ windowChanged,
224
+ agedOutCount,
154
225
  });
155
226
  diagLog('Preserving existing character profile during update (use update-summary to regenerate)', {
156
227
  username,
157
228
  hasCharacter: Boolean(existing.character),
158
229
  });
159
230
  const userData = {
231
+ ...existing,
160
232
  username,
161
233
  lastUpdated: new Date().toISOString(),
162
234
  prs: allPRs,
163
235
  stats,
164
236
  character: existing.character,
237
+ scope: {
238
+ organisation: resolvedOptions.org,
239
+ repository: resolvedOptions.repository,
240
+ windowWeeks: effectiveWeeks,
241
+ },
242
+ teams: effectiveTeams,
165
243
  };
166
244
  saveUserData(userData);
167
245
  diagLog('Persisted user data for update', {
@@ -171,11 +249,13 @@ export async function runUpdate(username, options) {
171
249
  totalPoints: userData.stats.totalPoints,
172
250
  level: userData.stats.level,
173
251
  },
252
+ teams: userData.teams,
253
+ scope: userData.scope,
174
254
  });
175
255
  rpgLog('Forging updated Quack-Card...');
176
256
  const reportStartedAt = Date.now();
177
257
  const html = generateHTML(userData);
178
- const reportPath = saveReport(username, html);
258
+ const reportPath = saveReport(resolvedOptions.selector, html);
179
259
  diagLog('Generated and saved report for update', {
180
260
  durationMs: Date.now() - reportStartedAt,
181
261
  reportPath,