stepproof 0.2.22 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/adapters/anthropic.d.ts +3 -2
  2. package/dist/adapters/anthropic.d.ts.map +1 -1
  3. package/dist/adapters/anthropic.js +27 -5
  4. package/dist/adapters/anthropic.js.map +1 -1
  5. package/dist/adapters/base.d.ts +14 -1
  6. package/dist/adapters/base.d.ts.map +1 -1
  7. package/dist/adapters/gemini.d.ts +9 -0
  8. package/dist/adapters/gemini.d.ts.map +1 -0
  9. package/dist/adapters/gemini.js +80 -0
  10. package/dist/adapters/gemini.js.map +1 -0
  11. package/dist/adapters/index.d.ts.map +1 -1
  12. package/dist/adapters/index.js +7 -1
  13. package/dist/adapters/index.js.map +1 -1
  14. package/dist/adapters/ollama.d.ts +9 -0
  15. package/dist/adapters/ollama.d.ts.map +1 -0
  16. package/dist/adapters/ollama.js +61 -0
  17. package/dist/adapters/ollama.js.map +1 -0
  18. package/dist/adapters/openai.d.ts +3 -2
  19. package/dist/adapters/openai.d.ts.map +1 -1
  20. package/dist/adapters/openai.js +16 -5
  21. package/dist/adapters/openai.js.map +1 -1
  22. package/dist/assertions/engine.d.ts +6 -1
  23. package/dist/assertions/engine.d.ts.map +1 -1
  24. package/dist/assertions/engine.js +176 -11
  25. package/dist/assertions/engine.js.map +1 -1
  26. package/dist/baseline.d.ts +23 -0
  27. package/dist/baseline.d.ts.map +1 -0
  28. package/dist/baseline.js +98 -0
  29. package/dist/baseline.js.map +1 -0
  30. package/dist/cache.d.ts +5 -0
  31. package/dist/cache.d.ts.map +1 -0
  32. package/dist/cache.js +71 -0
  33. package/dist/cache.js.map +1 -0
  34. package/dist/cli.js +283 -18
  35. package/dist/cli.js.map +1 -1
  36. package/dist/commands/compare.d.ts +43 -0
  37. package/dist/commands/compare.d.ts.map +1 -0
  38. package/dist/commands/compare.js +75 -0
  39. package/dist/commands/compare.js.map +1 -0
  40. package/dist/commands/diff.d.ts +2 -0
  41. package/dist/commands/diff.d.ts.map +1 -0
  42. package/dist/commands/diff.js +252 -0
  43. package/dist/commands/diff.js.map +1 -0
  44. package/dist/commands/history.d.ts +2 -0
  45. package/dist/commands/history.d.ts.map +1 -0
  46. package/dist/commands/history.js +46 -0
  47. package/dist/commands/history.js.map +1 -0
  48. package/dist/commands/results-store.d.ts +15 -0
  49. package/dist/commands/results-store.d.ts.map +1 -0
  50. package/dist/commands/results-store.js +77 -0
  51. package/dist/commands/results-store.js.map +1 -0
  52. package/dist/commands/view.d.ts +2 -0
  53. package/dist/commands/view.d.ts.map +1 -0
  54. package/dist/commands/view.js +51 -0
  55. package/dist/commands/view.js.map +1 -0
  56. package/dist/commands/watch.d.ts +7 -0
  57. package/dist/commands/watch.d.ts.map +1 -0
  58. package/dist/commands/watch.js +84 -0
  59. package/dist/commands/watch.js.map +1 -0
  60. package/dist/core/scenario-parser.d.ts.map +1 -1
  61. package/dist/core/scenario-parser.js +47 -5
  62. package/dist/core/scenario-parser.js.map +1 -1
  63. package/dist/core/scenario-runner.d.ts +12 -0
  64. package/dist/core/scenario-runner.d.ts.map +1 -1
  65. package/dist/core/scenario-runner.js +372 -41
  66. package/dist/core/scenario-runner.js.map +1 -1
  67. package/dist/core/types.d.ts +63 -9
  68. package/dist/core/types.d.ts.map +1 -1
  69. package/dist/dataset.d.ts +6 -0
  70. package/dist/dataset.d.ts.map +1 -0
  71. package/dist/dataset.js +108 -0
  72. package/dist/dataset.js.map +1 -0
  73. package/dist/reporters/github-comment.d.ts +8 -0
  74. package/dist/reporters/github-comment.d.ts.map +1 -0
  75. package/dist/reporters/github-comment.js +114 -0
  76. package/dist/reporters/github-comment.js.map +1 -0
  77. package/dist/reporters/html-reporter.d.ts +3 -0
  78. package/dist/reporters/html-reporter.d.ts.map +1 -0
  79. package/dist/reporters/html-reporter.js +152 -0
  80. package/dist/reporters/html-reporter.js.map +1 -0
  81. package/dist/reporters/terminal-reporter.d.ts +10 -1
  82. package/dist/reporters/terminal-reporter.d.ts.map +1 -1
  83. package/dist/reporters/terminal-reporter.js +139 -8
  84. package/dist/reporters/terminal-reporter.js.map +1 -1
  85. package/package.json +2 -1
