@aida-dev/metrics 0.3.0 → 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.
- package/dist/index.d.ts +114 -25
- package/dist/index.js +80 -25
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
import { CommitStream, Commit } from '@aida-dev/core';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
+
declare const Attribution: z.ZodObject<{
|
|
5
|
+
commitsTotal: z.ZodNumber;
|
|
6
|
+
ai: z.ZodNumber;
|
|
7
|
+
human: z.ZodNumber;
|
|
8
|
+
unknown: z.ZodNumber;
|
|
9
|
+
coverage: z.ZodNumber;
|
|
10
|
+
defaultAttribution: z.ZodEnum<["ai", "human", "unknown"]>;
|
|
11
|
+
coverageThreshold: z.ZodNumber;
|
|
12
|
+
belowThreshold: z.ZodBoolean;
|
|
13
|
+
}, "strip", z.ZodTypeAny, {
|
|
14
|
+
ai: number;
|
|
15
|
+
human: number;
|
|
16
|
+
unknown: number;
|
|
17
|
+
commitsTotal: number;
|
|
18
|
+
coverage: number;
|
|
19
|
+
defaultAttribution: "ai" | "human" | "unknown";
|
|
20
|
+
coverageThreshold: number;
|
|
21
|
+
belowThreshold: boolean;
|
|
22
|
+
}, {
|
|
23
|
+
ai: number;
|
|
24
|
+
human: number;
|
|
25
|
+
unknown: number;
|
|
26
|
+
commitsTotal: number;
|
|
27
|
+
coverage: number;
|
|
28
|
+
defaultAttribution: "ai" | "human" | "unknown";
|
|
29
|
+
coverageThreshold: number;
|
|
30
|
+
belowThreshold: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
type Attribution = z.infer<typeof Attribution>;
|
|
4
33
|
declare const MergeRatio: z.ZodObject<{
|
|
5
34
|
aiCommitsTotal: z.ZodNumber;
|
|
6
35
|
aiCommitsMerged: z.ZodNumber;
|
|
@@ -67,27 +96,28 @@ declare const CohortMergeRatio: z.ZodObject<{
|
|
|
67
96
|
commitsMerged: z.ZodNumber;
|
|
68
97
|
mergeRatio: z.ZodNumber;
|
|
69
98
|
}, "strip", z.ZodTypeAny, {
|
|
70
|
-
mergeRatio: number;
|
|
71
99
|
commitsTotal: number;
|
|
100
|
+
mergeRatio: number;
|
|
72
101
|
commitsMerged: number;
|
|
73
102
|
}, {
|
|
74
|
-
mergeRatio: number;
|
|
75
103
|
commitsTotal: number;
|
|
104
|
+
mergeRatio: number;
|
|
76
105
|
commitsMerged: number;
|
|
77
106
|
}>;
|
|
78
107
|
type CohortMergeRatio = z.infer<typeof CohortMergeRatio>;
|
|
79
108
|
declare const Baseline: z.ZodObject<{
|
|
109
|
+
assumed: z.ZodBoolean;
|
|
80
110
|
mergeRatio: z.ZodObject<{
|
|
81
111
|
commitsTotal: z.ZodNumber;
|
|
82
112
|
commitsMerged: z.ZodNumber;
|
|
83
113
|
mergeRatio: z.ZodNumber;
|
|
84
114
|
}, "strip", z.ZodTypeAny, {
|
|
85
|
-
mergeRatio: number;
|
|
86
115
|
commitsTotal: number;
|
|
116
|
+
mergeRatio: number;
|
|
87
117
|
commitsMerged: number;
|
|
88
118
|
}, {
|
|
89
|
-
mergeRatio: number;
|
|
90
119
|
commitsTotal: number;
|
|
120
|
+
mergeRatio: number;
|
|
91
121
|
commitsMerged: number;
|
|
92
122
|
}>;
|
|
93
123
|
persistence: z.ZodObject<{
|
|
@@ -138,8 +168,8 @@ declare const Baseline: z.ZodObject<{
|
|
|
138
168
|
}>;
|
|
139
169
|
}, "strip", z.ZodTypeAny, {
|
|
140
170
|
mergeRatio: {
|
|
141
|
-
mergeRatio: number;
|
|
142
171
|
commitsTotal: number;
|
|
172
|
+
mergeRatio: number;
|
|
143
173
|
commitsMerged: number;
|
|
144
174
|
};
|
|
145
175
|
persistence: {
|
|
@@ -154,10 +184,11 @@ declare const Baseline: z.ZodObject<{
|
|
|
154
184
|
d90_plus: number;
|
|
155
185
|
};
|
|
156
186
|
};
|
|
187
|
+
assumed: boolean;
|
|
157
188
|
}, {
|
|
158
189
|
mergeRatio: {
|
|
159
|
-
mergeRatio: number;
|
|
160
190
|
commitsTotal: number;
|
|
191
|
+
mergeRatio: number;
|
|
161
192
|
commitsMerged: number;
|
|
162
193
|
};
|
|
163
194
|
persistence: {
|
|
@@ -172,6 +203,7 @@ declare const Baseline: z.ZodObject<{
|
|
|
172
203
|
d90_plus: number;
|
|
173
204
|
};
|
|
174
205
|
};
|
|
206
|
+
assumed: boolean;
|
|
175
207
|
}>;
|
|
176
208
|
type Baseline = z.infer<typeof Baseline>;
|
|
177
209
|
declare const Delta: z.ZodObject<{
|
|
@@ -202,6 +234,34 @@ declare const Metrics: z.ZodObject<{
|
|
|
202
234
|
}>;
|
|
203
235
|
repoPath: z.ZodString;
|
|
204
236
|
defaultBranch: z.ZodString;
|
|
237
|
+
attribution: z.ZodObject<{
|
|
238
|
+
commitsTotal: z.ZodNumber;
|
|
239
|
+
ai: z.ZodNumber;
|
|
240
|
+
human: z.ZodNumber;
|
|
241
|
+
unknown: z.ZodNumber;
|
|
242
|
+
coverage: z.ZodNumber;
|
|
243
|
+
defaultAttribution: z.ZodEnum<["ai", "human", "unknown"]>;
|
|
244
|
+
coverageThreshold: z.ZodNumber;
|
|
245
|
+
belowThreshold: z.ZodBoolean;
|
|
246
|
+
}, "strip", z.ZodTypeAny, {
|
|
247
|
+
ai: number;
|
|
248
|
+
human: number;
|
|
249
|
+
unknown: number;
|
|
250
|
+
commitsTotal: number;
|
|
251
|
+
coverage: number;
|
|
252
|
+
defaultAttribution: "ai" | "human" | "unknown";
|
|
253
|
+
coverageThreshold: number;
|
|
254
|
+
belowThreshold: boolean;
|
|
255
|
+
}, {
|
|
256
|
+
ai: number;
|
|
257
|
+
human: number;
|
|
258
|
+
unknown: number;
|
|
259
|
+
commitsTotal: number;
|
|
260
|
+
coverage: number;
|
|
261
|
+
defaultAttribution: "ai" | "human" | "unknown";
|
|
262
|
+
coverageThreshold: number;
|
|
263
|
+
belowThreshold: boolean;
|
|
264
|
+
}>;
|
|
205
265
|
mergeRatio: z.ZodObject<{
|
|
206
266
|
aiCommitsTotal: z.ZodNumber;
|
|
207
267
|
aiCommitsMerged: z.ZodNumber;
|
|
@@ -261,18 +321,19 @@ declare const Metrics: z.ZodObject<{
|
|
|
261
321
|
d90_plus: number;
|
|
262
322
|
};
|
|
263
323
|
}>;
|
|
264
|
-
baseline: z.ZodObject<{
|
|
324
|
+
baseline: z.ZodNullable<z.ZodObject<{
|
|
325
|
+
assumed: z.ZodBoolean;
|
|
265
326
|
mergeRatio: z.ZodObject<{
|
|
266
327
|
commitsTotal: z.ZodNumber;
|
|
267
328
|
commitsMerged: z.ZodNumber;
|
|
268
329
|
mergeRatio: z.ZodNumber;
|
|
269
330
|
}, "strip", z.ZodTypeAny, {
|
|
270
|
-
mergeRatio: number;
|
|
271
331
|
commitsTotal: number;
|
|
332
|
+
mergeRatio: number;
|
|
272
333
|
commitsMerged: number;
|
|
273
334
|
}, {
|
|
274
|
-
mergeRatio: number;
|
|
275
335
|
commitsTotal: number;
|
|
336
|
+
mergeRatio: number;
|
|
276
337
|
commitsMerged: number;
|
|
277
338
|
}>;
|
|
278
339
|
persistence: z.ZodObject<{
|
|
@@ -323,8 +384,8 @@ declare const Metrics: z.ZodObject<{
|
|
|
323
384
|
}>;
|
|
324
385
|
}, "strip", z.ZodTypeAny, {
|
|
325
386
|
mergeRatio: {
|
|
326
|
-
mergeRatio: number;
|
|
327
387
|
commitsTotal: number;
|
|
388
|
+
mergeRatio: number;
|
|
328
389
|
commitsMerged: number;
|
|
329
390
|
};
|
|
330
391
|
persistence: {
|
|
@@ -339,10 +400,11 @@ declare const Metrics: z.ZodObject<{
|
|
|
339
400
|
d90_plus: number;
|
|
340
401
|
};
|
|
341
402
|
};
|
|
403
|
+
assumed: boolean;
|
|
342
404
|
}, {
|
|
343
405
|
mergeRatio: {
|
|
344
|
-
mergeRatio: number;
|
|
345
406
|
commitsTotal: number;
|
|
407
|
+
mergeRatio: number;
|
|
346
408
|
commitsMerged: number;
|
|
347
409
|
};
|
|
348
410
|
persistence: {
|
|
@@ -357,8 +419,9 @@ declare const Metrics: z.ZodObject<{
|
|
|
357
419
|
d90_plus: number;
|
|
358
420
|
};
|
|
359
421
|
};
|
|
360
|
-
|
|
361
|
-
|
|
422
|
+
assumed: boolean;
|
|
423
|
+
}>>;
|
|
424
|
+
delta: z.ZodNullable<z.ZodObject<{
|
|
362
425
|
mergeRatio: z.ZodNumber;
|
|
363
426
|
avgPersistenceDays: z.ZodNumber;
|
|
364
427
|
medianPersistenceDays: z.ZodNumber;
|
|
@@ -370,7 +433,7 @@ declare const Metrics: z.ZodObject<{
|
|
|
370
433
|
mergeRatio: number;
|
|
371
434
|
avgPersistenceDays: number;
|
|
372
435
|
medianPersistenceDays: number;
|
|
373
|
-
}
|
|
436
|
+
}>>;
|
|
374
437
|
caveats: z.ZodArray<z.ZodString, "many">;
|
|
375
438
|
}, "strip", z.ZodTypeAny, {
|
|
376
439
|
generatedAt: string;
|
|
@@ -380,6 +443,16 @@ declare const Metrics: z.ZodObject<{
|
|
|
380
443
|
};
|
|
381
444
|
repoPath: string;
|
|
382
445
|
defaultBranch: string;
|
|
446
|
+
attribution: {
|
|
447
|
+
ai: number;
|
|
448
|
+
human: number;
|
|
449
|
+
unknown: number;
|
|
450
|
+
commitsTotal: number;
|
|
451
|
+
coverage: number;
|
|
452
|
+
defaultAttribution: "ai" | "human" | "unknown";
|
|
453
|
+
coverageThreshold: number;
|
|
454
|
+
belowThreshold: boolean;
|
|
455
|
+
};
|
|
383
456
|
mergeRatio: {
|
|
384
457
|
aiCommitsTotal: number;
|
|
385
458
|
aiCommitsMerged: number;
|
|
@@ -399,8 +472,8 @@ declare const Metrics: z.ZodObject<{
|
|
|
399
472
|
};
|
|
400
473
|
baseline: {
|
|
401
474
|
mergeRatio: {
|
|
402
|
-
mergeRatio: number;
|
|
403
475
|
commitsTotal: number;
|
|
476
|
+
mergeRatio: number;
|
|
404
477
|
commitsMerged: number;
|
|
405
478
|
};
|
|
406
479
|
persistence: {
|
|
@@ -415,12 +488,13 @@ declare const Metrics: z.ZodObject<{
|
|
|
415
488
|
d90_plus: number;
|
|
416
489
|
};
|
|
417
490
|
};
|
|
418
|
-
|
|
491
|
+
assumed: boolean;
|
|
492
|
+
} | null;
|
|
419
493
|
delta: {
|
|
420
494
|
mergeRatio: number;
|
|
421
495
|
avgPersistenceDays: number;
|
|
422
496
|
medianPersistenceDays: number;
|
|
423
|
-
};
|
|
497
|
+
} | null;
|
|
424
498
|
caveats: string[];
|
|
425
499
|
}, {
|
|
426
500
|
generatedAt: string;
|
|
@@ -430,6 +504,16 @@ declare const Metrics: z.ZodObject<{
|
|
|
430
504
|
};
|
|
431
505
|
repoPath: string;
|
|
432
506
|
defaultBranch: string;
|
|
507
|
+
attribution: {
|
|
508
|
+
ai: number;
|
|
509
|
+
human: number;
|
|
510
|
+
unknown: number;
|
|
511
|
+
commitsTotal: number;
|
|
512
|
+
coverage: number;
|
|
513
|
+
defaultAttribution: "ai" | "human" | "unknown";
|
|
514
|
+
coverageThreshold: number;
|
|
515
|
+
belowThreshold: boolean;
|
|
516
|
+
};
|
|
433
517
|
mergeRatio: {
|
|
434
518
|
aiCommitsTotal: number;
|
|
435
519
|
aiCommitsMerged: number;
|
|
@@ -449,8 +533,8 @@ declare const Metrics: z.ZodObject<{
|
|
|
449
533
|
};
|
|
450
534
|
baseline: {
|
|
451
535
|
mergeRatio: {
|
|
452
|
-
mergeRatio: number;
|
|
453
536
|
commitsTotal: number;
|
|
537
|
+
mergeRatio: number;
|
|
454
538
|
commitsMerged: number;
|
|
455
539
|
};
|
|
456
540
|
persistence: {
|
|
@@ -465,22 +549,27 @@ declare const Metrics: z.ZodObject<{
|
|
|
465
549
|
d90_plus: number;
|
|
466
550
|
};
|
|
467
551
|
};
|
|
468
|
-
|
|
552
|
+
assumed: boolean;
|
|
553
|
+
} | null;
|
|
469
554
|
delta: {
|
|
470
555
|
mergeRatio: number;
|
|
471
556
|
avgPersistenceDays: number;
|
|
472
557
|
medianPersistenceDays: number;
|
|
473
|
-
};
|
|
558
|
+
} | null;
|
|
474
559
|
caveats: string[];
|
|
475
560
|
}>;
|
|
476
561
|
type Metrics = z.infer<typeof Metrics>;
|
|
477
562
|
|
|
478
|
-
declare function calculateMergeRatio(commitStream: CommitStream): MergeRatio;
|
|
479
|
-
declare function calculateBaselineMergeRatio(commitStream: CommitStream): CohortMergeRatio;
|
|
563
|
+
declare function calculateMergeRatio(commitStream: CommitStream, isTarget?: (commit: Commit) => boolean): MergeRatio;
|
|
564
|
+
declare function calculateBaselineMergeRatio(commitStream: CommitStream, isTarget?: (commit: Commit) => boolean): CohortMergeRatio;
|
|
480
565
|
|
|
481
566
|
declare function calculatePersistence(commitStream: CommitStream, isTarget?: (commit: Commit) => boolean): Persistence;
|
|
482
|
-
declare function calculateBaselinePersistence(commitStream: CommitStream): Persistence;
|
|
567
|
+
declare function calculateBaselinePersistence(commitStream: CommitStream, isTarget?: (commit: Commit) => boolean): Persistence;
|
|
483
568
|
|
|
484
|
-
|
|
569
|
+
interface MetricsOptions {
|
|
570
|
+
defaultAttribution?: 'ai' | 'human' | 'unknown';
|
|
571
|
+
coverageThreshold?: number;
|
|
572
|
+
}
|
|
573
|
+
declare function calculateMetrics(commitStream: CommitStream, options?: MetricsOptions): Metrics;
|
|
485
574
|
|
|
486
|
-
export { Baseline, CohortMergeRatio, Delta, MergeRatio, Metrics, Persistence, calculateBaselineMergeRatio, calculateBaselinePersistence, calculateMergeRatio, calculateMetrics, calculatePersistence };
|
|
575
|
+
export { Attribution, Baseline, CohortMergeRatio, Delta, MergeRatio, Metrics, type MetricsOptions, Persistence, calculateBaselineMergeRatio, calculateBaselinePersistence, calculateMergeRatio, calculateMetrics, calculatePersistence };
|
package/dist/index.js
CHANGED
|
@@ -10,8 +10,8 @@ function computeMergeRatio(commits) {
|
|
|
10
10
|
mergeRatio: commits.length > 0 ? merged.length / commits.length : 0
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
-
function calculateMergeRatio(commitStream) {
|
|
14
|
-
const aiCommits = commitStream.commits.filter(
|
|
13
|
+
function calculateMergeRatio(commitStream, isTarget = (commit) => commit.tags.attribution === "ai") {
|
|
14
|
+
const aiCommits = commitStream.commits.filter(isTarget);
|
|
15
15
|
const result = computeMergeRatio(aiCommits);
|
|
16
16
|
return {
|
|
17
17
|
aiCommitsTotal: result.commitsTotal,
|
|
@@ -19,14 +19,14 @@ function calculateMergeRatio(commitStream) {
|
|
|
19
19
|
mergeRatio: result.mergeRatio
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
|
-
function calculateBaselineMergeRatio(commitStream) {
|
|
23
|
-
const
|
|
24
|
-
return computeMergeRatio(
|
|
22
|
+
function calculateBaselineMergeRatio(commitStream, isTarget = (commit) => commit.tags.attribution === "human") {
|
|
23
|
+
const baselineCommits = commitStream.commits.filter(isTarget);
|
|
24
|
+
return computeMergeRatio(baselineCommits);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// src/persistence.ts
|
|
28
28
|
import { daysBetween } from "@aida-dev/core";
|
|
29
|
-
function calculatePersistence(commitStream, isTarget = (commit) => commit.tags.ai) {
|
|
29
|
+
function calculatePersistence(commitStream, isTarget = (commit) => commit.tags.attribution === "ai") {
|
|
30
30
|
const targetCommits = commitStream.commits.filter(isTarget);
|
|
31
31
|
if (targetCommits.length === 0) {
|
|
32
32
|
return {
|
|
@@ -95,12 +95,24 @@ function calculatePersistence(commitStream, isTarget = (commit) => commit.tags.a
|
|
|
95
95
|
buckets
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
|
-
function calculateBaselinePersistence(commitStream) {
|
|
99
|
-
return calculatePersistence(commitStream,
|
|
98
|
+
function calculateBaselinePersistence(commitStream, isTarget = (commit) => commit.tags.attribution === "human") {
|
|
99
|
+
return calculatePersistence(commitStream, isTarget);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// src/schema/metrics.ts
|
|
103
103
|
import { z } from "zod";
|
|
104
|
+
var Attribution = z.object({
|
|
105
|
+
commitsTotal: z.number().int().nonnegative(),
|
|
106
|
+
ai: z.number().int().nonnegative(),
|
|
107
|
+
human: z.number().int().nonnegative(),
|
|
108
|
+
unknown: z.number().int().nonnegative(),
|
|
109
|
+
coverage: z.number().min(0).max(1),
|
|
110
|
+
// (ai + human) / total
|
|
111
|
+
defaultAttribution: z.enum(["ai", "human", "unknown"]),
|
|
112
|
+
// prior applied to unknown commits
|
|
113
|
+
coverageThreshold: z.number().min(0).max(1),
|
|
114
|
+
belowThreshold: z.boolean()
|
|
115
|
+
});
|
|
104
116
|
var MergeRatio = z.object({
|
|
105
117
|
aiCommitsTotal: z.number().int().nonnegative(),
|
|
106
118
|
aiCommitsMerged: z.number().int().nonnegative(),
|
|
@@ -124,6 +136,9 @@ var CohortMergeRatio = z.object({
|
|
|
124
136
|
mergeRatio: z.number().min(0).max(1)
|
|
125
137
|
});
|
|
126
138
|
var Baseline = z.object({
|
|
139
|
+
// True when the cohort includes 'unknown' commits via defaultAttribution:
|
|
140
|
+
// the baseline is an assumption, not observed attribution.
|
|
141
|
+
assumed: z.boolean(),
|
|
127
142
|
mergeRatio: CohortMergeRatio,
|
|
128
143
|
persistence: Persistence
|
|
129
144
|
});
|
|
@@ -140,10 +155,13 @@ var Metrics = z.object({
|
|
|
140
155
|
}),
|
|
141
156
|
repoPath: z.string(),
|
|
142
157
|
defaultBranch: z.string(),
|
|
158
|
+
attribution: Attribution,
|
|
143
159
|
mergeRatio: MergeRatio,
|
|
144
160
|
persistence: Persistence,
|
|
145
|
-
|
|
146
|
-
|
|
161
|
+
// Null when no commit is attributed 'human' and no defaultAttribution prior
|
|
162
|
+
// assigns the unknowns: AIDA does not invent a comparison cohort.
|
|
163
|
+
baseline: Baseline.nullable(),
|
|
164
|
+
delta: Delta.nullable(),
|
|
147
165
|
caveats: z.array(z.string())
|
|
148
166
|
});
|
|
149
167
|
|
|
@@ -152,22 +170,60 @@ function round(value, decimals) {
|
|
|
152
170
|
const factor = 10 ** decimals;
|
|
153
171
|
return Math.round(value * factor) / factor;
|
|
154
172
|
}
|
|
155
|
-
function calculateMetrics(commitStream) {
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
173
|
+
function calculateMetrics(commitStream, options = {}) {
|
|
174
|
+
const { defaultAttribution = "unknown", coverageThreshold = 0.7 } = options;
|
|
175
|
+
const counts = { ai: 0, human: 0, unknown: 0 };
|
|
176
|
+
for (const commit of commitStream.commits) {
|
|
177
|
+
counts[commit.tags.attribution]++;
|
|
178
|
+
}
|
|
179
|
+
const total = commitStream.commits.length;
|
|
180
|
+
const coverage = total > 0 ? (counts.ai + counts.human) / total : 0;
|
|
181
|
+
const attribution = {
|
|
182
|
+
commitsTotal: total,
|
|
183
|
+
ai: counts.ai,
|
|
184
|
+
human: counts.human,
|
|
185
|
+
unknown: counts.unknown,
|
|
186
|
+
coverage: round(coverage, 4),
|
|
187
|
+
defaultAttribution,
|
|
188
|
+
coverageThreshold,
|
|
189
|
+
belowThreshold: coverage < coverageThreshold
|
|
164
190
|
};
|
|
191
|
+
const isAI = (commit) => commit.tags.attribution === "ai" || defaultAttribution === "ai" && commit.tags.attribution === "unknown";
|
|
192
|
+
const isBaseline = (commit) => commit.tags.attribution === "human" || defaultAttribution === "human" && commit.tags.attribution === "unknown";
|
|
193
|
+
const mergeRatio = calculateMergeRatio(commitStream, isAI);
|
|
194
|
+
const persistence = calculatePersistence(commitStream, isAI);
|
|
195
|
+
const baselineSize = commitStream.commits.filter(isBaseline).length;
|
|
196
|
+
const baselineAssumed = defaultAttribution === "human" && counts.unknown > 0;
|
|
197
|
+
const baseline = baselineSize > 0 ? {
|
|
198
|
+
assumed: baselineAssumed,
|
|
199
|
+
mergeRatio: calculateBaselineMergeRatio(commitStream, isBaseline),
|
|
200
|
+
persistence: calculateBaselinePersistence(commitStream, isBaseline)
|
|
201
|
+
} : null;
|
|
202
|
+
const delta = baseline ? {
|
|
203
|
+
mergeRatio: round(mergeRatio.mergeRatio - baseline.mergeRatio.mergeRatio, 4),
|
|
204
|
+
avgPersistenceDays: round(persistence.avgDays - baseline.persistence.avgDays, 2),
|
|
205
|
+
medianPersistenceDays: round(
|
|
206
|
+
persistence.medianDays - baseline.persistence.medianDays,
|
|
207
|
+
2
|
|
208
|
+
)
|
|
209
|
+
} : null;
|
|
165
210
|
const caveats = [
|
|
211
|
+
`Attribution coverage is ${(coverage * 100).toFixed(1)}%: metrics only describe commits whose provenance is known.`,
|
|
166
212
|
"Persistence is file-level, not line-level.",
|
|
167
213
|
"Merge ratio: commits from all branches checked against default branch ancestry. Squash merges may undercount unmerged commits.",
|
|
168
|
-
"
|
|
169
|
-
"
|
|
214
|
+
"Time-windowed collection (--since) also windows the ancestry check: commits merged into the default branch before the window may appear unmerged.",
|
|
215
|
+
"AI tagging uses heuristic patterns; false positives/negatives possible."
|
|
170
216
|
];
|
|
217
|
+
if (baseline?.assumed) {
|
|
218
|
+
caveats.push(
|
|
219
|
+
`Baseline includes ${counts.unknown} unattributed commit(s) assumed human via defaultAttribution \u2014 undetected AI usage may leak into it.`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
if (!baseline) {
|
|
223
|
+
caveats.push(
|
|
224
|
+
'No baseline: no commits are attributed as human. Set defaultAttribution to "human" in .aida.json if unattributed commits in this repo are human-authored.'
|
|
225
|
+
);
|
|
226
|
+
}
|
|
171
227
|
return {
|
|
172
228
|
generatedAt: formatISODate(/* @__PURE__ */ new Date()),
|
|
173
229
|
window: {
|
|
@@ -176,17 +232,16 @@ function calculateMetrics(commitStream) {
|
|
|
176
232
|
},
|
|
177
233
|
repoPath: commitStream.repoPath,
|
|
178
234
|
defaultBranch: commitStream.defaultBranch,
|
|
235
|
+
attribution,
|
|
179
236
|
mergeRatio,
|
|
180
237
|
persistence,
|
|
181
|
-
baseline
|
|
182
|
-
mergeRatio: baselineMergeRatio,
|
|
183
|
-
persistence: baselinePersistence
|
|
184
|
-
},
|
|
238
|
+
baseline,
|
|
185
239
|
delta,
|
|
186
240
|
caveats
|
|
187
241
|
};
|
|
188
242
|
}
|
|
189
243
|
export {
|
|
244
|
+
Attribution,
|
|
190
245
|
Baseline,
|
|
191
246
|
CohortMergeRatio,
|
|
192
247
|
Delta,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/merge-ratio.ts","../src/persistence.ts","../src/schema/metrics.ts"],"sourcesContent":["import { CommitStream, formatISODate } from '@aida-dev/core';\nimport { calculateBaselineMergeRatio, calculateMergeRatio } from './merge-ratio.js';\nimport { calculateBaselinePersistence, calculatePersistence } from './persistence.js';\nimport { Metrics } from './schema/metrics.js';\n\nexport * from './schema/metrics.js';\nexport * from './merge-ratio.js';\nexport * from './persistence.js';\n\nfunction round(value: number, decimals: number): number {\n const factor = 10 ** decimals;\n return Math.round(value * factor) / factor;\n}\n\nexport function calculateMetrics(commitStream: CommitStream): Metrics {\n const mergeRatio = calculateMergeRatio(commitStream);\n const persistence = calculatePersistence(commitStream);\n const baselineMergeRatio = calculateBaselineMergeRatio(commitStream);\n const baselinePersistence = calculateBaselinePersistence(commitStream);\n\n const delta = {\n mergeRatio: round(mergeRatio.mergeRatio - baselineMergeRatio.mergeRatio, 4),\n avgPersistenceDays: round(persistence.avgDays - baselinePersistence.avgDays, 2),\n medianPersistenceDays: round(persistence.medianDays - baselinePersistence.medianDays, 2),\n };\n\n const caveats = [\n 'Persistence is file-level, not line-level.',\n 'Merge ratio: commits from all branches checked against default branch ancestry. Squash merges may undercount unmerged commits.',\n 'AI tagging uses heuristic patterns; false positives/negatives possible.',\n 'Baseline covers all non-AI-tagged commits; undetected AI usage may leak into the baseline.',\n ];\n\n return {\n generatedAt: formatISODate(new Date()),\n window: {\n since: commitStream.since,\n until: commitStream.until,\n },\n repoPath: commitStream.repoPath,\n defaultBranch: commitStream.defaultBranch,\n mergeRatio,\n persistence,\n baseline: {\n mergeRatio: baselineMergeRatio,\n persistence: baselinePersistence,\n },\n delta,\n caveats,\n };\n}\n","import { Commit, CommitStream } from '@aida-dev/core';\nimport { CohortMergeRatio, MergeRatio } from './schema/metrics.js';\n\nfunction computeMergeRatio(commits: Commit[]): CohortMergeRatio {\n const merged = commits.filter((commit) => commit.inDefaultBranchAncestry);\n\n return {\n commitsTotal: commits.length,\n commitsMerged: merged.length,\n mergeRatio: commits.length > 0 ? merged.length / commits.length : 0,\n };\n}\n\nexport function calculateMergeRatio(commitStream: CommitStream): MergeRatio {\n const aiCommits = commitStream.commits.filter((commit) => commit.tags.ai);\n const result = computeMergeRatio(aiCommits);\n\n return {\n aiCommitsTotal: result.commitsTotal,\n aiCommitsMerged: result.commitsMerged,\n mergeRatio: result.mergeRatio,\n };\n}\n\nexport function calculateBaselineMergeRatio(commitStream: CommitStream): CohortMergeRatio {\n const nonAICommits = commitStream.commits.filter((commit) => !commit.tags.ai);\n return computeMergeRatio(nonAICommits);\n}\n","import { Commit, CommitStream, daysBetween } from '@aida-dev/core';\nimport { Persistence } from './schema/metrics.js';\n\ninterface FileLifecycle {\n path: string;\n firstTargetCommitDate: Date;\n lastSeenDate: Date;\n persistenceDays: number;\n}\n\nexport function calculatePersistence(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.ai\n): Persistence {\n const targetCommits = commitStream.commits.filter(isTarget);\n\n if (targetCommits.length === 0) {\n return {\n commitsConsidered: 0,\n avgDays: 0,\n medianDays: 0,\n buckets: {\n d0_1: 0,\n d2_7: 0,\n d8_30: 0,\n d31_90: 0,\n d90_plus: 0,\n },\n };\n }\n\n // Group files by path and track their lifecycle\n const fileLifecycles = new Map<string, FileLifecycle>();\n\n // Sort commits by date (oldest first)\n const sortedCommits = [...commitStream.commits].sort(\n (a, b) => new Date(a.authorDate).getTime() - new Date(b.authorDate).getTime()\n );\n\n // Track first target commit per file\n for (const commit of sortedCommits) {\n if (isTarget(commit)) {\n const commitDate = new Date(commit.authorDate);\n\n for (const file of commit.stats.files) {\n if (!fileLifecycles.has(file.path)) {\n fileLifecycles.set(file.path, {\n path: file.path,\n firstTargetCommitDate: commitDate,\n lastSeenDate: commitDate,\n persistenceDays: 0,\n });\n }\n }\n }\n }\n\n // Track last seen date for each file\n for (const commit of sortedCommits) {\n const commitDate = new Date(commit.authorDate);\n\n for (const file of commit.stats.files) {\n const lifecycle = fileLifecycles.get(file.path);\n if (lifecycle) {\n // Update last seen date if file is not deleted\n if (file.status !== 'deleted') {\n lifecycle.lastSeenDate = commitDate;\n }\n }\n }\n }\n\n // Calculate persistence days for each file\n const persistenceDays: number[] = [];\n\n for (const lifecycle of fileLifecycles.values()) {\n lifecycle.persistenceDays = daysBetween(lifecycle.firstTargetCommitDate, lifecycle.lastSeenDate);\n persistenceDays.push(lifecycle.persistenceDays);\n }\n\n // Calculate statistics\n const avgDays =\n persistenceDays.length > 0\n ? persistenceDays.reduce((sum, days) => sum + days, 0) / persistenceDays.length\n : 0;\n\n const sortedDays = [...persistenceDays].sort((a, b) => a - b);\n const medianDays =\n sortedDays.length > 0\n ? sortedDays.length % 2 === 0\n ? (sortedDays[sortedDays.length / 2 - 1] + sortedDays[sortedDays.length / 2]) / 2\n : sortedDays[Math.floor(sortedDays.length / 2)]\n : 0;\n\n // Calculate buckets\n const buckets = {\n d0_1: persistenceDays.filter((days) => days <= 1).length,\n d2_7: persistenceDays.filter((days) => days >= 2 && days <= 7).length,\n d8_30: persistenceDays.filter((days) => days >= 8 && days <= 30).length,\n d31_90: persistenceDays.filter((days) => days >= 31 && days <= 90).length,\n d90_plus: persistenceDays.filter((days) => days > 90).length,\n };\n\n return {\n commitsConsidered: targetCommits.length,\n avgDays: Math.round(avgDays * 100) / 100, // Round to 2 decimal places\n medianDays: Math.round(medianDays * 100) / 100,\n buckets,\n };\n}\n\nexport function calculateBaselinePersistence(commitStream: CommitStream): Persistence {\n return calculatePersistence(commitStream, (commit) => !commit.tags.ai);\n}\n","import { z } from 'zod';\n\nexport const MergeRatio = z.object({\n aiCommitsTotal: z.number().int().nonnegative(),\n aiCommitsMerged: z.number().int().nonnegative(),\n mergeRatio: z.number().min(0).max(1),\n});\n\nexport const Persistence = z.object({\n commitsConsidered: z.number().int().nonnegative(),\n avgDays: z.number().nonnegative(),\n medianDays: z.number().nonnegative(),\n buckets: z.object({\n d0_1: z.number().int().nonnegative(),\n d2_7: z.number().int().nonnegative(),\n d8_30: z.number().int().nonnegative(),\n d31_90: z.number().int().nonnegative(),\n d90_plus: z.number().int().nonnegative(),\n }),\n});\n\nexport const CohortMergeRatio = z.object({\n commitsTotal: z.number().int().nonnegative(),\n commitsMerged: z.number().int().nonnegative(),\n mergeRatio: z.number().min(0).max(1),\n});\n\nexport const Baseline = z.object({\n mergeRatio: CohortMergeRatio,\n persistence: Persistence,\n});\n\nexport const Delta = z.object({\n mergeRatio: z.number(),\n avgPersistenceDays: z.number(),\n medianPersistenceDays: z.number(),\n});\n\nexport const Metrics = z.object({\n generatedAt: z.string().datetime(),\n window: z.object({\n since: z.string().optional(),\n until: z.string().optional(),\n }),\n repoPath: z.string(),\n defaultBranch: z.string(),\n mergeRatio: MergeRatio,\n persistence: Persistence,\n baseline: Baseline,\n delta: Delta,\n caveats: z.array(z.string()),\n});\n\nexport type MergeRatio = z.infer<typeof MergeRatio>;\nexport type Persistence = z.infer<typeof Persistence>;\nexport type CohortMergeRatio = z.infer<typeof CohortMergeRatio>;\nexport type Baseline = z.infer<typeof Baseline>;\nexport type Delta = z.infer<typeof Delta>;\nexport type Metrics = z.infer<typeof Metrics>;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/merge-ratio.ts","../src/persistence.ts","../src/schema/metrics.ts"],"sourcesContent":["import { Commit, CommitStream, formatISODate } from '@aida-dev/core';\nimport { calculateBaselineMergeRatio, calculateMergeRatio } from './merge-ratio.js';\nimport { calculateBaselinePersistence, calculatePersistence } from './persistence.js';\nimport { Attribution, Metrics } from './schema/metrics.js';\n\nexport * from './schema/metrics.js';\nexport * from './merge-ratio.js';\nexport * from './persistence.js';\n\nexport interface MetricsOptions {\n // Prior applied to 'unknown' commits: which cohort (if any) they join.\n defaultAttribution?: 'ai' | 'human' | 'unknown';\n coverageThreshold?: number;\n}\n\nfunction round(value: number, decimals: number): number {\n const factor = 10 ** decimals;\n return Math.round(value * factor) / factor;\n}\n\nexport function calculateMetrics(\n commitStream: CommitStream,\n options: MetricsOptions = {}\n): Metrics {\n const { defaultAttribution = 'unknown', coverageThreshold = 0.7 } = options;\n\n const counts = { ai: 0, human: 0, unknown: 0 };\n for (const commit of commitStream.commits) {\n counts[commit.tags.attribution]++;\n }\n const total = commitStream.commits.length;\n const coverage = total > 0 ? (counts.ai + counts.human) / total : 0;\n\n const attribution: Attribution = {\n commitsTotal: total,\n ai: counts.ai,\n human: counts.human,\n unknown: counts.unknown,\n coverage: round(coverage, 4),\n defaultAttribution,\n coverageThreshold,\n belowThreshold: coverage < coverageThreshold,\n };\n\n // Cohort membership: unknown commits join a cohort only via the prior.\n const isAI = (commit: Commit) =>\n commit.tags.attribution === 'ai' ||\n (defaultAttribution === 'ai' && commit.tags.attribution === 'unknown');\n const isBaseline = (commit: Commit) =>\n commit.tags.attribution === 'human' ||\n (defaultAttribution === 'human' && commit.tags.attribution === 'unknown');\n\n const mergeRatio = calculateMergeRatio(commitStream, isAI);\n const persistence = calculatePersistence(commitStream, isAI);\n\n // No baseline cohort → no baseline, no delta. AIDA does not invent a\n // comparison out of unattributed commits.\n const baselineSize = commitStream.commits.filter(isBaseline).length;\n const baselineAssumed = defaultAttribution === 'human' && counts.unknown > 0;\n\n const baseline =\n baselineSize > 0\n ? {\n assumed: baselineAssumed,\n mergeRatio: calculateBaselineMergeRatio(commitStream, isBaseline),\n persistence: calculateBaselinePersistence(commitStream, isBaseline),\n }\n : null;\n\n const delta = baseline\n ? {\n mergeRatio: round(mergeRatio.mergeRatio - baseline.mergeRatio.mergeRatio, 4),\n avgPersistenceDays: round(persistence.avgDays - baseline.persistence.avgDays, 2),\n medianPersistenceDays: round(\n persistence.medianDays - baseline.persistence.medianDays,\n 2\n ),\n }\n : null;\n\n const caveats = [\n `Attribution coverage is ${(coverage * 100).toFixed(1)}%: metrics only describe commits whose provenance is known.`,\n 'Persistence is file-level, not line-level.',\n 'Merge ratio: commits from all branches checked against default branch ancestry. Squash merges may undercount unmerged commits.',\n 'Time-windowed collection (--since) also windows the ancestry check: commits merged into the default branch before the window may appear unmerged.',\n 'AI tagging uses heuristic patterns; false positives/negatives possible.',\n ];\n if (baseline?.assumed) {\n caveats.push(\n `Baseline includes ${counts.unknown} unattributed commit(s) assumed human via defaultAttribution — undetected AI usage may leak into it.`\n );\n }\n if (!baseline) {\n caveats.push(\n 'No baseline: no commits are attributed as human. Set defaultAttribution to \"human\" in .aida.json if unattributed commits in this repo are human-authored.'\n );\n }\n\n return {\n generatedAt: formatISODate(new Date()),\n window: {\n since: commitStream.since,\n until: commitStream.until,\n },\n repoPath: commitStream.repoPath,\n defaultBranch: commitStream.defaultBranch,\n attribution,\n mergeRatio,\n persistence,\n baseline,\n delta,\n caveats,\n };\n}\n","import { Commit, CommitStream } from '@aida-dev/core';\nimport { CohortMergeRatio, MergeRatio } from './schema/metrics.js';\n\nfunction computeMergeRatio(commits: Commit[]): CohortMergeRatio {\n const merged = commits.filter((commit) => commit.inDefaultBranchAncestry);\n\n return {\n commitsTotal: commits.length,\n commitsMerged: merged.length,\n mergeRatio: commits.length > 0 ? merged.length / commits.length : 0,\n };\n}\n\nexport function calculateMergeRatio(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.attribution === 'ai'\n): MergeRatio {\n const aiCommits = commitStream.commits.filter(isTarget);\n const result = computeMergeRatio(aiCommits);\n\n return {\n aiCommitsTotal: result.commitsTotal,\n aiCommitsMerged: result.commitsMerged,\n mergeRatio: result.mergeRatio,\n };\n}\n\nexport function calculateBaselineMergeRatio(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.attribution === 'human'\n): CohortMergeRatio {\n const baselineCommits = commitStream.commits.filter(isTarget);\n return computeMergeRatio(baselineCommits);\n}\n","import { Commit, CommitStream, daysBetween } from '@aida-dev/core';\nimport { Persistence } from './schema/metrics.js';\n\ninterface FileLifecycle {\n path: string;\n firstTargetCommitDate: Date;\n lastSeenDate: Date;\n persistenceDays: number;\n}\n\nexport function calculatePersistence(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.attribution === 'ai'\n): Persistence {\n const targetCommits = commitStream.commits.filter(isTarget);\n\n if (targetCommits.length === 0) {\n return {\n commitsConsidered: 0,\n avgDays: 0,\n medianDays: 0,\n buckets: {\n d0_1: 0,\n d2_7: 0,\n d8_30: 0,\n d31_90: 0,\n d90_plus: 0,\n },\n };\n }\n\n // Group files by path and track their lifecycle\n const fileLifecycles = new Map<string, FileLifecycle>();\n\n // Sort commits by date (oldest first)\n const sortedCommits = [...commitStream.commits].sort(\n (a, b) => new Date(a.authorDate).getTime() - new Date(b.authorDate).getTime()\n );\n\n // Track first target commit per file\n for (const commit of sortedCommits) {\n if (isTarget(commit)) {\n const commitDate = new Date(commit.authorDate);\n\n for (const file of commit.stats.files) {\n if (!fileLifecycles.has(file.path)) {\n fileLifecycles.set(file.path, {\n path: file.path,\n firstTargetCommitDate: commitDate,\n lastSeenDate: commitDate,\n persistenceDays: 0,\n });\n }\n }\n }\n }\n\n // Track last seen date for each file\n for (const commit of sortedCommits) {\n const commitDate = new Date(commit.authorDate);\n\n for (const file of commit.stats.files) {\n const lifecycle = fileLifecycles.get(file.path);\n if (lifecycle) {\n // Update last seen date if file is not deleted\n if (file.status !== 'deleted') {\n lifecycle.lastSeenDate = commitDate;\n }\n }\n }\n }\n\n // Calculate persistence days for each file\n const persistenceDays: number[] = [];\n\n for (const lifecycle of fileLifecycles.values()) {\n lifecycle.persistenceDays = daysBetween(lifecycle.firstTargetCommitDate, lifecycle.lastSeenDate);\n persistenceDays.push(lifecycle.persistenceDays);\n }\n\n // Calculate statistics\n const avgDays =\n persistenceDays.length > 0\n ? persistenceDays.reduce((sum, days) => sum + days, 0) / persistenceDays.length\n : 0;\n\n const sortedDays = [...persistenceDays].sort((a, b) => a - b);\n const medianDays =\n sortedDays.length > 0\n ? sortedDays.length % 2 === 0\n ? (sortedDays[sortedDays.length / 2 - 1] + sortedDays[sortedDays.length / 2]) / 2\n : sortedDays[Math.floor(sortedDays.length / 2)]\n : 0;\n\n // Calculate buckets\n const buckets = {\n d0_1: persistenceDays.filter((days) => days <= 1).length,\n d2_7: persistenceDays.filter((days) => days >= 2 && days <= 7).length,\n d8_30: persistenceDays.filter((days) => days >= 8 && days <= 30).length,\n d31_90: persistenceDays.filter((days) => days >= 31 && days <= 90).length,\n d90_plus: persistenceDays.filter((days) => days > 90).length,\n };\n\n return {\n commitsConsidered: targetCommits.length,\n avgDays: Math.round(avgDays * 100) / 100, // Round to 2 decimal places\n medianDays: Math.round(medianDays * 100) / 100,\n buckets,\n };\n}\n\nexport function calculateBaselinePersistence(\n commitStream: CommitStream,\n isTarget: (commit: Commit) => boolean = (commit) => commit.tags.attribution === 'human'\n): Persistence {\n return calculatePersistence(commitStream, isTarget);\n}\n","import { z } from 'zod';\n\n// Attribution coverage (#34): the headline metric. Every other number in this\n// file is only as trustworthy as this block says it is.\nexport const Attribution = z.object({\n commitsTotal: z.number().int().nonnegative(),\n ai: z.number().int().nonnegative(),\n human: z.number().int().nonnegative(),\n unknown: z.number().int().nonnegative(),\n coverage: z.number().min(0).max(1), // (ai + human) / total\n defaultAttribution: z.enum(['ai', 'human', 'unknown']), // prior applied to unknown commits\n coverageThreshold: z.number().min(0).max(1),\n belowThreshold: z.boolean(),\n});\n\nexport const MergeRatio = z.object({\n aiCommitsTotal: z.number().int().nonnegative(),\n aiCommitsMerged: z.number().int().nonnegative(),\n mergeRatio: z.number().min(0).max(1),\n});\n\nexport const Persistence = z.object({\n commitsConsidered: z.number().int().nonnegative(),\n avgDays: z.number().nonnegative(),\n medianDays: z.number().nonnegative(),\n buckets: z.object({\n d0_1: z.number().int().nonnegative(),\n d2_7: z.number().int().nonnegative(),\n d8_30: z.number().int().nonnegative(),\n d31_90: z.number().int().nonnegative(),\n d90_plus: z.number().int().nonnegative(),\n }),\n});\n\nexport const CohortMergeRatio = z.object({\n commitsTotal: z.number().int().nonnegative(),\n commitsMerged: z.number().int().nonnegative(),\n mergeRatio: z.number().min(0).max(1),\n});\n\nexport const Baseline = z.object({\n // True when the cohort includes 'unknown' commits via defaultAttribution:\n // the baseline is an assumption, not observed attribution.\n assumed: z.boolean(),\n mergeRatio: CohortMergeRatio,\n persistence: Persistence,\n});\n\nexport const Delta = z.object({\n mergeRatio: z.number(),\n avgPersistenceDays: z.number(),\n medianPersistenceDays: z.number(),\n});\n\nexport const Metrics = z.object({\n generatedAt: z.string().datetime(),\n window: z.object({\n since: z.string().optional(),\n until: z.string().optional(),\n }),\n repoPath: z.string(),\n defaultBranch: z.string(),\n attribution: Attribution,\n mergeRatio: MergeRatio,\n persistence: Persistence,\n // Null when no commit is attributed 'human' and no defaultAttribution prior\n // assigns the unknowns: AIDA does not invent a comparison cohort.\n baseline: Baseline.nullable(),\n delta: Delta.nullable(),\n caveats: z.array(z.string()),\n});\n\nexport type Attribution = z.infer<typeof Attribution>;\nexport type MergeRatio = z.infer<typeof MergeRatio>;\nexport type Persistence = z.infer<typeof Persistence>;\nexport type CohortMergeRatio = z.infer<typeof CohortMergeRatio>;\nexport type Baseline = z.infer<typeof Baseline>;\nexport type Delta = z.infer<typeof Delta>;\nexport type Metrics = z.infer<typeof Metrics>;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aida-dev/metrics",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Metrics engine for AIDA (AI Development Accounting) - Merge ratio, persistence, and AI impact analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"zod": "^3.22.4",
|
|
14
|
-
"@aida-dev/core": "0.
|
|
14
|
+
"@aida-dev/core": "0.9.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"tsup": "^8.0.0",
|