tlc-claude-code 1.2.29 → 1.3.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 (80) hide show
  1. package/dashboard/dist/components/UsagePane.d.ts +13 -0
  2. package/dashboard/dist/components/UsagePane.js +51 -0
  3. package/dashboard/dist/components/UsagePane.test.d.ts +1 -0
  4. package/dashboard/dist/components/UsagePane.test.js +142 -0
  5. package/dashboard/dist/components/WorkspaceDocsPane.d.ts +19 -0
  6. package/dashboard/dist/components/WorkspaceDocsPane.js +146 -0
  7. package/dashboard/dist/components/WorkspaceDocsPane.test.d.ts +1 -0
  8. package/dashboard/dist/components/WorkspaceDocsPane.test.js +242 -0
  9. package/dashboard/dist/components/WorkspacePane.d.ts +18 -0
  10. package/dashboard/dist/components/WorkspacePane.js +17 -0
  11. package/dashboard/dist/components/WorkspacePane.test.d.ts +1 -0
  12. package/dashboard/dist/components/WorkspacePane.test.js +84 -0
  13. package/package.json +1 -1
  14. package/server/lib/architecture-command.js +450 -0
  15. package/server/lib/architecture-command.test.js +754 -0
  16. package/server/lib/ast-analyzer.js +324 -0
  17. package/server/lib/ast-analyzer.test.js +437 -0
  18. package/server/lib/auth-system.test.js +4 -1
  19. package/server/lib/boundary-detector.js +427 -0
  20. package/server/lib/boundary-detector.test.js +320 -0
  21. package/server/lib/budget-alerts.js +138 -0
  22. package/server/lib/budget-alerts.test.js +235 -0
  23. package/server/lib/candidates-tracker.js +210 -0
  24. package/server/lib/candidates-tracker.test.js +300 -0
  25. package/server/lib/checkpoint-manager.js +251 -0
  26. package/server/lib/checkpoint-manager.test.js +474 -0
  27. package/server/lib/circular-detector.js +337 -0
  28. package/server/lib/circular-detector.test.js +353 -0
  29. package/server/lib/cohesion-analyzer.js +310 -0
  30. package/server/lib/cohesion-analyzer.test.js +447 -0
  31. package/server/lib/contract-testing.js +625 -0
  32. package/server/lib/contract-testing.test.js +342 -0
  33. package/server/lib/conversion-planner.js +469 -0
  34. package/server/lib/conversion-planner.test.js +361 -0
  35. package/server/lib/convert-command.js +351 -0
  36. package/server/lib/convert-command.test.js +608 -0
  37. package/server/lib/coupling-calculator.js +189 -0
  38. package/server/lib/coupling-calculator.test.js +509 -0
  39. package/server/lib/dependency-graph.js +367 -0
  40. package/server/lib/dependency-graph.test.js +516 -0
  41. package/server/lib/duplication-detector.js +349 -0
  42. package/server/lib/duplication-detector.test.js +401 -0
  43. package/server/lib/example-service.js +616 -0
  44. package/server/lib/example-service.test.js +397 -0
  45. package/server/lib/impact-scorer.js +184 -0
  46. package/server/lib/impact-scorer.test.js +211 -0
  47. package/server/lib/mermaid-generator.js +358 -0
  48. package/server/lib/mermaid-generator.test.js +301 -0
  49. package/server/lib/messaging-patterns.js +750 -0
  50. package/server/lib/messaging-patterns.test.js +213 -0
  51. package/server/lib/microservice-template.js +386 -0
  52. package/server/lib/microservice-template.test.js +325 -0
  53. package/server/lib/new-project-microservice.js +450 -0
  54. package/server/lib/new-project-microservice.test.js +600 -0
  55. package/server/lib/refactor-command.js +326 -0
  56. package/server/lib/refactor-command.test.js +528 -0
  57. package/server/lib/refactor-executor.js +254 -0
  58. package/server/lib/refactor-executor.test.js +305 -0
  59. package/server/lib/refactor-observer.js +292 -0
  60. package/server/lib/refactor-observer.test.js +422 -0
  61. package/server/lib/refactor-progress.js +193 -0
  62. package/server/lib/refactor-progress.test.js +251 -0
  63. package/server/lib/refactor-reporter.js +237 -0
  64. package/server/lib/refactor-reporter.test.js +247 -0
  65. package/server/lib/semantic-analyzer.js +198 -0
  66. package/server/lib/semantic-analyzer.test.js +474 -0
  67. package/server/lib/service-scaffold.js +486 -0
  68. package/server/lib/service-scaffold.test.js +373 -0
  69. package/server/lib/shared-kernel.js +578 -0
  70. package/server/lib/shared-kernel.test.js +255 -0
  71. package/server/lib/traefik-config.js +282 -0
  72. package/server/lib/traefik-config.test.js +312 -0
  73. package/server/lib/usage-command.js +218 -0
  74. package/server/lib/usage-command.test.js +391 -0
  75. package/server/lib/usage-formatter.js +192 -0
  76. package/server/lib/usage-formatter.test.js +267 -0
  77. package/server/lib/usage-history.js +122 -0
  78. package/server/lib/usage-history.test.js +206 -0
  79. package/server/package-lock.json +14 -0
  80. package/server/package.json +1 -0
