@indemn/cli 1.0.0 → 1.1.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 (104) hide show
  1. package/.claude-plugin/marketplace.json +28 -0
  2. package/README.md +29 -23
  3. package/dist/cli/agents.d.ts.map +1 -1
  4. package/dist/cli/agents.js +20 -0
  5. package/dist/cli/agents.js.map +1 -1
  6. package/dist/cli/eval.d.ts.map +1 -1
  7. package/dist/cli/eval.js +38 -0
  8. package/dist/cli/eval.js.map +1 -1
  9. package/dist/mcp/tools.d.ts.map +1 -1
  10. package/dist/mcp/tools.js +40 -0
  11. package/dist/mcp/tools.js.map +1 -1
  12. package/dist/report/agent-card/cover.d.ts +20 -0
  13. package/dist/report/agent-card/cover.d.ts.map +1 -0
  14. package/dist/report/agent-card/cover.js +58 -0
  15. package/dist/report/agent-card/cover.js.map +1 -0
  16. package/dist/report/agent-card/document.d.ts +7 -0
  17. package/dist/report/agent-card/document.d.ts.map +1 -0
  18. package/dist/report/agent-card/document.js +144 -0
  19. package/dist/report/agent-card/document.js.map +1 -0
  20. package/dist/report/agent-card/types.d.ts +77 -0
  21. package/dist/report/agent-card/types.d.ts.map +1 -0
  22. package/dist/report/agent-card/types.js +3 -0
  23. package/dist/report/agent-card/types.js.map +1 -0
  24. package/dist/report/assets/Indemn_PrimaryLogo_Iris.png +0 -0
  25. package/dist/report/assets/assets/Indemn_PrimaryLogo_Iris.png +0 -0
  26. package/dist/report/assets/assets/fonts/Barlow-Bold.ttf +0 -0
  27. package/dist/report/assets/assets/fonts/Barlow-Medium.ttf +0 -0
  28. package/dist/report/assets/assets/fonts/Barlow-Regular.ttf +0 -0
  29. package/dist/report/assets/assets/fonts/Barlow-SemiBold.ttf +0 -0
  30. package/dist/report/assets/fonts/Barlow-Bold.ttf +0 -0
  31. package/dist/report/assets/fonts/Barlow-Medium.ttf +0 -0
  32. package/dist/report/assets/fonts/Barlow-Regular.ttf +0 -0
  33. package/dist/report/assets/fonts/Barlow-SemiBold.ttf +0 -0
  34. package/dist/report/eval-report/cover.d.ts +16 -0
  35. package/dist/report/eval-report/cover.d.ts.map +1 -0
  36. package/dist/report/eval-report/cover.js +8 -0
  37. package/dist/report/eval-report/cover.js.map +1 -0
  38. package/dist/report/eval-report/document.d.ts +19 -0
  39. package/dist/report/eval-report/document.d.ts.map +1 -0
  40. package/dist/report/eval-report/document.js +32 -0
  41. package/dist/report/eval-report/document.js.map +1 -0
  42. package/dist/report/eval-report/example-page.d.ts +11 -0
  43. package/dist/report/eval-report/example-page.d.ts.map +1 -0
  44. package/dist/report/eval-report/example-page.js +18 -0
  45. package/dist/report/eval-report/example-page.js.map +1 -0
  46. package/dist/report/eval-report/item-page.d.ts +9 -0
  47. package/dist/report/eval-report/item-page.d.ts.map +1 -0
  48. package/dist/report/eval-report/item-page.js +72 -0
  49. package/dist/report/eval-report/item-page.js.map +1 -0
  50. package/dist/report/eval-report/matrix-page.d.ts +8 -0
  51. package/dist/report/eval-report/matrix-page.d.ts.map +1 -0
  52. package/dist/report/eval-report/matrix-page.js +31 -0
  53. package/dist/report/eval-report/matrix-page.js.map +1 -0
  54. package/dist/report/eval-report/types.d.ts +69 -0
  55. package/dist/report/eval-report/types.d.ts.map +1 -0
  56. package/dist/report/eval-report/types.js +7 -0
  57. package/dist/report/eval-report/types.js.map +1 -0
  58. package/dist/report/eval-report/v2-cover.d.ts +9 -0
  59. package/dist/report/eval-report/v2-cover.d.ts.map +1 -0
  60. package/dist/report/eval-report/v2-cover.js +32 -0
  61. package/dist/report/eval-report/v2-cover.js.map +1 -0
  62. package/dist/report/export.d.ts +17 -0
  63. package/dist/report/export.d.ts.map +1 -0
  64. package/dist/report/export.js +327 -0
  65. package/dist/report/export.js.map +1 -0
  66. package/dist/report/generate.d.ts +7 -0
  67. package/dist/report/generate.d.ts.map +1 -0
  68. package/dist/report/generate.js +30 -0
  69. package/dist/report/generate.js.map +1 -0
  70. package/dist/report/scoring.d.ts +90 -0
  71. package/dist/report/scoring.d.ts.map +1 -0
  72. package/dist/report/scoring.js +174 -0
  73. package/dist/report/scoring.js.map +1 -0
  74. package/dist/report/styles.d.ts +111 -0
  75. package/dist/report/styles.d.ts.map +1 -0
  76. package/dist/report/styles.js +134 -0
  77. package/dist/report/styles.js.map +1 -0
  78. package/docs/plans/2026-03-13-cli-exports.md +819 -0
  79. package/package.json +14 -4
  80. package/skills/eval-analysis/SKILL.md +178 -0
  81. package/skills/eval-analysis/references/data-shapes.md +279 -0
  82. package/src/cli/agents.ts +22 -0
  83. package/src/cli/eval.ts +42 -0
  84. package/src/mcp/tools.ts +50 -0
  85. package/src/report/agent-card/cover.tsx +232 -0
  86. package/src/report/agent-card/document.tsx +582 -0
  87. package/src/report/agent-card/types.ts +86 -0
  88. package/src/report/assets/Indemn_PrimaryLogo_Iris.png +0 -0
  89. package/src/report/assets/fonts/Barlow-Bold.ttf +0 -0
  90. package/src/report/assets/fonts/Barlow-Medium.ttf +0 -0
  91. package/src/report/assets/fonts/Barlow-Regular.ttf +0 -0
  92. package/src/report/assets/fonts/Barlow-SemiBold.ttf +0 -0
  93. package/src/report/eval-report/cover.tsx +69 -0
  94. package/src/report/eval-report/document.tsx +105 -0
  95. package/src/report/eval-report/example-page.tsx +121 -0
  96. package/src/report/eval-report/item-page.tsx +211 -0
  97. package/src/report/eval-report/matrix-page.tsx +124 -0
  98. package/src/report/eval-report/types.ts +77 -0
  99. package/src/report/eval-report/v2-cover.tsx +102 -0
  100. package/src/report/export.ts +394 -0
  101. package/src/report/generate.tsx +37 -0
  102. package/src/report/scoring.ts +281 -0
  103. package/src/report/styles.ts +138 -0
  104. package/tsconfig.json +2 -1