@@ -1,30 +1,52 @@
1
- export type Provider = 'openai' | 'anthropic';
2
- export type AssertionType = 'contains' | 'not_contains' | 'regex' | 'json_schema' | 'llm_judge';
1
+ export type Provider = 'openai' | 'anthropic' | 'gemini' | 'ollama';
2
+ export type AssertionType = 'contains' | 'not_contains' | 'regex' | 'json_schema' | 'llm_judge' | 'similarity' | 'sentiment' | 'toxicity' | 'starts_with' | 'ends_with' | 'length' | 'word_count' | 'cost_under' | 'latency_under';
3
+ export interface ConversationTurn {
4
+ role: 'user' | 'assistant' | 'system';
5
+ content?: string;
6
+ }
3
7
  export interface Assertion {
4
8
  type: AssertionType;
5
- /** For contains, not_contains, regex */
6
- value?: string;
9
+ /** For contains, not_contains, regex, starts_with, ends_with, cost_under, latency_under */
10
+ value?: string | number;
7
11
  /** For json_schema: path to JSON schema file (relative to scenario file) */
8
12
  schema?: string;
9
13
  /** For llm_judge: the evaluation prompt */
10
14
  prompt?: string;
11
15
  /** For llm_judge: the expected response prefix (default: "yes") */
12
16
  pass_on?: string;
13
- /** For llm_judge: override provider (default: anthropic) */
17
+ /** For llm_judge, similarity, sentiment, toxicity: override provider (default: anthropic) */
14
18
  provider?: Provider;
15
- /** For llm_judge: override model (default: claude-haiku or gpt-4o-mini) */
19
+ /** For llm_judge, similarity, sentiment, toxicity: override model */
16
20
  model?: string;
21
+ /** For similarity: minimum similarity score 0.0-1.0 (default: 0.7) */
22
+ threshold?: number;
23
+ /** For toxicity: maximum toxicity score 0.0-1.0 (default: 0.5) */
24
+ max_score?: number;
25
+ /** For length, word_count: minimum value */
26
+ min?: number;
27
+ /** For length, word_count: maximum value */
28
+ max?: number;
17
29
  }
18
30
  export interface Step {
19
31
  id: string;
20
32
  provider: Provider;
21
33
  model: string;
22
- /** Prompt template. Use {{variable}} for variables, {{step_id.output}} for prior step outputs. */
23
- prompt: string;
34
+ /** Prompt template. Use {{variable}} for variables, {{step_id.output}} for prior step outputs. Required unless conversation is set. */
35
+ prompt?: string;
24
36
  /** Optional system prompt */
25
37
  system?: string;
26
- /** Minimum pass rate threshold (0.0–1.0). Default: 0.8 */
38
+ /** Multi-turn conversation. The last assistant turn without content is where the LLM responds. */
39
+ conversation?: ConversationTurn[];
40
+ /** Step IDs this step depends on. Steps without dependencies run in parallel. */
41
+ depends_on?: string[];
42
+ /** Condition string — if false, step is skipped. Supports contains, not_contains, matches operators. */
43
+ if?: string;
44
+ /** Minimum pass rate threshold (0.0-1.0). Default: 0.8 */
27
45
  min_pass_rate?: number;
46
+ /** Retry up to N times on assertion failure (0 = no retries). Default: 0 */
47
+ retry?: number;
48
+ /** Delay in ms between retries. Default: 1000 */
49
+ retry_delay?: number;
28
50
  assertions: Assertion[];
29
51
  }
