@open330/oac-tracking 2026.2.17

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Open330
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,357 @@
1
+ import { z } from 'zod';
2
+
3
+ declare const taskSourceValues: readonly ["lint", "todo", "test-gap", "dead-code", "github-issue", "github-pr-review", "custom"];
4
+ declare const taskComplexityValues: readonly ["trivial", "simple", "moderate", "complex"];
5
+ declare const contributionTaskStatusValues: readonly ["success", "partial", "failed"];
6
+ type TaskSource = (typeof taskSourceValues)[number];
7
+ type TaskComplexity = (typeof taskComplexityValues)[number];
8
+ type ContributionTaskStatus = (typeof contributionTaskStatusValues)[number];
9
+ type AgentProviderId = string;
10
+ declare const contributionTaskSchema: z.ZodObject<{
11
+ taskId: z.ZodString;
12
+ title: z.ZodString;
13
+ source: z.ZodEnum<["lint", "todo", "test-gap", "dead-code", "github-issue", "github-pr-review", "custom"]>;
14
+ complexity: z.ZodEnum<["trivial", "simple", "moderate", "complex"]>;
15
+ status: z.ZodEnum<["success", "partial", "failed"]>;
16
+ tokensUsed: z.ZodNumber;
17
+ duration: z.ZodNumber;
18
+ filesChanged: z.ZodArray<z.ZodString, "many">;
19
+ pr: z.ZodOptional<z.ZodObject<{
20
+ number: z.ZodNumber;
21
+ url: z.ZodString;
22
+ status: z.ZodEnum<["open", "merged", "closed"]>;
23
+ }, "strip", z.ZodTypeAny, {
24
+ number: number;
25
+ status: "open" | "merged" | "closed";
26
+ url: string;
27
+ }, {
28
+ number: number;
29
+ status: "open" | "merged" | "closed";
30
+ url: string;
31
+ }>>;
32
+ linkedIssue: z.ZodOptional<z.ZodObject<{
33
+ number: z.ZodNumber;
34
+ url: z.ZodString;
35
+ }, "strip", z.ZodTypeAny, {
36
+ number: number;
37
+ url: string;
38
+ }, {
39
+ number: number;
40
+ url: string;
41
+ }>>;
42
+ error: z.ZodOptional<z.ZodString>;
43
+ }, "strip", z.ZodTypeAny, {
44
+ taskId: string;
45
+ title: string;
46
+ source: "lint" | "todo" | "test-gap" | "dead-code" | "github-issue" | "github-pr-review" | "custom";
47
+ status: "success" | "partial" | "failed";
48
+ complexity: "trivial" | "simple" | "moderate" | "complex";
49
+ tokensUsed: number;
50
+ duration: number;
51
+ filesChanged: string[];
52
+ pr?: {
53
+ number: number;
54
+ status: "open" | "merged" | "closed";
55
+ url: string;
56
+ } | undefined;
57
+ linkedIssue?: {
58
+ number: number;
59
+ url: string;
60
+ } | undefined;
61
+ error?: string | undefined;
62
+ }, {
63
+ taskId: string;
64
+ title: string;
65
+ source: "lint" | "todo" | "test-gap" | "dead-code" | "github-issue" | "github-pr-review" | "custom";
66
+ status: "success" | "partial" | "failed";
67
+ complexity: "trivial" | "simple" | "moderate" | "complex";
68
+ tokensUsed: number;
69
+ duration: number;
70
+ filesChanged: string[];
71
+ pr?: {
72
+ number: number;
73
+ status: "open" | "merged" | "closed";
74
+ url: string;
75
+ } | undefined;
76
+ linkedIssue?: {
77
+ number: number;
78
+ url: string;
79
+ } | undefined;
80
+ error?: string | undefined;
81
+ }>;
82
+ declare const contributionLogSchema: z.ZodObject<{
83
+ version: z.ZodLiteral<"1.0">;
84
+ runId: z.ZodString;
85
+ timestamp: z.ZodString;
86
+ contributor: z.ZodObject<{
87
+ githubUsername: z.ZodString;
88
+ email: z.ZodOptional<z.ZodString>;
89
+ }, "strip", z.ZodTypeAny, {
90
+ githubUsername: string;
91
+ email?: string | undefined;
92
+ }, {
93
+ githubUsername: string;
94
+ email?: string | undefined;
95
+ }>;
96
+ repo: z.ZodObject<{
97
+ fullName: z.ZodString;
98
+ headSha: z.ZodString;
99
+ defaultBranch: z.ZodString;
100
+ }, "strip", z.ZodTypeAny, {
101
+ fullName: string;
102
+ headSha: string;
103
+ defaultBranch: string;
104
+ }, {
105
+ fullName: string;
106
+ headSha: string;
107
+ defaultBranch: string;
108
+ }>;
109
+ budget: z.ZodObject<{
110
+ provider: z.ZodString;
111
+ totalTokensBudgeted: z.ZodNumber;
112
+ totalTokensUsed: z.ZodNumber;
113
+ estimatedCostUsd: z.ZodOptional<z.ZodNumber>;
114
+ }, "strip", z.ZodTypeAny, {
115
+ provider: string;
116
+ totalTokensBudgeted: number;
117
+ totalTokensUsed: number;
118
+ estimatedCostUsd?: number | undefined;
119
+ }, {
120
+ provider: string;
121
+ totalTokensBudgeted: number;
122
+ totalTokensUsed: number;
123
+ estimatedCostUsd?: number | undefined;
124
+ }>;
125
+ tasks: z.ZodArray<z.ZodObject<{
126
+ taskId: z.ZodString;
127
+ title: z.ZodString;
128
+ source: z.ZodEnum<["lint", "todo", "test-gap", "dead-code", "github-issue", "github-pr-review", "custom"]>;
129
+ complexity: z.ZodEnum<["trivial", "simple", "moderate", "complex"]>;
130
+ status: z.ZodEnum<["success", "partial", "failed"]>;
131
+ tokensUsed: z.ZodNumber;
132
+ duration: z.ZodNumber;
133
+ filesChanged: z.ZodArray<z.ZodString, "many">;
134
+ pr: z.ZodOptional<z.ZodObject<{
135
+ number: z.ZodNumber;
136
+ url: z.ZodString;
137
+ status: z.ZodEnum<["open", "merged", "closed"]>;
138
+ }, "strip", z.ZodTypeAny, {
139
+ number: number;
140
+ status: "open" | "merged" | "closed";
141
+ url: string;
142
+ }, {
143
+ number: number;
144
+ status: "open" | "merged" | "closed";
145
+ url: string;
146
+ }>>;
147
+ linkedIssue: z.ZodOptional<z.ZodObject<{
148
+ number: z.ZodNumber;
149
+ url: z.ZodString;
150
+ }, "strip", z.ZodTypeAny, {
151
+ number: number;
152
+ url: string;
153
+ }, {
154
+ number: number;
155
+ url: string;
156
+ }>>;
157
+ error: z.ZodOptional<z.ZodString>;
158
+ }, "strip", z.ZodTypeAny, {
159
+ taskId: string;
160
+ title: string;
161
+ source: "lint" | "todo" | "test-gap" | "dead-code" | "github-issue" | "github-pr-review" | "custom";
162
+ status: "success" | "partial" | "failed";
163
+ complexity: "trivial" | "simple" | "moderate" | "complex";
164
+ tokensUsed: number;
165
+ duration: number;
166
+ filesChanged: string[];
167
+ pr?: {
168
+ number: number;
169
+ status: "open" | "merged" | "closed";
170
+ url: string;
171
+ } | undefined;
172
+ linkedIssue?: {
173
+ number: number;
174
+ url: string;
175
+ } | undefined;
176
+ error?: string | undefined;
177
+ }, {
178
+ taskId: string;
179
+ title: string;
180
+ source: "lint" | "todo" | "test-gap" | "dead-code" | "github-issue" | "github-pr-review" | "custom";
181
+ status: "success" | "partial" | "failed";
182
+ complexity: "trivial" | "simple" | "moderate" | "complex";
183
+ tokensUsed: number;
184
+ duration: number;
185
+ filesChanged: string[];
186
+ pr?: {
187
+ number: number;
188
+ status: "open" | "merged" | "closed";
189
+ url: string;
190
+ } | undefined;
191
+ linkedIssue?: {
192
+ number: number;
193
+ url: string;
194
+ } | undefined;
195
+ error?: string | undefined;
196
+ }>, "many">;
197
+ metrics: z.ZodObject<{
198
+ tasksDiscovered: z.ZodNumber;
199
+ tasksAttempted: z.ZodNumber;
200
+ tasksSucceeded: z.ZodNumber;
201
+ tasksFailed: z.ZodNumber;
202
+ totalDuration: z.ZodNumber;
203
+ totalFilesChanged: z.ZodNumber;
204
+ totalLinesAdded: z.ZodNumber;
205
+ totalLinesRemoved: z.ZodNumber;
206
+ }, "strip", z.ZodTypeAny, {
207
+ tasksDiscovered: number;
208
+ tasksAttempted: number;
209
+ tasksSucceeded: number;
210
+ tasksFailed: number;
211
+ totalDuration: number;
212
+ totalFilesChanged: number;
213
+ totalLinesAdded: number;
214
+ totalLinesRemoved: number;
215
+ }, {
216
+ tasksDiscovered: number;
217
+ tasksAttempted: number;
218
+ tasksSucceeded: number;
219
+ tasksFailed: number;
220
+ totalDuration: number;
221
+ totalFilesChanged: number;
222
+ totalLinesAdded: number;
223
+ totalLinesRemoved: number;
224
+ }>;
225
+ }, "strip", z.ZodTypeAny, {
226
+ version: "1.0";
227
+ runId: string;
228
+ timestamp: string;
229
+ contributor: {
230
+ githubUsername: string;
231
+ email?: string | undefined;
232
+ };
233
+ repo: {
234
+ fullName: string;
235
+ headSha: string;
236
+ defaultBranch: string;
237
+ };
238
+ budget: {
239
+ provider: string;
240
+ totalTokensBudgeted: number;
241
+ totalTokensUsed: number;
242
+ estimatedCostUsd?: number | undefined;
243
+ };
244
+ tasks: {
245
+ taskId: string;
246
+ title: string;
247
+ source: "lint" | "todo" | "test-gap" | "dead-code" | "github-issue" | "github-pr-review" | "custom";
248
+ status: "success" | "partial" | "failed";
249
+ complexity: "trivial" | "simple" | "moderate" | "complex";
250
+ tokensUsed: number;
251
+ duration: number;
252
+ filesChanged: string[];
253
+ pr?: {
254
+ number: number;
255
+ status: "open" | "merged" | "closed";
256
+ url: string;
257
+ } | undefined;
258
+ linkedIssue?: {
259
+ number: number;
260
+ url: string;
261
+ } | undefined;
262
+ error?: string | undefined;
263
+ }[];
264
+ metrics: {
265
+ tasksDiscovered: number;
266
+ tasksAttempted: number;
267
+ tasksSucceeded: number;
268
+ tasksFailed: number;
269
+ totalDuration: number;
270
+ totalFilesChanged: number;
271
+ totalLinesAdded: number;
272
+ totalLinesRemoved: number;
273
+ };
274
+ }, {
275
+ version: "1.0";
276
+ runId: string;
277
+ timestamp: string;
278
+ contributor: {
279
+ githubUsername: string;
280
+ email?: string | undefined;
281
+ };
282
+ repo: {
283
+ fullName: string;
284
+ headSha: string;
285
+ defaultBranch: string;
286
+ };
287
+ budget: {
288
+ provider: string;
289
+ totalTokensBudgeted: number;
290
+ totalTokensUsed: number;
291
+ estimatedCostUsd?: number | undefined;
292
+ };
293
+ tasks: {
294
+ taskId: string;
295
+ title: string;
296
+ source: "lint" | "todo" | "test-gap" | "dead-code" | "github-issue" | "github-pr-review" | "custom";
297
+ status: "success" | "partial" | "failed";
298
+ complexity: "trivial" | "simple" | "moderate" | "complex";
299
+ tokensUsed: number;
300
+ duration: number;
301
+ filesChanged: string[];
302
+ pr?: {
303
+ number: number;
304
+ status: "open" | "merged" | "closed";
305
+ url: string;
306
+ } | undefined;
307
+ linkedIssue?: {
308
+ number: number;
309
+ url: string;
310
+ } | undefined;
311
+ error?: string | undefined;
312
+ }[];
313
+ metrics: {
314
+ tasksDiscovered: number;
315
+ tasksAttempted: number;
316
+ tasksSucceeded: number;
317
+ tasksFailed: number;
318
+ totalDuration: number;
319
+ totalFilesChanged: number;
320
+ totalLinesAdded: number;
321
+ totalLinesRemoved: number;
322
+ };
323
+ }>;
324
+ type ContributionTask = z.infer<typeof contributionTaskSchema>;
325
+ type ContributionLog = z.infer<typeof contributionLogSchema>;
326
+ declare function parseContributionLog(input: unknown): ContributionLog;
327
+
328
+ declare function writeContributionLog(log: ContributionLog, repoPath: string): Promise<string>;
329
+
330
+ interface Leaderboard {
331
+ generatedAt: string;
332
+ entries: LeaderboardEntry[];
333
+ repoStats: {
334
+ totalContributions: number;
335
+ totalTokensUsed: number;
336
+ totalPRsCreated: number;
337
+ totalPRsMerged: number;
338
+ firstContribution: string;
339
+ lastContribution: string;
340
+ };
341
+ }
342
+ interface LeaderboardEntry {
343
+ githubUsername: string;
344
+ totalRuns: number;
345
+ totalTasksCompleted: number;
346
+ totalTokensDonated: number;
347
+ totalFilesChanged: number;
348
+ totalLinesChanged: number;
349
+ totalPRsCreated: number;
350
+ totalPRsMerged: number;
351
+ favoriteTaskSource: TaskSource;
352
+ firstContribution: string;
353
+ lastContribution: string;
354
+ }
355
+ declare function buildLeaderboard(repoPath: string): Promise<Leaderboard>;
356
+
357
+ export { type AgentProviderId, type ContributionLog, type ContributionTask, type ContributionTaskStatus, type Leaderboard, type LeaderboardEntry, type TaskComplexity, type TaskSource, buildLeaderboard, contributionLogSchema, contributionTaskSchema, parseContributionLog, writeContributionLog };
package/dist/index.js ADDED
@@ -0,0 +1,316 @@
1
+ // src/log-schema.ts
2
+ import { z } from "zod";
3
+ var taskSourceValues = [
4
+ "lint",
5
+ "todo",
6
+ "test-gap",
7
+ "dead-code",
8
+ "github-issue",
9
+ "github-pr-review",
10
+ "custom"
11
+ ];
12
+ var taskComplexityValues = ["trivial", "simple", "moderate", "complex"];
13
+ var contributionTaskStatusValues = ["success", "partial", "failed"];
14
+ var githubUsernameSchema = z.string().min(1).max(39).regex(/^(?!-)[A-Za-z0-9-]+(?<!-)$/, "Invalid GitHub username.");
15
+ var contributionTaskSchema = z.object({
16
+ taskId: z.string().min(1),
17
+ title: z.string().min(1),
18
+ source: z.enum(taskSourceValues),
19
+ complexity: z.enum(taskComplexityValues),
20
+ status: z.enum(contributionTaskStatusValues),
21
+ tokensUsed: z.number().int().nonnegative(),
22
+ duration: z.number().nonnegative(),
23
+ filesChanged: z.array(z.string().min(1)),
24
+ pr: z.object({
25
+ number: z.number().int().positive(),
26
+ url: z.string().url(),
27
+ status: z.enum(["open", "merged", "closed"])
28
+ }).optional(),
29
+ linkedIssue: z.object({
30
+ number: z.number().int().positive(),
31
+ url: z.string().url()
32
+ }).optional(),
33
+ error: z.string().min(1).optional()
34
+ });
35
+ var contributionLogSchema = z.object({
36
+ version: z.literal("1.0"),
37
+ runId: z.string().min(1),
38
+ timestamp: z.string().datetime({ offset: true }),
39
+ contributor: z.object({
40
+ githubUsername: githubUsernameSchema,
41
+ email: z.string().email().optional()
42
+ }),
43
+ repo: z.object({
44
+ fullName: z.string().min(1).regex(/^[^\s/]+\/[^\s/]+$/, "Expected repository in owner/repo format."),
45
+ headSha: z.string().regex(/^[A-Fa-f0-9]{7,40}$/, "Expected git SHA (7-40 hex chars)."),
46
+ defaultBranch: z.string().min(1)
47
+ }),
48
+ budget: z.object({
49
+ provider: z.string().min(1),
50
+ totalTokensBudgeted: z.number().int().nonnegative(),
51
+ totalTokensUsed: z.number().int().nonnegative(),
52
+ estimatedCostUsd: z.number().nonnegative().optional()
53
+ }),
54
+ tasks: z.array(contributionTaskSchema),
55
+ metrics: z.object({
56
+ tasksDiscovered: z.number().int().nonnegative(),
57
+ tasksAttempted: z.number().int().nonnegative(),
58
+ tasksSucceeded: z.number().int().nonnegative(),
59
+ tasksFailed: z.number().int().nonnegative(),
60
+ totalDuration: z.number().nonnegative(),
61
+ totalFilesChanged: z.number().int().nonnegative(),
62
+ totalLinesAdded: z.number().int().nonnegative(),
63
+ totalLinesRemoved: z.number().int().nonnegative()
64
+ })
65
+ });
66
+ function parseContributionLog(input) {
67
+ return contributionLogSchema.parse(input);
68
+ }
69
+
70
+ // src/logger.ts
71
+ import { randomUUID as randomUUID2 } from "crypto";
72
+ import { mkdir as mkdir2, rename as rename2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
73
+ import { join as join2, resolve as resolve2 } from "path";
74
+
75
+ // src/leaderboard.ts
76
+ import { randomUUID } from "crypto";
77
+ import { mkdir, readFile, readdir, rename, rm, writeFile } from "fs/promises";
78
+ import { join, resolve } from "path";
79
+ var OAC_DIRECTORY = ".oac";
80
+ var CONTRIBUTIONS_DIRECTORY = "contributions";
81
+ var LEADERBOARD_FILENAME = "leaderboard.json";
82
+ var EMPTY_TIMESTAMP = "";
83
+ async function buildLeaderboard(repoPath) {
84
+ const repoRoot = resolve(repoPath);
85
+ const oacPath = join(repoRoot, OAC_DIRECTORY);
86
+ const contributionsPath = join(oacPath, CONTRIBUTIONS_DIRECTORY);
87
+ const logs = await readContributionLogs(contributionsPath);
88
+ const aggregates = /* @__PURE__ */ new Map();
89
+ const repoStats = {
90
+ totalContributions: logs.length,
91
+ totalTokensUsed: 0,
92
+ totalPRsCreated: 0,
93
+ totalPRsMerged: 0,
94
+ firstContribution: EMPTY_TIMESTAMP,
95
+ lastContribution: EMPTY_TIMESTAMP
96
+ };
97
+ for (const log of logs) {
98
+ const username = log.contributor.githubUsername;
99
+ const accumulator = aggregates.get(username) ?? createAccumulator(username, log.timestamp);
100
+ accumulator.totalRuns += 1;
101
+ accumulator.totalTasksCompleted += log.tasks.filter((task) => task.status !== "failed").length;
102
+ accumulator.totalTokensDonated += log.budget.totalTokensUsed;
103
+ accumulator.totalFilesChanged += log.metrics.totalFilesChanged;
104
+ accumulator.totalLinesChanged += log.metrics.totalLinesAdded + log.metrics.totalLinesRemoved;
105
+ accumulator.firstContribution = minIsoTimestamp(accumulator.firstContribution, log.timestamp);
106
+ accumulator.lastContribution = maxIsoTimestamp(accumulator.lastContribution, log.timestamp);
107
+ for (const task of log.tasks) {
108
+ if (task.pr) {
109
+ accumulator.totalPRsCreated += 1;
110
+ if (task.pr.status === "merged") {
111
+ accumulator.totalPRsMerged += 1;
112
+ }
113
+ }
114
+ accumulator.sourceCounts.set(
115
+ task.source,
116
+ (accumulator.sourceCounts.get(task.source) ?? 0) + 1
117
+ );
118
+ }
119
+ aggregates.set(username, accumulator);
120
+ repoStats.totalTokensUsed += log.budget.totalTokensUsed;
121
+ repoStats.firstContribution = minIsoTimestamp(repoStats.firstContribution, log.timestamp);
122
+ repoStats.lastContribution = maxIsoTimestamp(repoStats.lastContribution, log.timestamp);
123
+ repoStats.totalPRsCreated += log.tasks.filter((task) => Boolean(task.pr)).length;
124
+ repoStats.totalPRsMerged += log.tasks.filter((task) => task.pr?.status === "merged").length;
125
+ }
126
+ const entries = Array.from(aggregates.values()).map((entry) => ({
127
+ githubUsername: entry.githubUsername,
128
+ totalRuns: entry.totalRuns,
129
+ totalTasksCompleted: entry.totalTasksCompleted,
130
+ totalTokensDonated: entry.totalTokensDonated,
131
+ totalFilesChanged: entry.totalFilesChanged,
132
+ totalLinesChanged: entry.totalLinesChanged,
133
+ totalPRsCreated: entry.totalPRsCreated,
134
+ totalPRsMerged: entry.totalPRsMerged,
135
+ favoriteTaskSource: getFavoriteTaskSource(entry.sourceCounts),
136
+ firstContribution: entry.firstContribution,
137
+ lastContribution: entry.lastContribution
138
+ })).sort((a, b) => {
139
+ if (b.totalTasksCompleted !== a.totalTasksCompleted) {
140
+ return b.totalTasksCompleted - a.totalTasksCompleted;
141
+ }
142
+ if (b.totalRuns !== a.totalRuns) {
143
+ return b.totalRuns - a.totalRuns;
144
+ }
145
+ if (b.totalTokensDonated !== a.totalTokensDonated) {
146
+ return b.totalTokensDonated - a.totalTokensDonated;
147
+ }
148
+ return a.githubUsername.localeCompare(b.githubUsername);
149
+ });
150
+ const leaderboard = {
151
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
152
+ entries,
153
+ repoStats
154
+ };
155
+ await mkdir(oacPath, { recursive: true });
156
+ const leaderboardPath = join(oacPath, LEADERBOARD_FILENAME);
157
+ const payload = `${JSON.stringify(leaderboard, null, 2)}
158
+ `;
159
+ await writeFileAtomically(leaderboardPath, payload);
160
+ return leaderboard;
161
+ }
162
+ function createAccumulator(githubUsername, initialTimestamp) {
163
+ return {
164
+ githubUsername,
165
+ totalRuns: 0,
166
+ totalTasksCompleted: 0,
167
+ totalTokensDonated: 0,
168
+ totalFilesChanged: 0,
169
+ totalLinesChanged: 0,
170
+ totalPRsCreated: 0,
171
+ totalPRsMerged: 0,
172
+ firstContribution: initialTimestamp,
173
+ lastContribution: initialTimestamp,
174
+ sourceCounts: /* @__PURE__ */ new Map()
175
+ };
176
+ }
177
+ async function readContributionLogs(contributionsPath) {
178
+ let fileNames;
179
+ try {
180
+ const entries = await readdir(contributionsPath, { withFileTypes: true, encoding: "utf8" });
181
+ fileNames = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort();
182
+ } catch (error) {
183
+ if (isFileNotFoundError(error)) {
184
+ return [];
185
+ }
186
+ throw error;
187
+ }
188
+ const logs = await Promise.all(
189
+ fileNames.map(async (fileName) => {
190
+ const filePath = join(contributionsPath, fileName);
191
+ try {
192
+ const fileContent = await readFile(filePath, "utf8");
193
+ const parsedJson = JSON.parse(fileContent);
194
+ const parsedLog = contributionLogSchema.safeParse(parsedJson);
195
+ if (!parsedLog.success) {
196
+ console.warn(
197
+ `[tracking] Skipping invalid contribution log "${fileName}": ${parsedLog.error.issues[0]?.message ?? "Schema validation failed."}`
198
+ );
199
+ return null;
200
+ }
201
+ return parsedLog.data;
202
+ } catch (error) {
203
+ const message = error instanceof Error ? error.message : "Unknown read error";
204
+ console.warn(`[tracking] Skipping unreadable contribution log "${fileName}": ${message}`);
205
+ return null;
206
+ }
207
+ })
208
+ );
209
+ return logs.filter((log) => log !== null);
210
+ }
211
+ function getFavoriteTaskSource(sourceCounts) {
212
+ if (sourceCounts.size === 0) {
213
+ return "custom";
214
+ }
215
+ let favorite = "custom";
216
+ let favoriteCount = -1;
217
+ for (const [source, count] of sourceCounts.entries()) {
218
+ if (count > favoriteCount) {
219
+ favorite = source;
220
+ favoriteCount = count;
221
+ continue;
222
+ }
223
+ if (count === favoriteCount && source.localeCompare(favorite) < 0) {
224
+ favorite = source;
225
+ }
226
+ }
227
+ return favorite;
228
+ }
229
+ function minIsoTimestamp(a, b) {
230
+ if (!a) {
231
+ return b;
232
+ }
233
+ if (!b) {
234
+ return a;
235
+ }
236
+ return Date.parse(a) <= Date.parse(b) ? a : b;
237
+ }
238
+ function maxIsoTimestamp(a, b) {
239
+ if (!a) {
240
+ return b;
241
+ }
242
+ if (!b) {
243
+ return a;
244
+ }
245
+ return Date.parse(a) >= Date.parse(b) ? a : b;
246
+ }
247
+ function isFileNotFoundError(error) {
248
+ if (typeof error !== "object" || error === null) {
249
+ return false;
250
+ }
251
+ const code = error.code;
252
+ return code === "ENOENT";
253
+ }
254
+ async function writeFileAtomically(destinationPath, content) {
255
+ const tempPath = `${destinationPath}.${process.pid}.${randomUUID()}.tmp`;
256
+ try {
257
+ await writeFile(tempPath, content, { encoding: "utf8", flag: "wx" });
258
+ await rename(tempPath, destinationPath);
259
+ } finally {
260
+ await rm(tempPath, { force: true });
261
+ }
262
+ }
263
+
264
+ // src/logger.ts
265
+ var OAC_DIRECTORY2 = ".oac";
266
+ var CONTRIBUTIONS_DIRECTORY2 = "contributions";
267
+ async function writeContributionLog(log, repoPath) {
268
+ const parsedLog = contributionLogSchema.parse(log);
269
+ const contributionsPath = resolve2(repoPath, OAC_DIRECTORY2, CONTRIBUTIONS_DIRECTORY2);
270
+ await mkdir2(contributionsPath, { recursive: true });
271
+ const timestamp = formatFileTimestamp(parsedLog.timestamp);
272
+ const username = toSafeFilenameSegment(parsedLog.contributor.githubUsername);
273
+ const filename = `${timestamp}-${username}.json`;
274
+ const filePath = join2(contributionsPath, filename);
275
+ const payload = `${JSON.stringify(parsedLog, null, 2)}
276
+ `;
277
+ await writeFileAtomically2(filePath, payload);
278
+ try {
279
+ await buildLeaderboard(repoPath);
280
+ } catch (error) {
281
+ const message = error instanceof Error ? error.message : "Unknown leaderboard error";
282
+ console.warn(`[tracking] Contribution log written, but leaderboard refresh failed: ${message}`);
283
+ }
284
+ return filePath;
285
+ }
286
+ function formatFileTimestamp(timestamp) {
287
+ const date = new Date(timestamp);
288
+ const yyyy = String(date.getUTCFullYear());
289
+ const mm = String(date.getUTCMonth() + 1).padStart(2, "0");
290
+ const dd = String(date.getUTCDate()).padStart(2, "0");
291
+ const hh = String(date.getUTCHours()).padStart(2, "0");
292
+ const min = String(date.getUTCMinutes()).padStart(2, "0");
293
+ const sec = String(date.getUTCSeconds()).padStart(2, "0");
294
+ return `${yyyy}-${mm}-${dd}-${hh}${min}${sec}`;
295
+ }
296
+ function toSafeFilenameSegment(value) {
297
+ const sanitized = value.trim().replace(/[^A-Za-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
298
+ return sanitized || "unknown";
299
+ }
300
+ async function writeFileAtomically2(destinationPath, content) {
301
+ const tempPath = `${destinationPath}.${process.pid}.${randomUUID2()}.tmp`;
302
+ try {
303
+ await writeFile2(tempPath, content, { encoding: "utf8", flag: "wx" });
304
+ await rename2(tempPath, destinationPath);
305
+ } finally {
306
+ await rm2(tempPath, { force: true });
307
+ }
308
+ }
309
+ export {
310
+ buildLeaderboard,
311
+ contributionLogSchema,
312
+ contributionTaskSchema,
313
+ parseContributionLog,
314
+ writeContributionLog
315
+ };
316
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/log-schema.ts","../src/logger.ts","../src/leaderboard.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const taskSourceValues = [\n \"lint\",\n \"todo\",\n \"test-gap\",\n \"dead-code\",\n \"github-issue\",\n \"github-pr-review\",\n \"custom\",\n] as const;\n\nexport const taskComplexityValues = [\"trivial\", \"simple\", \"moderate\", \"complex\"] as const;\n\nexport const contributionTaskStatusValues = [\"success\", \"partial\", \"failed\"] as const;\n\nexport type TaskSource = (typeof taskSourceValues)[number];\nexport type TaskComplexity = (typeof taskComplexityValues)[number];\nexport type ContributionTaskStatus = (typeof contributionTaskStatusValues)[number];\nexport type AgentProviderId = string;\n\nconst githubUsernameSchema = z\n .string()\n .min(1)\n .max(39)\n .regex(/^(?!-)[A-Za-z0-9-]+(?<!-)$/, \"Invalid GitHub username.\");\n\nexport const contributionTaskSchema = z.object({\n taskId: z.string().min(1),\n title: z.string().min(1),\n source: z.enum(taskSourceValues),\n complexity: z.enum(taskComplexityValues),\n status: z.enum(contributionTaskStatusValues),\n tokensUsed: z.number().int().nonnegative(),\n duration: z.number().nonnegative(),\n filesChanged: z.array(z.string().min(1)),\n pr: z\n .object({\n number: z.number().int().positive(),\n url: z.string().url(),\n status: z.enum([\"open\", \"merged\", \"closed\"]),\n })\n .optional(),\n linkedIssue: z\n .object({\n number: z.number().int().positive(),\n url: z.string().url(),\n })\n .optional(),\n error: z.string().min(1).optional(),\n});\n\nexport const contributionLogSchema = z.object({\n version: z.literal(\"1.0\"),\n runId: z.string().min(1),\n timestamp: z.string().datetime({ offset: true }),\n contributor: z.object({\n githubUsername: githubUsernameSchema,\n email: z.string().email().optional(),\n }),\n repo: z.object({\n fullName: z\n .string()\n .min(1)\n .regex(/^[^\\s/]+\\/[^\\s/]+$/, \"Expected repository in owner/repo format.\"),\n headSha: z.string().regex(/^[A-Fa-f0-9]{7,40}$/, \"Expected git SHA (7-40 hex chars).\"),\n defaultBranch: z.string().min(1),\n }),\n budget: z.object({\n provider: z.string().min(1),\n totalTokensBudgeted: z.number().int().nonnegative(),\n totalTokensUsed: z.number().int().nonnegative(),\n estimatedCostUsd: z.number().nonnegative().optional(),\n }),\n tasks: z.array(contributionTaskSchema),\n metrics: z.object({\n tasksDiscovered: z.number().int().nonnegative(),\n tasksAttempted: z.number().int().nonnegative(),\n tasksSucceeded: z.number().int().nonnegative(),\n tasksFailed: z.number().int().nonnegative(),\n totalDuration: z.number().nonnegative(),\n totalFilesChanged: z.number().int().nonnegative(),\n totalLinesAdded: z.number().int().nonnegative(),\n totalLinesRemoved: z.number().int().nonnegative(),\n }),\n});\n\nexport type ContributionTask = z.infer<typeof contributionTaskSchema>;\nexport type ContributionLog = z.infer<typeof contributionLogSchema>;\n\nexport function parseContributionLog(input: unknown): ContributionLog {\n return contributionLogSchema.parse(input);\n}\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, rename, rm, writeFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\n\nimport { buildLeaderboard } from \"./leaderboard.js\";\nimport { type ContributionLog, contributionLogSchema } from \"./log-schema.js\";\n\nconst OAC_DIRECTORY = \".oac\";\nconst CONTRIBUTIONS_DIRECTORY = \"contributions\";\n\nexport async function writeContributionLog(\n log: ContributionLog,\n repoPath: string,\n): Promise<string> {\n const parsedLog = contributionLogSchema.parse(log);\n const contributionsPath = resolve(repoPath, OAC_DIRECTORY, CONTRIBUTIONS_DIRECTORY);\n\n await mkdir(contributionsPath, { recursive: true });\n\n const timestamp = formatFileTimestamp(parsedLog.timestamp);\n const username = toSafeFilenameSegment(parsedLog.contributor.githubUsername);\n const filename = `${timestamp}-${username}.json`;\n const filePath = join(contributionsPath, filename);\n\n const payload = `${JSON.stringify(parsedLog, null, 2)}\\n`;\n await writeFileAtomically(filePath, payload);\n try {\n await buildLeaderboard(repoPath);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown leaderboard error\";\n console.warn(`[tracking] Contribution log written, but leaderboard refresh failed: ${message}`);\n }\n\n return filePath;\n}\n\nfunction formatFileTimestamp(timestamp: string): string {\n const date = new Date(timestamp);\n const yyyy = String(date.getUTCFullYear());\n const mm = String(date.getUTCMonth() + 1).padStart(2, \"0\");\n const dd = String(date.getUTCDate()).padStart(2, \"0\");\n const hh = String(date.getUTCHours()).padStart(2, \"0\");\n const min = String(date.getUTCMinutes()).padStart(2, \"0\");\n const sec = String(date.getUTCSeconds()).padStart(2, \"0\");\n\n return `${yyyy}-${mm}-${dd}-${hh}${min}${sec}`;\n}\n\nfunction toSafeFilenameSegment(value: string): string {\n const sanitized = value\n .trim()\n .replace(/[^A-Za-z0-9-]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\n return sanitized || \"unknown\";\n}\n\nasync function writeFileAtomically(destinationPath: string, content: string): Promise<void> {\n const tempPath = `${destinationPath}.${process.pid}.${randomUUID()}.tmp`;\n\n try {\n await writeFile(tempPath, content, { encoding: \"utf8\", flag: \"wx\" });\n await rename(tempPath, destinationPath);\n } finally {\n await rm(tempPath, { force: true });\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, readdir, rename, rm, writeFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\n\nimport { type ContributionLog, type TaskSource, contributionLogSchema } from \"./log-schema.js\";\n\nconst OAC_DIRECTORY = \".oac\";\nconst CONTRIBUTIONS_DIRECTORY = \"contributions\";\nconst LEADERBOARD_FILENAME = \"leaderboard.json\";\nconst EMPTY_TIMESTAMP = \"\";\n\ninterface LeaderboardAccumulator {\n githubUsername: string;\n totalRuns: number;\n totalTasksCompleted: number;\n totalTokensDonated: number;\n totalFilesChanged: number;\n totalLinesChanged: number;\n totalPRsCreated: number;\n totalPRsMerged: number;\n firstContribution: string;\n lastContribution: string;\n sourceCounts: Map<TaskSource, number>;\n}\n\nexport interface Leaderboard {\n generatedAt: string;\n entries: LeaderboardEntry[];\n repoStats: {\n totalContributions: number;\n totalTokensUsed: number;\n totalPRsCreated: number;\n totalPRsMerged: number;\n firstContribution: string;\n lastContribution: string;\n };\n}\n\nexport interface LeaderboardEntry {\n githubUsername: string;\n totalRuns: number;\n totalTasksCompleted: number;\n totalTokensDonated: number;\n totalFilesChanged: number;\n totalLinesChanged: number;\n totalPRsCreated: number;\n totalPRsMerged: number;\n favoriteTaskSource: TaskSource;\n firstContribution: string;\n lastContribution: string;\n}\n\nexport async function buildLeaderboard(repoPath: string): Promise<Leaderboard> {\n const repoRoot = resolve(repoPath);\n const oacPath = join(repoRoot, OAC_DIRECTORY);\n const contributionsPath = join(oacPath, CONTRIBUTIONS_DIRECTORY);\n const logs = await readContributionLogs(contributionsPath);\n\n const aggregates = new Map<string, LeaderboardAccumulator>();\n const repoStats = {\n totalContributions: logs.length,\n totalTokensUsed: 0,\n totalPRsCreated: 0,\n totalPRsMerged: 0,\n firstContribution: EMPTY_TIMESTAMP,\n lastContribution: EMPTY_TIMESTAMP,\n };\n\n for (const log of logs) {\n const username = log.contributor.githubUsername;\n const accumulator = aggregates.get(username) ?? createAccumulator(username, log.timestamp);\n\n accumulator.totalRuns += 1;\n accumulator.totalTasksCompleted += log.tasks.filter((task) => task.status !== \"failed\").length;\n accumulator.totalTokensDonated += log.budget.totalTokensUsed;\n accumulator.totalFilesChanged += log.metrics.totalFilesChanged;\n accumulator.totalLinesChanged += log.metrics.totalLinesAdded + log.metrics.totalLinesRemoved;\n accumulator.firstContribution = minIsoTimestamp(accumulator.firstContribution, log.timestamp);\n accumulator.lastContribution = maxIsoTimestamp(accumulator.lastContribution, log.timestamp);\n\n for (const task of log.tasks) {\n if (task.pr) {\n accumulator.totalPRsCreated += 1;\n if (task.pr.status === \"merged\") {\n accumulator.totalPRsMerged += 1;\n }\n }\n\n accumulator.sourceCounts.set(\n task.source,\n (accumulator.sourceCounts.get(task.source) ?? 0) + 1,\n );\n }\n\n aggregates.set(username, accumulator);\n\n repoStats.totalTokensUsed += log.budget.totalTokensUsed;\n repoStats.firstContribution = minIsoTimestamp(repoStats.firstContribution, log.timestamp);\n repoStats.lastContribution = maxIsoTimestamp(repoStats.lastContribution, log.timestamp);\n repoStats.totalPRsCreated += log.tasks.filter((task) => Boolean(task.pr)).length;\n repoStats.totalPRsMerged += log.tasks.filter((task) => task.pr?.status === \"merged\").length;\n }\n\n const entries: LeaderboardEntry[] = Array.from(aggregates.values())\n .map((entry) => ({\n githubUsername: entry.githubUsername,\n totalRuns: entry.totalRuns,\n totalTasksCompleted: entry.totalTasksCompleted,\n totalTokensDonated: entry.totalTokensDonated,\n totalFilesChanged: entry.totalFilesChanged,\n totalLinesChanged: entry.totalLinesChanged,\n totalPRsCreated: entry.totalPRsCreated,\n totalPRsMerged: entry.totalPRsMerged,\n favoriteTaskSource: getFavoriteTaskSource(entry.sourceCounts),\n firstContribution: entry.firstContribution,\n lastContribution: entry.lastContribution,\n }))\n .sort((a, b) => {\n if (b.totalTasksCompleted !== a.totalTasksCompleted) {\n return b.totalTasksCompleted - a.totalTasksCompleted;\n }\n if (b.totalRuns !== a.totalRuns) {\n return b.totalRuns - a.totalRuns;\n }\n if (b.totalTokensDonated !== a.totalTokensDonated) {\n return b.totalTokensDonated - a.totalTokensDonated;\n }\n return a.githubUsername.localeCompare(b.githubUsername);\n });\n\n const leaderboard: Leaderboard = {\n generatedAt: new Date().toISOString(),\n entries,\n repoStats,\n };\n\n await mkdir(oacPath, { recursive: true });\n const leaderboardPath = join(oacPath, LEADERBOARD_FILENAME);\n const payload = `${JSON.stringify(leaderboard, null, 2)}\\n`;\n await writeFileAtomically(leaderboardPath, payload);\n\n return leaderboard;\n}\n\nfunction createAccumulator(\n githubUsername: string,\n initialTimestamp: string,\n): LeaderboardAccumulator {\n return {\n githubUsername,\n totalRuns: 0,\n totalTasksCompleted: 0,\n totalTokensDonated: 0,\n totalFilesChanged: 0,\n totalLinesChanged: 0,\n totalPRsCreated: 0,\n totalPRsMerged: 0,\n firstContribution: initialTimestamp,\n lastContribution: initialTimestamp,\n sourceCounts: new Map<TaskSource, number>(),\n };\n}\n\nasync function readContributionLogs(contributionsPath: string): Promise<ContributionLog[]> {\n let fileNames: string[];\n try {\n const entries = await readdir(contributionsPath, { withFileTypes: true, encoding: \"utf8\" });\n fileNames = entries\n .filter((entry) => entry.isFile() && entry.name.endsWith(\".json\"))\n .map((entry) => entry.name)\n .sort();\n } catch (error) {\n if (isFileNotFoundError(error)) {\n return [];\n }\n throw error;\n }\n\n const logs = await Promise.all(\n fileNames.map(async (fileName): Promise<ContributionLog | null> => {\n const filePath = join(contributionsPath, fileName);\n\n try {\n const fileContent = await readFile(filePath, \"utf8\");\n const parsedJson = JSON.parse(fileContent) as unknown;\n const parsedLog = contributionLogSchema.safeParse(parsedJson);\n\n if (!parsedLog.success) {\n console.warn(\n `[tracking] Skipping invalid contribution log \"${fileName}\": ${parsedLog.error.issues[0]?.message ?? \"Schema validation failed.\"}`,\n );\n return null;\n }\n\n return parsedLog.data;\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown read error\";\n console.warn(`[tracking] Skipping unreadable contribution log \"${fileName}\": ${message}`);\n return null;\n }\n }),\n );\n\n return logs.filter((log): log is ContributionLog => log !== null);\n}\n\nfunction getFavoriteTaskSource(sourceCounts: Map<TaskSource, number>): TaskSource {\n if (sourceCounts.size === 0) {\n return \"custom\";\n }\n\n let favorite: TaskSource = \"custom\";\n let favoriteCount = -1;\n\n for (const [source, count] of sourceCounts.entries()) {\n if (count > favoriteCount) {\n favorite = source;\n favoriteCount = count;\n continue;\n }\n\n if (count === favoriteCount && source.localeCompare(favorite) < 0) {\n favorite = source;\n }\n }\n\n return favorite;\n}\n\nfunction minIsoTimestamp(a: string, b: string): string {\n if (!a) {\n return b;\n }\n if (!b) {\n return a;\n }\n return Date.parse(a) <= Date.parse(b) ? a : b;\n}\n\nfunction maxIsoTimestamp(a: string, b: string): string {\n if (!a) {\n return b;\n }\n if (!b) {\n return a;\n }\n return Date.parse(a) >= Date.parse(b) ? a : b;\n}\n\nfunction isFileNotFoundError(error: unknown): boolean {\n if (typeof error !== \"object\" || error === null) {\n return false;\n }\n\n const code = (error as { code?: unknown }).code;\n return code === \"ENOENT\";\n}\n\nasync function writeFileAtomically(destinationPath: string, content: string): Promise<void> {\n const tempPath = `${destinationPath}.${process.pid}.${randomUUID()}.tmp`;\n\n try {\n await writeFile(tempPath, content, { encoding: \"utf8\", flag: \"wx\" });\n await rename(tempPath, destinationPath);\n } finally {\n await rm(tempPath, { force: true });\n }\n}\n"],"mappings":"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","names":["randomUUID","mkdir","rename","rm","writeFile","join","resolve","OAC_DIRECTORY","CONTRIBUTIONS_DIRECTORY","resolve","mkdir","join","writeFileAtomically","randomUUID","writeFile","rename","rm"]}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@open330/oac-tracking",
3
+ "version": "2026.2.17",
4
+ "description": "Contribution logging and leaderboard tracking for OAC",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/Open330/open-agent-contribution.git",
9
+ "directory": "packages/tracking"
10
+ },
11
+ "type": "module",
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": "./dist/index.js",
17
+ "types": "./dist/index.d.ts"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "engines": {
27
+ "node": ">=22.0.0"
28
+ },
29
+ "dependencies": {
30
+ "zod": "^3.24.2",
31
+ "@open330/oac-core": "2026.2.17"
32
+ },
33
+ "devDependencies": {
34
+ "tsup": "^8.3.6",
35
+ "typescript": "^5.7.3"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup src/index.ts --format esm --dts",
39
+ "test": "vitest run",
40
+ "typecheck": "tsc --noEmit"
41
+ }
42
+ }