@@ -0,0 +1,397 @@
1
+ /**
2
+ * Example Service Template Tests
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+
7
+ describe('ExampleService', () => {
8
+ describe('generate', () => {
9
+ it('generates service directory structure', async () => {
10
+ const { ExampleService } = await import('./example-service.js');
11
+ const service = new ExampleService();
12
+
13
+ const config = {
14
+ name: 'user',
15
+ port: 3001,
16
+ database: 'postgres',
17
+ };
18
+
19
+ const result = service.generate(config);
20
+
21
+ expect(result.directories).toContain('user');
22
+ expect(result.directories).toContain('user/src');
23
+ expect(result.directories).toContain('user/src/routes');
24
+ expect(result.directories).toContain('user/migrations');
25
+ expect(result.directories).toContain('user/tests');
26
+ });
27
+
28
+ it('generates all required files', async () => {
29
+ const { ExampleService } = await import('./example-service.js');
30
+ const service = new ExampleService();
31
+
32
+ const config = {
33
+ name: 'order',
34
+ port: 3002,
35
+ database: 'postgres',
36
+ };
37
+
38
+ const result = service.generate(config);
39
+ const filePaths = result.files.map(f => f.path);
40
+
41
+ expect(filePaths).toContain('order/package.json');
42
+ expect(filePaths).toContain('order/src/index.js');
43
+ expect(filePaths).toContain('order/src/routes/index.js');
44
+ expect(filePaths).toContain('order/Dockerfile');
45
+ expect(filePaths).toContain('order/docker-compose.yml');
46
+ });
47
+ });
48
+
49
+ describe('generatePackageJson', () => {
50
+ it('has correct name from config', async () => {
51
+ const { ExampleService } = await import('./example-service.js');
52
+ const service = new ExampleService();
53
+
54
+ const config = { name: 'inventory', port: 3003 };
55
+
56
+ const result = service.generatePackageJson(config);
57
+ const pkg = JSON.parse(result);
58
+
59
+ expect(pkg.name).toBe('inventory');
60
+ });
61
+
62
+ it('includes express dependency', async () => {
63
+ const { ExampleService } = await import('./example-service.js');
64
+ const service = new ExampleService();
65
+
66
+ const config = { name: 'catalog', port: 3004 };
67
+
68
+ const result = service.generatePackageJson(config);
69
+ const pkg = JSON.parse(result);
70
+
71
+ expect(pkg.dependencies.express).toBeDefined();
72
+ });
73
+
74
+ it('includes pg when database is postgres', async () => {
75
+ const { ExampleService } = await import('./example-service.js');
76
+ const service = new ExampleService();
77
+
78
+ const config = { name: 'billing', port: 3005, database: 'postgres' };
79
+
80
+ const result = service.generatePackageJson(config);
81
+ const pkg = JSON.parse(result);
82
+
83
+ expect(pkg.dependencies.pg).toBeDefined();
84
+ });
85
+
86
+ it('does not include pg when no database specified', async () => {
87
+ const { ExampleService } = await import('./example-service.js');
88
+ const service = new ExampleService();
89
+
90
+ const config = { name: 'simple', port: 3006 };
91
+
92
+ const result = service.generatePackageJson(config);
93
+ const pkg = JSON.parse(result);
94
+
95
+ expect(pkg.dependencies.pg).toBeUndefined();
96
+ });
97
+
98
+ it('includes required scripts', async () => {
99
+ const { ExampleService } = await import('./example-service.js');
100
+ const service = new ExampleService();
101
+
102
+ const config = { name: 'worker', port: 3007 };
103
+
104
+ const result = service.generatePackageJson(config);
105
+ const pkg = JSON.parse(result);
106
+
107
+ expect(pkg.scripts.start).toBeDefined();
108
+ expect(pkg.scripts.dev).toBeDefined();
109
+ expect(pkg.scripts.test).toBeDefined();
110
+ expect(pkg.scripts.migrate).toBeDefined();
111
+ });
112
+ });
113
+
114
+ describe('generateIndex', () => {
115
+ it('creates express app', async () => {
116
+ const { ExampleService } = await import('./example-service.js');
117
+ const service = new ExampleService();
118
+
119
+ const config = { name: 'api', port: 3008 };
120
+
121
+ const result = service.generateIndex(config);
122
+
123
+ expect(result).toContain("require('express')");
124
+ expect(result).toContain('express()');
125
+ });
126
+
127
+ it('has health endpoint', async () => {
128
+ const { ExampleService } = await import('./example-service.js');
129
+ const service = new ExampleService();
130
+
131
+ const config = { name: 'health-check', port: 3009 };
132
+
133
+ const result = service.generateIndex(config);
134
+
135
+ expect(result).toContain('/health');
136
+ expect(result).toContain('healthy');
137
+ });
138
+
139
+ it('imports routes', async () => {
140
+ const { ExampleService } = await import('./example-service.js');
141
+ const service = new ExampleService();
142
+
143
+ const config = { name: 'router', port: 3010 };
144
+
145
+ const result = service.generateIndex(config);
146
+
147
+ expect(result).toContain("require('./routes')");
148
+ });
149
+
150
+ it('has error middleware', async () => {
151
+ const { ExampleService } = await import('./example-service.js');
152
+ const service = new ExampleService();
153
+
154
+ const config = { name: 'errors', port: 3011 };
155
+
156
+ const result = service.generateIndex(config);
157
+
158
+ expect(result).toContain('err');
159
+ expect(result).toContain('500');
160
+ });
161
+ });
162
+
163
+ describe('generateRoutes', () => {
164
+ it('includes GET list endpoint', async () => {
165
+ const { ExampleService } = await import('./example-service.js');
166
+ const service = new ExampleService();
167
+
168
+ const config = { name: 'items', port: 3012 };
169
+
170
+ const result = service.generateRoutes(config);
171
+
172
+ expect(result).toContain("router.get('/'");
173
+ });
174
+
175
+ it('includes GET by id endpoint', async () => {
176
+ const { ExampleService } = await import('./example-service.js');
177
+ const service = new ExampleService();
178
+
179
+ const config = { name: 'products', port: 3013 };
180
+
181
+ const result = service.generateRoutes(config);
182
+
183
+ expect(result).toContain("router.get('/:id'");
184
+ });
185
+
186
+ it('includes POST create endpoint', async () => {
187
+ const { ExampleService } = await import('./example-service.js');
188
+ const service = new ExampleService();
189
+
190
+ const config = { name: 'users', port: 3014 };
191
+
192
+ const result = service.generateRoutes(config);
193
+
194
+ expect(result).toContain("router.post('/'");
195
+ });
196
+
197
+ it('includes PUT update endpoint', async () => {
198
+ const { ExampleService } = await import('./example-service.js');
199
+ const service = new ExampleService();
200
+
201
+ const config = { name: 'accounts', port: 3015 };
202
+
203
+ const result = service.generateRoutes(config);
204
+
205
+ expect(result).toContain("router.put('/:id'");
206
+ });
207
+
208
+ it('includes DELETE endpoint', async () => {
209
+ const { ExampleService } = await import('./example-service.js');
210
+ const service = new ExampleService();
211
+
212
+ const config = { name: 'tasks', port: 3016 };
213
+
214
+ const result = service.generateRoutes(config);
215
+
216
+ expect(result).toContain("router.delete('/:id'");
217
+ });
218
+ });
219
+
220
+ describe('generateMigrations', () => {
221
+ it('has up function', async () => {
222
+ const { ExampleService } = await import('./example-service.js');
223
+ const service = new ExampleService();
224
+
225
+ const config = { name: 'migrate', port: 3017, database: 'postgres' };
226
+
227
+ const result = service.generateMigrations(config);
228
+
229
+ expect(result.content).toContain('async function up');
230
+ });
231
+
232
+ it('has down function', async () => {
233
+ const { ExampleService } = await import('./example-service.js');
234
+ const service = new ExampleService();
235
+
236
+ const config = { name: 'rollback', port: 3018, database: 'postgres' };
237
+
238
+ const result = service.generateMigrations(config);
239
+
240
+ expect(result.content).toContain('async function down');
241
+ });
242
+
243
+ it('has timestamp naming', async () => {
244
+ const { ExampleService } = await import('./example-service.js');
245
+ const service = new ExampleService();
246
+
247
+ const config = { name: 'schema', port: 3019, database: 'postgres' };
248
+
249
+ const result = service.generateMigrations(config);
250
+
251
+ // Should have timestamp format like 001_initial_schema.js or YYYYMMDD format
252
+ expect(result.path).toMatch(/\d+.*\.js$/);
253
+ });
254
+ });
255
+
256
+ describe('generateTests', () => {
257
+ it('covers CRUD operations', async () => {
258
+ const { ExampleService } = await import('./example-service.js');
259
+ const service = new ExampleService();
260
+
261
+ const config = { name: 'crud', port: 3020 };
262
+
263
+ const result = service.generateTests(config);
264
+
265
+ expect(result.unit).toContain('GET');
266
+ expect(result.unit).toContain('POST');
267
+ expect(result.unit).toContain('PUT');
268
+ expect(result.unit).toContain('DELETE');
269
+ });
270
+
271
+ it('has integration test examples', async () => {
272
+ const { ExampleService } = await import('./example-service.js');
273
+ const service = new ExampleService();
274
+
275
+ const config = { name: 'integration', port: 3021 };
276
+
277
+ const result = service.generateTests(config);
278
+
279
+ expect(result.integration).toContain('describe');
280
+ expect(result.integration).toContain('it(');
281
+ });
282
+ });
283
+
284
+ describe('generateDockerfile', () => {
285
+ it('has multi-stage build', async () => {
286
+ const { ExampleService } = await import('./example-service.js');
287
+ const service = new ExampleService();
288
+
289
+ const config = { name: 'docker', port: 3022 };
290
+
291
+ const result = service.generateDockerfile(config);
292
+
293
+ expect(result).toContain('FROM');
294
+ expect(result).toContain('AS');
295
+ });
296
+
297
+ it('has health check', async () => {
298
+ const { ExampleService } = await import('./example-service.js');
299
+ const service = new ExampleService();
300
+
301
+ const config = { name: 'healthcheck', port: 3023 };
302
+
303
+ const result = service.generateDockerfile(config);
304
+
305
+ expect(result).toContain('HEALTHCHECK');
306
+ });
307
+
308
+ it('has non-root user', async () => {
309
+ const { ExampleService } = await import('./example-service.js');
310
+ const service = new ExampleService();
311
+
312
+ const config = { name: 'secure', port: 3024 };
313
+
314
+ const result = service.generateDockerfile(config);
315
+
316
+ expect(result).toContain('USER');
317
+ });
318
+ });
319
+
320
+ describe('generateDockerCompose', () => {
321
+ it('includes service container', async () => {
322
+ const { ExampleService } = await import('./example-service.js');
323
+ const service = new ExampleService();
324
+
325
+ const config = { name: 'compose', port: 3025 };
326
+
327
+ const result = service.generateDockerCompose(config);
328
+
329
+ expect(result).toContain('compose');
330
+ expect(result).toContain('services:');
331
+ });
332
+
333
+ it('includes database when configured', async () => {
334
+ const { ExampleService } = await import('./example-service.js');
335
+ const service = new ExampleService();
336
+
337
+ const config = { name: 'db-compose', port: 3026, database: 'postgres' };
338
+
339
+ const result = service.generateDockerCompose(config);
340
+
341
+ expect(result).toContain('postgres');
342
+ });
343
+
344
+ it('has network configuration', async () => {
345
+ const { ExampleService } = await import('./example-service.js');
346
+ const service = new ExampleService();
347
+
348
+ const config = { name: 'network', port: 3027 };
349
+
350
+ const result = service.generateDockerCompose(config);
351
+
352
+ expect(result).toContain('networks:');
353
+ });
354
+ });
355
+
356
+ describe('edge cases', () => {
357
+ it('handles service without database', async () => {
358
+ const { ExampleService } = await import('./example-service.js');
359
+ const service = new ExampleService();
360
+
361
+ const config = { name: 'no-db', port: 3028 };
362
+
363
+ const result = service.generate(config);
364
+
365
+ // Should still generate basic structure
366
+ expect(result.directories).toContain('no-db');
367
+ expect(result.files.length).toBeGreaterThan(0);
368
+
369
+ // Docker compose should NOT have postgres
370
+ const composeFile = result.files.find(f => f.path.endsWith('docker-compose.yml'));
371
+ expect(composeFile.content).not.toContain('postgres');
372
+ });
373
+
374
+ it('service starts without errors (structure check)', async () => {
375
+ const { ExampleService } = await import('./example-service.js');
376
+ const service = new ExampleService();
377
+
378
+ const config = { name: 'valid', port: 3029, database: 'postgres' };
379
+
380
+ const result = service.generate(config);
381
+
382
+ // Verify complete structure
383
+ expect(result.directories).toBeDefined();
384
+ expect(result.files).toBeDefined();
385
+ expect(Array.isArray(result.directories)).toBe(true);
386
+ expect(Array.isArray(result.files)).toBe(true);
387
+
388
+ // Each file should have path and content
389
+ for (const file of result.files) {
390
+ expect(file.path).toBeDefined();
391
+ expect(file.content).toBeDefined();
392
+ expect(typeof file.path).toBe('string');
393
+ expect(typeof file.content).toBe('string');
394
+ }
395
+ });
396
+ });
397
+ });
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Impact Scorer
3
+ * Calculate priority score for refactoring opportunities
4
+ */
5
+
6
+ const { execSync } = require('child_process');
7
+
8
+ class ImpactScorer {
9
+ constructor(options = {}) {
10
+ this.weights = {
11
+ complexityReduction: options.complexityWeight || 0.30,
12
+ blastRadius: options.blastRadiusWeight || 0.25,
13
+ changeFrequency: options.frequencyWeight || 0.25,
14
+ risk: options.riskWeight || 0.20,
15
+ };
16
+ this.exec = options.exec || this.defaultExec.bind(this);
17
+ }
18
+
19
+ defaultExec(command) {
20
+ try {
21
+ return execSync(command, { encoding: 'utf-8' });
22
+ } catch {
23
+ return '';
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Calculate impact score for a refactoring opportunity
29
+ * @param {Object} opportunity - Refactoring opportunity
30
+ * @returns {Object} Score breakdown and total
31
+ */
32
+ score(opportunity) {
33
+ const complexityScore = this.scoreComplexityReduction(opportunity);
34
+ const blastRadiusScore = this.scoreBlastRadius(opportunity);
35
+ const frequencyScore = this.scoreChangeFrequency(opportunity);
36
+ const riskScore = this.scoreRisk(opportunity);
37
+
38
+ const total = Math.round(
39
+ complexityScore * this.weights.complexityReduction +
40
+ blastRadiusScore * this.weights.blastRadius +
41
+ frequencyScore * this.weights.changeFrequency +
42
+ riskScore * this.weights.risk
43
+ );
44
+
45
+ return {
46
+ total: Math.min(100, Math.max(0, total)),
47
+ breakdown: {
48
+ complexityReduction: complexityScore,
49
+ blastRadius: blastRadiusScore,
50
+ changeFrequency: frequencyScore,
51
+ risk: riskScore,
52
+ },
53
+ weights: this.weights,
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Score based on complexity reduction potential
59
+ * Higher complexity = higher score (more to gain)
60
+ */
61
+ scoreComplexityReduction(opportunity) {
62
+ const { complexity, targetComplexity } = opportunity;
63
+
64
+ if (!complexity) return 50; // Default middle score
65
+
66
+ const reduction = complexity - (targetComplexity || 1);
67
+
68
+ // Scale: 0-5 reduction = 0-50, 5-15 = 50-80, 15+ = 80-100
69
+ if (reduction <= 0) return 20;
70
+ if (reduction <= 5) return 20 + (reduction * 10);
71
+ if (reduction <= 15) return 70 + ((reduction - 5) * 2);
72
+ return 90 + Math.min(10, (reduction - 15));
73
+ }
74
+
75
+ /**
76
+ * Score based on blast radius (files affected)
77
+ */
78
+ scoreBlastRadius(opportunity) {
79
+ const { filesAffected, linesAffected } = opportunity;
80
+
81
+ // More files affected = higher impact
82
+ let score = 30; // base
83
+
84
+ if (filesAffected) {
85
+ if (filesAffected === 1) score = 40;
86
+ else if (filesAffected <= 3) score = 60;
87
+ else if (filesAffected <= 10) score = 80;
88
+ else score = 95;
89
+ }
90
+
91
+ // Adjust for lines affected
92
+ if (linesAffected) {
93
+ if (linesAffected > 100) score = Math.min(100, score + 10);
94
+ if (linesAffected > 500) score = Math.min(100, score + 10);
95
+ }
96
+
97
+ return score;
98
+ }
99
+
100
+ /**
101
+ * Score based on change frequency from git history
102
+ */
103
+ scoreChangeFrequency(opportunity) {
104
+ const { filePath, changeCount } = opportunity;
105
+
106
+ // If changeCount provided, use it
107
+ if (changeCount !== undefined) {
108
+ if (changeCount === 0) return 30;
109
+ if (changeCount <= 5) return 50;
110
+ if (changeCount <= 20) return 70;
111
+ if (changeCount <= 50) return 85;
112
+ return 95;
113
+ }
114
+
115
+ // Otherwise try to get from git
116
+ if (filePath) {
117
+ try {
118
+ const output = this.exec(`git log --oneline "${filePath}" 2>/dev/null | wc -l`);
119
+ const commits = parseInt(output.trim(), 10) || 0;
120
+
121
+ if (commits === 0) return 30;
122
+ if (commits <= 5) return 50;
123
+ if (commits <= 20) return 70;
124
+ if (commits <= 50) return 85;
125
+ return 95;
126
+ } catch {
127
+ return 50; // Default if git fails
128
+ }
129
+ }
130
+
131
+ return 50;
132
+ }
133
+
134
+ /**
135
+ * Score based on risk (test coverage, criticality)
136
+ * Lower coverage = higher risk = higher priority to refactor safely
137
+ */
138
+ scoreRisk(opportunity) {
139
+ const { testCoverage, isCritical } = opportunity;
140
+
141
+ let score = 50;
142
+
143
+ // Lower test coverage = higher risk score
144
+ if (testCoverage !== undefined) {
145
+ if (testCoverage >= 80) score = 30;
146
+ else if (testCoverage >= 60) score = 50;
147
+ else if (testCoverage >= 40) score = 70;
148
+ else if (testCoverage >= 20) score = 85;
149
+ else score = 95;
150
+ }
151
+
152
+ // Critical paths get higher score
153
+ if (isCritical) {
154
+ score = Math.min(100, score + 15);
155
+ }
156
+
157
+ return score;
158
+ }
159
+
160
+ /**
161
+ * Score multiple opportunities and sort by impact
162
+ * @param {Array} opportunities - Array of opportunities
163
+ * @returns {Array} Sorted opportunities with scores
164
+ */
165
+ scoreAll(opportunities) {
166
+ return opportunities
167
+ .map(opp => ({
168
+ ...opp,
169
+ impact: this.score(opp),
170
+ }))
171
+ .sort((a, b) => b.impact.total - a.impact.total);
172
+ }
173
+
174
+ /**
175
+ * Get priority tier from score
176
+ */
177
+ static getTier(score) {
178
+ if (score >= 80) return 'high';
179
+ if (score >= 50) return 'medium';
180
+ return 'low';
181
+ }
182
+ }
183
+
184
+ module.exports = { ImpactScorer };