30
52
  export interface Scenario {
@@ -45,9 +67,17 @@ export interface StepResult {
45
67
  iteration: number;
46
68
  output: string;
47
69
  passed: boolean;
70
+ skipped?: boolean;
71
+ /** Number of retries used before final result (0 = passed on first try) */
72
+ retriesUsed?: number;
73
+ /** Dataset row index (0-based) when running in dataset mode */
74
+ datasetRow?: number;
48
75
  assertionResults: AssertionResult[];
49
76
  error?: string;
50
77
  durationMs: number;
78
+ inputTokens?: number;
79
+ outputTokens?: number;
80
+ costUsd?: number;
51
81
  }
52
82
  export interface StepSummary {
53
83
  stepId: string;
@@ -57,6 +87,23 @@ export interface StepSummary {
57
87
  passRate: number;
58
88
  minPassRate: number;
59
89
  belowThreshold: boolean;
90
+ avgDurationMs: number;
91
+ totalCostUsd: number;
92
+ avgCostUsd: number;
93
+ /** Number of step results that required at least one retry */
94
+ retriedCount?: number;
95
+ }
96
+ /** Per-row result summary when running in dataset mode */
97
+ export interface DatasetRowSummary {
98
+ rowIndex: number;
99
+ /** First few columns from the row for display */
100
+ rowPreview: Record<string, string>;
101
+ allStepsPassed: boolean;
102
+ stepResults: Array<{
103
+ stepId: string;
104
+ passed: boolean;
105
+ passRate: number;
106
+ }>;
60
107
  }
61
108
  export interface ScenarioReport {
62
109
  scenarioName: string;
@@ -67,5 +114,12 @@ export interface ScenarioReport {
67
114
  steps: StepSummary[];
68
115
  allPassed: boolean;
69
116
  results: StepResult[];
117
+ /** Dataset mode info — present when run with --dataset */
118
+ dataset?: {
119
+ path: string;
120
+ totalRows: number;
121
+ rowsPassed: number;
122
+ rowSummaries: DatasetRowSummary[];
123
+ };
70
124
  }
71
125
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,cAAc,GAAG,OAAO,GAAG,aAAa,GAAG,WAAW,CAAC;AAEhG,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,aAAa,CAAC;IACpB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,kGAAkG;IAClG,MAAM,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Lightweight CSV parser. Handles quoted fields (including escaped quotes "").
3
+ * O(n) where n = file size.
4
+ */
5
+ export declare function loadDataset(csvPath: string): Array<Record<string, string>>;
6
+ //# sourceMappingURL=dataset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset.d.ts","sourceRoot":"","sources":["../src/dataset.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAkC1E"}
@@ -0,0 +1,108 @@
1
+ import * as fs from 'node:fs';
2
+ /**
3
+ * Lightweight CSV parser. Handles quoted fields (including escaped quotes "").
4
+ * O(n) where n = file size.
5
+ */
6
+ export function loadDataset(csvPath) {
7
+ let content;
8
+ try {
9
+ content = fs.readFileSync(csvPath, 'utf-8');
10
+ }
11
+ catch (e) {
12
+ throw new Error(`Cannot read dataset file: ${csvPath}`);
13
+ }
14
+ const lines = parseCSVLines(content);
15
+ if (lines.length < 2) {
16
+ throw new Error(`Dataset file must have a header row and at least one data row: ${csvPath}`);
17
+ }
18
+ const headers = lines[0];
19
+ const rows = [];
20
+ for (let i = 1; i < lines.length; i++) {
21
+ const fields = lines[i];
22
+ if (fields.length === 1 && fields[0] === '')
23
+ continue; // skip blank lines
24
+ if (fields.length !== headers.length) {
25
+ throw new Error(`Dataset row ${i} has ${fields.length} fields, expected ${headers.length}`);
26
+ }
27
+ const row = {};
28
+ for (let j = 0; j < headers.length; j++) {
29
+ row[headers[j]] = fields[j];
30
+ }
31
+ rows.push(row);
32
+ }
33
+ if (rows.length === 0) {
34
+ throw new Error(`Dataset file has no data rows: ${csvPath}`);
35
+ }
36
+ return rows;
37
+ }
38
+ /**
39
+ * Parse CSV content into array of field arrays.
40
+ * Handles: quoted fields, embedded commas, embedded newlines in quotes, escaped quotes ("").
41
+ */
42
+ function parseCSVLines(content) {
43
+ const results = [];
44
+ let currentRow = [];
45
+ let currentField = '';
46
+ let inQuotes = false;
47
+ let i = 0;
48
+ while (i < content.length) {
49
+ const ch = content[i];
50
+ if (inQuotes) {
51
+ if (ch === '"') {
52
+ if (i + 1 < content.length && content[i + 1] === '"') {
53
+ // Escaped quote
54
+ currentField += '"';
55
+ i += 2;
56
+ }
57
+ else {
58
+ // End of quoted field
59
+ inQuotes = false;
60
+ i++;
61
+ }
62
+ }
63
+ else {
64
+ currentField += ch;
65
+ i++;
66
+ }
67
+ }
68
+ else {
69
+ if (ch === '"') {
70
+ inQuotes = true;
71
+ i++;
72
+ }
73
+ else if (ch === ',') {
74
+ currentRow.push(currentField);
75
+ currentField = '';
76
+ i++;
77
+ }
78
+ else if (ch === '\r') {
79
+ // Handle \r\n and lone \r
80
+ currentRow.push(currentField);
81
+ currentField = '';
82
+ results.push(currentRow);
83
+ currentRow = [];
84
+ i++;
85
+ if (i < content.length && content[i] === '\n')
86
+ i++;
87
+ }
88
+ else if (ch === '\n') {
89
+ currentRow.push(currentField);
90
+ currentField = '';
91
+ results.push(currentRow);
92
+ currentRow = [];
93
+ i++;
94
+ }
95
+ else {
96
+ currentField += ch;
97
+ i++;
98
+ }
99
+ }
100
+ }
101
+ // Flush last field/row
102
+ if (currentField || currentRow.length > 0) {
103
+ currentRow.push(currentField);
104
+ results.push(currentRow);
105
+ }
106
+ return results;
107
+ }
108
+ //# sourceMappingURL=dataset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset.js","sourceRoot":"","sources":["../src/dataset.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ScenarioReport } from '../core/types.js';
2
+ import type { BaselineComparison } from '../baseline.js';
3
+ /**
4
+ * Format a ScenarioReport as a GitHub PR comment in markdown.
5
+ * Includes a marker comment so the action can update existing comments.
6
+ */
7
+ export declare function formatPRComment(report: ScenarioReport, baselineComparison?: BaselineComparison): string;
8
+ //# sourceMappingURL=github-comment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-comment.d.ts","sourceRoot":"","sources":["../../src/reporters/github-comment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,kBAAkB,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAkB,MAAM,gBAAgB,CAAC;AA6BzE;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,cAAc,EACtB,kBAAkB,CAAC,EAAE,kBAAkB,GACtC,MAAM,CA0FR"}
@@ -0,0 +1,114 @@
1
+ function formatDuration(ms) {
2
+ if (ms < 1000)
3
+ return `${ms}ms`;
4
+ if (ms < 60_000)
5
+ return `${(ms / 1000).toFixed(1)}s`;
6
+ const mins = Math.floor(ms / 60_000);
7
+ const secs = ((ms % 60_000) / 1000).toFixed(0);
8
+ return `${mins}m ${secs}s`;
9
+ }
10
+ function formatCost(usd) {
11
+ if (usd === 0)
12
+ return '$0.00';
13
+ if (usd < 0.01)
14
+ return `$${usd.toFixed(4)}`;
15
+ return `$${usd.toFixed(2)}`;
16
+ }
17
+ function baselineCell(step, baselineMap) {
18
+ const cmp = baselineMap.get(step.stepId);
19
+ if (!cmp)
20
+ return '—';
21
+ const deltaPct = (cmp.delta * 100).toFixed(1);
22
+ if (cmp.delta > 0)
23
+ return `↑ +${deltaPct}%`;
24
+ if (cmp.delta < 0)
25
+ return `↓ ${deltaPct}%`;
26
+ return '→ same';
27
+ }
28
+ function statusEmoji(belowThreshold) {
29
+ return belowThreshold ? '❌ Fail' : '✅ Pass';
30
+ }
31
+ /**
32
+ * Format a ScenarioReport as a GitHub PR comment in markdown.
33
+ * Includes a marker comment so the action can update existing comments.
34
+ */
35
+ export function formatPRComment(report, baselineComparison) {
36
+ const baselineMap = new Map();
37
+ if (baselineComparison) {
38
+ for (const s of baselineComparison.steps) {
39
+ baselineMap.set(s.stepId, s);
40
+ }
41
+ }
42
+ const hasBaseline = baselineMap.size > 0;
43
+ const totalCost = report.steps.reduce((sum, s) => sum + s.totalCostUsd, 0);
44
+ const lines = [];
45
+ // Marker for upsert
46
+ lines.push('<!-- stepproof-results -->');
47
+ lines.push(`## Stepproof Results: ${report.scenarioName}`);
48
+ lines.push('');
49
+ // Table header
50
+ if (hasBaseline) {
51
+ lines.push('| Step | Pass Rate | Threshold | Status | Baseline |');
52
+ lines.push('|------|-----------|-----------|--------|----------|');
53
+ }
54
+ else {
55
+ lines.push('| Step | Pass Rate | Threshold | Status |');
56
+ lines.push('|------|-----------|-----------|--------|');
57
+ }
58
+ // Table rows
59
+ for (const step of report.steps) {
60
+ const pct = (step.passRate * 100).toFixed(1);
61
+ const threshold = (step.minPassRate * 100).toFixed(0);
62
+ const passRateCell = `${pct}% (${step.passes}/${step.totalRuns})`;
63
+ const status = statusEmoji(step.belowThreshold);
64
+ if (hasBaseline) {
65
+ const bl = baselineCell(step, baselineMap);
66
+ lines.push(`| ${step.stepId} | ${passRateCell} | ${threshold}% | ${status} | ${bl} |`);
67
+ }
68
+ else {
69
+ lines.push(`| ${step.stepId} | ${passRateCell} | ${threshold}% | ${status} |`);
70
+ }
71
+ }
72
+ lines.push('');
73
+ // Summary line
74
+ const overallStatus = report.allPassed ? '✅ All steps passed' : '❌ Some steps failed';
75
+ const costStr = totalCost > 0 ? ` | 💰 ${formatCost(totalCost)}` : '';
76
+ lines.push(`**Overall: ${overallStatus}** | ⏱ ${formatDuration(report.durationMs)}${costStr}`);
77
+ // Regression warning
78
+ if (baselineComparison?.hasRegression) {
79
+ const regSteps = baselineComparison.steps.filter(s => s.regression);
80
+ lines.push('');
81
+ lines.push(`> ⚠️ **Regression detected** in ${regSteps.map(s => `\`${s.stepId}\``).join(', ')}`);
82
+ }
83
+ // Iteration details in collapsible section
84
+ if (report.results.length > 0) {
85
+ lines.push('');
86
+ lines.push('<details><summary>View iteration details</summary>');
87
+ lines.push('');
88
+ // Group results by step
89
+ const byStep = new Map();
90
+ for (const r of report.results) {
91
+ const arr = byStep.get(r.stepId) ?? [];
92
+ arr.push(r);
93
+ byStep.set(r.stepId, arr);
94
+ }
95
+ for (const [stepId, results] of byStep) {
96
+ lines.push(`### ${stepId}`);
97
+ lines.push('');
98
+ lines.push('| Iteration | Passed | Duration | Assertions |');
99
+ lines.push('|-----------|--------|----------|------------|');
100
+ for (const r of results) {
101
+ const passIcon = r.passed ? '✅' : '❌';
102
+ const dur = `${(r.durationMs / 1000).toFixed(1)}s`;
103
+ const assertions = r.assertionResults
104
+ .map(a => `${a.passed ? '✅' : '❌'} ${a.type}`)
105
+ .join(', ');
106
+ lines.push(`| ${r.iteration} | ${passIcon} | ${dur} | ${assertions} |`);
107
+ }
108
+ lines.push('');
109
+ }
110
+ lines.push('</details>');
111
+ }
112
+ return lines.join('\n');
113
+ }
114
+ //# sourceMappingURL=github-comment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-comment.js","sourceRoot":"","sources":["../../src/reporters/github-comment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ScenarioReport } from '../core/types.js';
2
+ export declare function generateHtmlReport(report: ScenarioReport): string;
3
+ //# sourceMappingURL=html-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-reporter.d.ts","sourceRoot":"","sources":["../../src/reporters/html-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,kBAAkB,CAAC;AAEhF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAuFjE"}
@@ -0,0 +1,152 @@
1
+ export function generateHtmlReport(report) {
2
+ const reportJson = JSON.stringify(report);
3
+ const passColor = '#22c55e';
4
+ const failColor = '#ef4444';
5
+ const verdictColor = report.allPassed ? passColor : failColor;
6
+ const verdictText = report.allPassed ? 'PASSED' : 'FAILED';
7
+ return `<!DOCTYPE html>
8
+ <html lang="en">
9
+ <head>
10
+ <meta charset="UTF-8">
11
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
12
+ <title>Stepproof — ${escHtml(report.scenarioName)}</title>
13
+ <style>
14
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
15
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0f172a; color: #e2e8f0; line-height: 1.6; padding: 2rem; max-width: 900px; margin: 0 auto; }
16
+ h1 { font-size: 1.5rem; font-weight: 600; margin-bottom: 0.25rem; }
17
+ .meta { color: #94a3b8; font-size: 0.875rem; margin-bottom: 1.5rem; }
18
+ .meta span { margin-right: 1.5rem; }
19
+ .verdict { display: inline-block; padding: 0.25rem 0.75rem; border-radius: 4px; font-weight: 700; font-size: 0.875rem; background: ${verdictColor}20; color: ${verdictColor}; border: 1px solid ${verdictColor}40; margin-bottom: 1.5rem; }
20
+ .card { background: #1e293b; border-radius: 8px; padding: 1.25rem; margin-bottom: 1rem; border: 1px solid #334155; }
21
+ .step-header { display: flex; align-items: center; justify-content: space-between; cursor: pointer; user-select: none; }
22
+ .step-header:hover { opacity: 0.85; }
23
+ .step-name { font-weight: 600; font-size: 1.05rem; }
24
+ .step-icon { font-size: 1.1rem; margin-right: 0.5rem; }
25
+ .step-rate { font-size: 0.875rem; color: #94a3b8; }
26
+ .bar-container { background: #334155; border-radius: 4px; height: 8px; margin: 0.75rem 0; overflow: hidden; }
27
+ .bar-fill { height: 100%; border-radius: 4px; transition: width 0.3s ease; }
28
+ .bar-pass { background: ${passColor}; }
29
+ .bar-fail { background: ${failColor}; }
30
+ .step-stats { display: flex; gap: 1.5rem; font-size: 0.8rem; color: #94a3b8; }
31
+ .iterations { display: none; margin-top: 1rem; border-top: 1px solid #334155; padding-top: 0.75rem; }
32
+ .iterations.open { display: block; }
33
+ .iter-row { display: flex; align-items: center; gap: 0.75rem; padding: 0.4rem 0; font-size: 0.8rem; border-bottom: 1px solid #1e293b; }
34
+ .iter-row:last-child { border-bottom: none; }
35
+ .iter-badge { display: inline-block; width: 18px; height: 18px; border-radius: 50%; text-align: center; line-height: 18px; font-size: 0.65rem; font-weight: 700; color: #fff; flex-shrink: 0; }
36
+ .iter-pass { background: ${passColor}; }
37
+ .iter-fail { background: ${failColor}; }
38
+ .iter-assertions { color: #94a3b8; flex: 1; }
39
+ .iter-duration { color: #64748b; white-space: nowrap; }
40
+ .iter-output { margin-top: 0.25rem; font-family: 'SF Mono', 'Fira Code', monospace; font-size: 0.75rem; color: #cbd5e1; background: #0f172a; padding: 0.5rem; border-radius: 4px; max-height: 120px; overflow-y: auto; white-space: pre-wrap; word-break: break-word; display: none; }
41
+ .iter-output.open { display: block; }
42
+ .iter-toggle { cursor: pointer; color: #60a5fa; font-size: 0.75rem; }
43
+ .iter-toggle:hover { text-decoration: underline; }
44
+ .assertion-line { font-size: 0.75rem; padding-left: 1.5rem; }
45
+ .assertion-pass { color: ${passColor}; }
46
+ .assertion-fail { color: ${failColor}; }
47
+ .summary-bar { display: flex; gap: 1.5rem; margin-top: 1rem; font-size: 0.8rem; color: #94a3b8; }
48
+ .chevron { transition: transform 0.2s; display: inline-block; }
49
+ .chevron.open { transform: rotate(90deg); }
50
+ @media (max-width: 640px) { body { padding: 1rem; } .step-stats { flex-wrap: wrap; gap: 0.75rem; } }
51
+ </style>
52
+ </head>
53
+ <body>
54
+
55
+ <h1>${escHtml(report.scenarioName)}</h1>
56
+ <div class="meta">
57
+ <span>${new Date(report.startedAt).toLocaleString()}</span>
58
+ <span>${formatDuration(report.durationMs)}</span>
59
+ <span>${report.iterations} iteration${report.iterations === 1 ? '' : 's'}</span>
60
+ </div>
61
+ <div class="verdict">${verdictText}</div>
62
+
63
+ ${report.steps.map((step, i) => renderStep(step, report.results.filter(r => r.stepId === step.stepId), i)).join('\n')}
64
+
65
+ <div class="summary-bar">
66
+ <span>${report.steps.filter(s => !s.belowThreshold).length}/${report.steps.length} steps passed</span>
67
+ <span>Total: ${formatDuration(report.durationMs)}</span>
68
+ </div>
69
+
70
+ <script>
71
+ const REPORT = ${reportJson};
72
+
73
+ function toggleIterations(idx) {
74
+ const el = document.getElementById('iters-' + idx);
75
+ const chev = document.getElementById('chev-' + idx);
76
+ if (el) { el.classList.toggle('open'); }
77
+ if (chev) { chev.classList.toggle('open'); }
78
+ }
79
+
80
+ function toggleOutput(id) {
81
+ const el = document.getElementById(id);
82
+ if (el) { el.classList.toggle('open'); }
83
+ }
84
+ </script>
85
+ </body>
86
+ </html>`;
87
+ }
88
+ function renderStep(step, results, index) {
89
+ const pct = (step.passRate * 100).toFixed(1);
90
+ const threshPct = (step.minPassRate * 100).toFixed(0);
91
+ const icon = step.belowThreshold ? '&#10005;' : '&#10003;';
92
+ const iconColor = step.belowThreshold ? '#ef4444' : '#22c55e';
93
+ const barColor = step.belowThreshold ? 'bar-fail' : 'bar-pass';
94
+ const avgDur = step.avgDurationMs ? formatDuration(step.avgDurationMs) : '';
95
+ const costStr = step.totalCostUsd > 0 ? `$${step.totalCostUsd.toFixed(4)} total / $${step.avgCostUsd.toFixed(4)} avg` : '';
96
+ const iterRows = results.map((r, i) => {
97
+ const badge = r.passed ? 'iter-pass' : 'iter-fail';
98
+ const sym = r.passed ? '&#10003;' : '&#10005;';
99
+ const iterCost = r.costUsd != null && r.costUsd > 0 ? ` &middot; $${r.costUsd.toFixed(4)}` : '';
100
+ const assertions = r.assertionResults.map(a => {
101
+ const cls = a.passed ? 'assertion-pass' : 'assertion-fail';
102
+ const asym = a.passed ? '&#10003;' : '&#10005;';
103
+ return `<div class="assertion-line ${cls}">${asym} ${escHtml(a.type)}${a.message ? ': ' + escHtml(a.message) : ''}</div>`;
104
+ }).join('');
105
+ const outputId = `output-${index}-${i}`;
106
+ return `<div>
107
+ <div class="iter-row">
108
+ <span class="iter-badge ${badge}">${sym}</span>
109
+ <span>Iteration ${r.iteration}</span>
110
+ <span class="iter-duration">${formatDuration(r.durationMs)}${iterCost}</span>
111
+ <span class="iter-toggle" onclick="toggleOutput('${outputId}')">output</span>
112
+ </div>
113
+ ${assertions}
114
+ <div class="iter-output" id="${outputId}">${escHtml(r.output || '(no output)')}</div>
115
+ </div>`;
116
+ }).join('');
117
+ return `<div class="card">
118
+ <div class="step-header" onclick="toggleIterations(${index})">
119
+ <div>
120
+ <span class="step-icon" style="color:${iconColor}">${icon}</span>
121
+ <span class="step-name">${escHtml(step.stepId)}</span>
122
+ </div>
123
+ <div style="display:flex;align-items:center;gap:0.75rem;">
124
+ <span class="step-rate">${pct}% (threshold: ${threshPct}%)</span>
125
+ <span class="chevron" id="chev-${index}">&#9654;</span>
126
+ </div>
127
+ </div>
128
+ <div class="bar-container"><div class="bar-fill ${barColor}" style="width:${pct}%"></div></div>
129
+ <div class="step-stats">
130
+ <span>${step.passes}/${step.totalRuns} passed</span>
131
+ <span>${step.failures} failed</span>
132
+ ${avgDur ? `<span>avg ${avgDur}</span>` : ''}
133
+ ${costStr ? `<span>${costStr}</span>` : ''}
134
+ </div>
135
+ <div class="iterations" id="iters-${index}">
136
+ ${iterRows}
137
+ </div>
138
+ </div>`;
139
+ }
140
+ function formatDuration(ms) {
141
+ if (ms < 1000)
142
+ return `${ms}ms`;
143
+ if (ms < 60_000)
144
+ return `${(ms / 1000).toFixed(1)}s`;
145
+ const mins = Math.floor(ms / 60_000);
146
+ const secs = ((ms % 60_000) / 1000).toFixed(0);
147
+ return `${mins}m ${secs}s`;
148
+ }
149
+ function escHtml(s) {
150
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
151
+ }
152
+ //# sourceMappingURL=html-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-reporter.js","sourceRoot":"","sources":["../../src/reporters/html-reporter.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,13 @@
1
1
  import type { ScenarioReport } from '../core/types.js';
2
- export declare function printReport(report: ScenarioReport, reportPath?: string): void;
2
+ import type { BaselineComparison } from '../baseline.js';
3
+ import type { CacheStats } from '../core/scenario-runner.js';
4
+ import type { ComparisonReport } from '../commands/compare.js';
5
+ export interface PrintReportOptions {
6
+ reportPath?: string;
7
+ baselineComparison?: BaselineComparison;
8
+ cacheStats?: CacheStats;
9
+ }
10
+ export declare function printReport(report: ScenarioReport, reportPathOrOpts?: string | PrintReportOptions): void;
3
11
  export declare function printProgress(stepId: string, iteration: number, total: number): void;
12
+ export declare function formatComparison(report: ComparisonReport): string;
4
13
  //# sourceMappingURL=terminal-reporter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"terminal-reporter.d.ts","sourceRoot":"","sources":["../../src/reporters/terminal-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,kBAAkB,CAAC;AAKpE,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAwB7E;AAsDD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAKpF"}
1
+ {"version":3,"file":"terminal-reporter.d.ts","sourceRoot":"","sources":["../../src/reporters/terminal-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,kBAAkB,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAkB,MAAM,gBAAgB,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAK/D,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CA+ExG;AAwFD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAKpF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CA6DjE"}