@@ -0,0 +1,281 @@
1
+ // src/report/scoring.ts — Scoring utilities ported from dashboard
2
+ // Pure arithmetic, no browser dependencies
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // Thresholds
6
+ // ---------------------------------------------------------------------------
7
+
8
+ export const SCORE_THRESHOLDS = {
9
+ SUCCESS: 0.70,
10
+ WARNING: 0.50,
11
+ } as const;
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Types
15
+ // ---------------------------------------------------------------------------
16
+
17
+ export interface ScoreMetrics {
18
+ criteriaRate: number | null; // 0-1, null when no data
19
+ rubricRate: number | null; // 0-1, null when no rubric data
20
+ criteriaPassed: number;
21
+ criteriaTotal: number;
22
+ rubricRulesPassed: number;
23
+ rubricRulesTotal: number;
24
+ }
25
+
26
+ export type ScoreColor = 'success' | 'warning' | 'error';
27
+
28
+ export interface ScoreColorClasses {
29
+ text: string;
30
+ bg: string;
31
+ bar: string;
32
+ hex: string;
33
+ }
34
+
35
+ // Minimal types for scoring functions
36
+ export interface ScoringRun {
37
+ status?: string;
38
+ passed?: number;
39
+ completed?: number;
40
+ criteria_passed?: number;
41
+ criteria_total?: number;
42
+ rubric_rules_passed?: number;
43
+ rubric_rules_total?: number;
44
+ component_scores?: Record<string, { score?: number; total?: number; passed?: number }>;
45
+ }
46
+
47
+ export interface ScoringResult {
48
+ criteria_scores?: Array<{ passed: boolean }>;
49
+ rubric_scores?: Array<{ passed: boolean }>;
50
+ }
51
+
52
+ // V1 matrix types
53
+ export type Severity = 'High' | 'Medium' | 'Low';
54
+
55
+ export interface EvaluatorInfo {
56
+ key: string;
57
+ name: string;
58
+ severity: Severity;
59
+ prompt?: string;
60
+ model?: string;
61
+ }
62
+
63
+ export interface MatrixCell {
64
+ score: number;
65
+ comment: string | null;
66
+ passed: boolean;
67
+ }
68
+
69
+ export interface MatrixRow {
70
+ resultId: string;
71
+ testCaseId: string;
72
+ question: string;
73
+ response: string;
74
+ cells: Record<string, MatrixCell>;
75
+ avgScore: number;
76
+ }
77
+
78
+ export interface MatrixData {
79
+ evaluators: EvaluatorInfo[];
80
+ rows: MatrixRow[];
81
+ columnAverages: Record<string, number>;
82
+ overallAverage: number;
83
+ }
84
+
85
+ export interface WeightConfig {
86
+ High: number;
87
+ Medium: number;
88
+ Low: number;
89
+ }
90
+
91
+ export const DEFAULT_WEIGHTS: WeightConfig = {
92
+ High: 2,
93
+ Medium: 1,
94
+ Low: 0.5,
95
+ };
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // Helpers
99
+ // ---------------------------------------------------------------------------
100
+
101
+ function sumComponentScores(cs: Record<string, { score?: number; total?: number; passed?: number }>): { passed: number; total: number } {
102
+ const scopes = ['prompt', 'knowledge_base', 'function', 'general'] as const;
103
+ let passed = 0;
104
+ let total = 0;
105
+ for (const scope of scopes) {
106
+ passed += cs[scope]?.passed ?? 0;
107
+ total += cs[scope]?.total ?? 0;
108
+ }
109
+ return { passed, total };
110
+ }
111
+
112
+ // ---------------------------------------------------------------------------
113
+ // Run-level scoring
114
+ // ---------------------------------------------------------------------------
115
+
116
+ export function getRunScoreMetrics(run: ScoringRun): ScoreMetrics {
117
+ let criteriaPassed: number;
118
+ let criteriaTotal: number;
119
+ let rubricRulesPassed: number;
120
+ let rubricRulesTotal: number;
121
+
122
+ if (run.criteria_total !== undefined && run.criteria_total > 0) {
123
+ criteriaPassed = run.criteria_passed ?? 0;
124
+ criteriaTotal = run.criteria_total;
125
+ } else {
126
+ criteriaPassed = run.passed ?? 0;
127
+ criteriaTotal = run.completed ?? 0;
128
+ }
129
+
130
+ const hasNewRubricFields = run.rubric_rules_total !== undefined;
131
+ if (hasNewRubricFields) {
132
+ rubricRulesPassed = run.rubric_rules_passed ?? 0;
133
+ rubricRulesTotal = run.rubric_rules_total ?? 0;
134
+ } else {
135
+ const cs = run.component_scores;
136
+ if (cs) {
137
+ const sums = sumComponentScores(cs);
138
+ rubricRulesPassed = sums.passed;
139
+ rubricRulesTotal = sums.total;
140
+ } else {
141
+ rubricRulesPassed = 0;
142
+ rubricRulesTotal = 0;
143
+ }
144
+ }
145
+
146
+ const criteriaRate = criteriaTotal > 0 ? criteriaPassed / criteriaTotal : null;
147
+ const rubricRate = rubricRulesTotal > 0 ? rubricRulesPassed / rubricRulesTotal : null;
148
+
149
+ return { criteriaRate, rubricRate, criteriaPassed, criteriaTotal, rubricRulesPassed, rubricRulesTotal };
150
+ }
151
+
152
+ export function getRunCriteriaScore(run: ScoringRun): number | null {
153
+ const { criteriaRate } = getRunScoreMetrics(run);
154
+ return criteriaRate !== null ? Math.round(criteriaRate * 100) : null;
155
+ }
156
+
157
+ export function getRunRubricScore(run: ScoringRun): number | null {
158
+ const { rubricRate } = getRunScoreMetrics(run);
159
+ return rubricRate !== null ? Math.round(rubricRate * 100) : null;
160
+ }
161
+
162
+ // ---------------------------------------------------------------------------
163
+ // Result-level scoring
164
+ // ---------------------------------------------------------------------------
165
+
166
+ export function getResultsScoreMetrics(results: ScoringResult[]): ScoreMetrics {
167
+ if (results.length === 0) {
168
+ return { criteriaRate: null, rubricRate: null, criteriaPassed: 0, criteriaTotal: 0, rubricRulesPassed: 0, rubricRulesTotal: 0 };
169
+ }
170
+
171
+ const allCriteria = results.flatMap((r) => r.criteria_scores || []);
172
+ const criteriaTotal = allCriteria.length;
173
+ const criteriaPassed = allCriteria.filter((c) => c.passed).length;
174
+ const criteriaRate = criteriaTotal > 0 ? criteriaPassed / criteriaTotal : null;
175
+
176
+ const allRubricScores = results.flatMap((r) => r.rubric_scores || []);
177
+ const rubricRulesTotal = allRubricScores.length;
178
+ const rubricRulesPassed = allRubricScores.filter((r) => r.passed).length;
179
+ const rubricRate = rubricRulesTotal > 0 ? rubricRulesPassed / rubricRulesTotal : null;
180
+
181
+ return { criteriaRate, rubricRate, criteriaPassed, criteriaTotal, rubricRulesPassed, rubricRulesTotal };
182
+ }
183
+
184
+ // ---------------------------------------------------------------------------
185
+ // Aggregate pass rate
186
+ // ---------------------------------------------------------------------------
187
+
188
+ export function getAggregatePassRate(runs: ScoringRun[]): number | null {
189
+ const completed = runs.filter((r) => r.status === 'completed');
190
+ if (completed.length === 0) return null;
191
+
192
+ let totalPassed = 0;
193
+ let totalChecks = 0;
194
+ for (const run of completed) {
195
+ if (run.criteria_total !== undefined && run.criteria_total > 0) {
196
+ totalPassed += run.criteria_passed ?? 0;
197
+ totalChecks += run.criteria_total;
198
+ } else {
199
+ totalPassed += run.passed ?? 0;
200
+ totalChecks += run.completed ?? 0;
201
+ }
202
+ }
203
+ return totalChecks > 0 ? totalPassed / totalChecks : null;
204
+ }
205
+
206
+ // ---------------------------------------------------------------------------
207
+ // Color utilities
208
+ // ---------------------------------------------------------------------------
209
+
210
+ export function getScoreColor(rate: number | null | undefined): ScoreColor {
211
+ if (rate === null || rate === undefined) return 'error';
212
+ if (rate >= SCORE_THRESHOLDS.SUCCESS) return 'success';
213
+ if (rate >= SCORE_THRESHOLDS.WARNING) return 'warning';
214
+ return 'error';
215
+ }
216
+
217
+ export function getScoreColorClasses(rate: number | null | undefined): ScoreColorClasses {
218
+ const color = getScoreColor(rate);
219
+ switch (color) {
220
+ case 'success':
221
+ return { text: 'text-success', bg: 'bg-success/10', bar: 'bg-success', hex: '#22c55e' };
222
+ case 'warning':
223
+ return { text: 'text-warning', bg: 'bg-warning/10', bar: 'bg-warning', hex: '#f59e0b' };
224
+ case 'error':
225
+ return { text: 'text-error', bg: 'bg-error/10', bar: 'bg-error', hex: '#ef4444' };
226
+ }
227
+ }
228
+
229
+ export function getScoreHexColor(rate: number | null | undefined): string {
230
+ return getScoreColorClasses(rate).hex;
231
+ }
232
+
233
+ // ---------------------------------------------------------------------------
234
+ // Weighted score calculation (V1 reports)
235
+ // ---------------------------------------------------------------------------
236
+
237
+ export function calculateWeightedScores(
238
+ matrixData: MatrixData,
239
+ weights: WeightConfig,
240
+ ): {
241
+ overall: number;
242
+ bySeverity: Record<Severity, { score: number; count: number }>;
243
+ } {
244
+ const bySeverity: Record<Severity, { total: number; weight: number; count: number }> = {
245
+ High: { total: 0, weight: 0, count: 0 },
246
+ Medium: { total: 0, weight: 0, count: 0 },
247
+ Low: { total: 0, weight: 0, count: 0 },
248
+ };
249
+
250
+ for (const evaluator of matrixData.evaluators) {
251
+ const avg = matrixData.columnAverages[evaluator.key] || 0;
252
+ const weight = weights[evaluator.severity];
253
+ bySeverity[evaluator.severity].total += avg * weight;
254
+ bySeverity[evaluator.severity].weight += weight;
255
+ bySeverity[evaluator.severity].count++;
256
+ }
257
+
258
+ let overallTotal = 0;
259
+ let overallWeight = 0;
260
+
261
+ const result: Record<Severity, { score: number; count: number }> = {
262
+ High: { score: 0, count: 0 },
263
+ Medium: { score: 0, count: 0 },
264
+ Low: { score: 0, count: 0 },
265
+ };
266
+
267
+ for (const severity of ['High', 'Medium', 'Low'] as Severity[]) {
268
+ const data = bySeverity[severity];
269
+ result[severity] = {
270
+ score: data.weight > 0 ? data.total / data.weight : 0,
271
+ count: data.count,
272
+ };
273
+ overallTotal += data.total;
274
+ overallWeight += data.weight;
275
+ }
276
+
277
+ return {
278
+ overall: overallWeight > 0 ? overallTotal / overallWeight : 0,
279
+ bySeverity: result,
280
+ };
281
+ }
@@ -0,0 +1,138 @@
1
+ // src/report/styles.ts — Brand styles and font registration for PDF reports
2
+ import { Font, StyleSheet } from '@react-pdf/renderer';
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname, join } from 'path';
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ const assetsDir = join(__dirname, 'assets');
8
+
9
+ // Register Barlow font family from bundled files
10
+ Font.register({
11
+ family: 'Barlow',
12
+ fonts: [
13
+ { src: join(assetsDir, 'fonts', 'Barlow-Regular.ttf'), fontWeight: 400 },
14
+ { src: join(assetsDir, 'fonts', 'Barlow-Medium.ttf'), fontWeight: 500 },
15
+ { src: join(assetsDir, 'fonts', 'Barlow-SemiBold.ttf'), fontWeight: 600 },
16
+ { src: join(assetsDir, 'fonts', 'Barlow-Bold.ttf'), fontWeight: 700 },
17
+ ],
18
+ });
19
+
20
+ export const logoPath = join(assetsDir, 'Indemn_PrimaryLogo_Iris.png');
21
+
22
+ // Brand colors from Indemn Brand Guide
23
+ export const colors = {
24
+ // Primary brand colors
25
+ iris: '#4752a3',
26
+ lilac: '#a67cb7',
27
+ eggplant: '#1e2553',
28
+ white: '#ffffff',
29
+ // Supporting brand colors
30
+ lime: '#e0da67',
31
+ olive: '#2a2b1a',
32
+ // Derived UI colors
33
+ background: '#f4f3f8',
34
+ muted: '#6b7094',
35
+ border: '#d4d7e3',
36
+ // Semantic colors
37
+ success: '#22c55e',
38
+ error: '#ef4444',
39
+ warning: '#f59e0b',
40
+ };
41
+
42
+ // Shared styles
43
+ export const styles = StyleSheet.create({
44
+ page: {
45
+ fontFamily: 'Barlow',
46
+ fontSize: 11,
47
+ color: colors.eggplant,
48
+ backgroundColor: colors.white,
49
+ padding: 40,
50
+ },
51
+ pageHeader: {
52
+ flexDirection: 'row',
53
+ justifyContent: 'space-between',
54
+ alignItems: 'center',
55
+ marginBottom: 24,
56
+ paddingBottom: 12,
57
+ borderBottomWidth: 1,
58
+ borderBottomColor: colors.border,
59
+ },
60
+ pageTitle: {
61
+ fontSize: 18,
62
+ fontWeight: 600,
63
+ color: colors.iris,
64
+ },
65
+ pageNumber: {
66
+ fontSize: 9,
67
+ color: colors.muted,
68
+ },
69
+ sectionTitle: {
70
+ fontSize: 14,
71
+ fontWeight: 600,
72
+ color: colors.iris,
73
+ marginBottom: 12,
74
+ },
75
+ bodyText: {
76
+ fontSize: 11,
77
+ fontWeight: 400,
78
+ color: colors.eggplant,
79
+ lineHeight: 1.5,
80
+ marginBottom: 8,
81
+ },
82
+ caption: {
83
+ fontSize: 9,
84
+ fontWeight: 500,
85
+ color: colors.muted,
86
+ },
87
+ card: {
88
+ backgroundColor: colors.background,
89
+ borderRadius: 6,
90
+ padding: 16,
91
+ marginBottom: 12,
92
+ },
93
+ metricValue: {
94
+ fontSize: 28,
95
+ fontWeight: 700,
96
+ color: colors.eggplant,
97
+ },
98
+ metricLabel: {
99
+ fontSize: 10,
100
+ fontWeight: 500,
101
+ color: colors.muted,
102
+ marginTop: 4,
103
+ },
104
+ row: {
105
+ flexDirection: 'row',
106
+ },
107
+ col: {
108
+ flex: 1,
109
+ },
110
+ footer: {
111
+ position: 'absolute',
112
+ bottom: 30,
113
+ left: 40,
114
+ right: 40,
115
+ flexDirection: 'row',
116
+ justifyContent: 'space-between',
117
+ alignItems: 'center',
118
+ paddingTop: 12,
119
+ borderTopWidth: 1,
120
+ borderTopColor: colors.border,
121
+ },
122
+ // Evaluation-specific styles
123
+ scorePass: {
124
+ color: colors.success,
125
+ },
126
+ scoreFail: {
127
+ color: colors.error,
128
+ },
129
+ severityHigh: {
130
+ color: '#3a4389', // Dark iris
131
+ },
132
+ severityMedium: {
133
+ color: colors.iris, // Standard iris
134
+ },
135
+ severityLow: {
136
+ color: '#7c85c0', // Light iris
137
+ },
138
+ });
package/tsconfig.json CHANGED
@@ -12,7 +12,8 @@
12
12
  "resolveJsonModule": true,
13
13
  "declaration": true,
14
14
  "declarationMap": true,
15
- "sourceMap": true
15
+ "sourceMap": true,
16
+ "jsx": "react-jsx"
16
17
  },
17
18
  "include": ["src/**/*"],
18
19
  "exclude": ["node_modules", "dist", "test"]