@vitronai/themis 0.1.0-beta.4 → 0.1.2

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.
@@ -0,0 +1,120 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/vitron-ai/themis/docs/schemas/contract-diff.v1.json",
4
+ "title": "Themis Contract Diff v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema", "runId", "createdAt", "artifacts", "summary", "items"],
8
+ "properties": {
9
+ "schema": {
10
+ "type": "string",
11
+ "const": "themis.contract.diff.v1"
12
+ },
13
+ "runId": {
14
+ "type": "string"
15
+ },
16
+ "createdAt": {
17
+ "type": "string"
18
+ },
19
+ "artifacts": {
20
+ "type": "object",
21
+ "additionalProperties": false,
22
+ "required": ["contractDiff"],
23
+ "properties": {
24
+ "contractDiff": {
25
+ "type": "string"
26
+ }
27
+ }
28
+ },
29
+ "summary": {
30
+ "type": "object",
31
+ "additionalProperties": false,
32
+ "required": ["total", "created", "updated", "drifted", "unchanged"],
33
+ "properties": {
34
+ "total": { "type": "number" },
35
+ "created": { "type": "number" },
36
+ "updated": { "type": "number" },
37
+ "drifted": { "type": "number" },
38
+ "unchanged": { "type": "number" }
39
+ }
40
+ },
41
+ "items": {
42
+ "type": "array",
43
+ "items": {
44
+ "$ref": "#/$defs/item"
45
+ }
46
+ }
47
+ },
48
+ "$defs": {
49
+ "item": {
50
+ "type": "object",
51
+ "additionalProperties": false,
52
+ "required": ["key", "name", "file", "testName", "fullName", "contractFile", "status", "updateCommand", "diff"],
53
+ "properties": {
54
+ "key": { "type": "string" },
55
+ "name": { "type": "string" },
56
+ "file": { "type": "string" },
57
+ "testName": { "type": "string" },
58
+ "fullName": { "type": "string" },
59
+ "contractFile": { "type": "string" },
60
+ "status": {
61
+ "type": "string",
62
+ "enum": ["created", "updated", "drifted", "unchanged"]
63
+ },
64
+ "updateCommand": { "type": "string" },
65
+ "diff": {
66
+ "$ref": "#/$defs/diff"
67
+ }
68
+ }
69
+ },
70
+ "diff": {
71
+ "type": "object",
72
+ "additionalProperties": false,
73
+ "required": ["equal", "unchangedCount", "added", "removed", "changed"],
74
+ "properties": {
75
+ "equal": { "type": "boolean" },
76
+ "unchangedCount": { "type": "number" },
77
+ "added": {
78
+ "type": "array",
79
+ "items": { "$ref": "#/$defs/afterEntry" }
80
+ },
81
+ "removed": {
82
+ "type": "array",
83
+ "items": { "$ref": "#/$defs/beforeEntry" }
84
+ },
85
+ "changed": {
86
+ "type": "array",
87
+ "items": { "$ref": "#/$defs/changeEntry" }
88
+ }
89
+ }
90
+ },
91
+ "afterEntry": {
92
+ "type": "object",
93
+ "additionalProperties": true,
94
+ "required": ["path", "after"],
95
+ "properties": {
96
+ "path": { "type": "string" },
97
+ "after": {}
98
+ }
99
+ },
100
+ "beforeEntry": {
101
+ "type": "object",
102
+ "additionalProperties": true,
103
+ "required": ["path", "before"],
104
+ "properties": {
105
+ "path": { "type": "string" },
106
+ "before": {}
107
+ }
108
+ },
109
+ "changeEntry": {
110
+ "type": "object",
111
+ "additionalProperties": true,
112
+ "required": ["path", "before", "after"],
113
+ "properties": {
114
+ "path": { "type": "string" },
115
+ "before": {},
116
+ "after": {}
117
+ }
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,117 @@
1
+ # Showcase Comparisons
2
+
3
+ These examples are the clearest proof of where Themis should beat Jest and Vitest for AI-agent-heavy JS/TS test loops.
4
+
5
+ ## 1. Replace snapshots with contract capture
6
+
7
+ Jest/Vitest snapshot flow:
8
+
9
+ ```ts
10
+ test('banner state', () => {
11
+ expect(renderBanner(model)).toMatchSnapshot();
12
+ });
13
+ ```
14
+
15
+ Themis contract flow:
16
+
17
+ ```ts
18
+ test('banner state', () => {
19
+ captureContract('banner state', {
20
+ title: renderBanner(model).title,
21
+ actions: renderBanner(model).actions
22
+ }, {
23
+ maskPaths: ['$.requestId'],
24
+ sortArrays: true
25
+ });
26
+ });
27
+ ```
28
+
29
+ Why it wins:
30
+
31
+ - captures only the fields that matter
32
+ - masks volatile IDs and timestamps instead of accepting noisy churn
33
+ - writes machine-readable drift to `.themis/contract-diff.json`
34
+ - updates stay explicit with `npx themis test --update-contracts`
35
+
36
+ ## 2. Migrate Jest/Vitest suites without a rewrite freeze
37
+
38
+ Starting suite:
39
+
40
+ ```ts
41
+ import { describe, it, expect } from '@jest/globals';
42
+
43
+ describe('worker', () => {
44
+ it('records calls', () => {
45
+ const fnRef = fn();
46
+ fnRef('ok');
47
+ expect(fnRef).toBeCalledTimes(1);
48
+ expect({ status: 'ok' }).toStrictEqual({ status: 'ok' });
49
+ });
50
+ });
51
+ ```
52
+
53
+ After `npx themis migrate jest --convert`:
54
+
55
+ ```ts
56
+ describe('worker', () => {
57
+ test('records calls', () => {
58
+ const fnRef = fn();
59
+ fnRef('ok');
60
+ expect(fnRef).toHaveBeenCalledTimes(1);
61
+ expect({ status: 'ok' }).toEqual({ status: 'ok' });
62
+ });
63
+ });
64
+ ```
65
+
66
+ Why it wins:
67
+
68
+ - removes common compatibility imports
69
+ - rewrites common matcher aliases into native Themis forms
70
+ - emits `.themis/migration-report.json` so agents and humans can track what changed
71
+
72
+ ## 3. Give agents a repair loop humans can still read
73
+
74
+ Jest/Vitest mostly produce console text plus optional snapshots.
75
+
76
+ Themis produces:
77
+
78
+ - `.themis/failed-tests.json`
79
+ - `.themis/run-diff.json`
80
+ - `.themis/fix-handoff.json`
81
+ - `.themis/contract-diff.json`
82
+
83
+ Why it wins:
84
+
85
+ - agents get deterministic machine-readable artifacts
86
+ - humans still get focused CLI and HTML diffs
87
+ - rerun, repair, and acceptance loops are explicit instead of buried in terminal logs
88
+
89
+ ## 4. Keep local loops fast without losing determinism
90
+
91
+ Themis local loop:
92
+
93
+ ```bash
94
+ npx themis test --watch --isolation in-process --cache --reporter next
95
+ ```
96
+
97
+ Why it wins:
98
+
99
+ - optimized for short edit-rerun-review cycles
100
+ - preserves canonical JSON and artifact contracts
101
+ - keeps failure diffs and contract drift visible while iterating
102
+
103
+ ## 5. Surface migration and contract work directly in VS Code
104
+
105
+ Themis sidebar groups:
106
+
107
+ - `Contract Review`
108
+ - `Migration Review`
109
+ - `Failures`
110
+ - `Generated Review`
111
+
112
+ Why it wins:
113
+
114
+ - the editor reads artifact contracts instead of reimplementing runner logic
115
+ - one action accepts reviewed contracts
116
+ - one action reruns migration codemods for the detected framework
117
+ - humans and agents operate from the same source of truth
@@ -7,8 +7,10 @@ This is the intended shape of the editor UX:
7
7
  - a Themis activity-bar container
8
8
  - a results sidebar driven by `.themis/*` artifacts
9
9
  - commands to run tests, rerun failures, refresh results, and open the HTML report
10
+ - commands to accept reviewed contract baselines and rerun migration codemods
10
11
  - failure navigation that jumps from artifact data into the source file
11
12
  - generated-review navigation for source files, generated tests, hint sidecars, and backlog items
13
+ - contract-review and migration-review groups driven by `.themis/contract-diff.json` and `.themis/migration-report.json`
12
14
 
13
15
  ## Current MVP Scope
14
16
 
@@ -17,6 +19,8 @@ The scaffold currently supports:
17
19
  - reading `.themis/last-run.json`
18
20
  - reading `.themis/failed-tests.json`
19
21
  - reading `.themis/run-diff.json`
22
+ - reading `.themis/contract-diff.json`
23
+ - reading `.themis/migration-report.json`
20
24
  - reading `.themis/generate-last.json`
21
25
  - reading `.themis/generate-map.json`
22
26
  - reading `.themis/generate-backlog.json`
@@ -27,6 +31,15 @@ The scaffold currently supports:
27
31
 
28
32
  The extension is intentionally thin. It shells out to Themis commands and treats the CLI plus artifacts as the canonical contract.
29
33
 
34
+ Current command surface:
35
+
36
+ - `Themis: Run Tests`
37
+ - `Themis: Rerun Failed`
38
+ - `Themis: Update Contracts`
39
+ - `Themis: Run Migration Codemods`
40
+ - `Themis: Open HTML Report`
41
+ - `Themis: Refresh Results`
42
+
30
43
  ## Local Development
31
44
 
32
45
  Open the repository in VS Code and use an Extension Development Host pointed at:
@@ -6,6 +6,7 @@ The core positioning is simple:
6
6
 
7
7
  - The best unit test framework for AI agents in Node.js and TypeScript
8
8
  - An AI verdict engine for human and agent review loops
9
+ - A contract-first alternative to snapshot-heavy test maintenance
9
10
 
10
11
  ## What "Next-Gen" Means Here
11
12
 
@@ -42,6 +43,8 @@ Themis supports structured outputs for tooling loops:
42
43
 
43
44
  `--rerun-failed`, `--watch`, and test-name filtering (`--match`) reduce iteration time and keep failure focus tight.
44
45
 
46
+ The fast local loop matters for AI-assisted editing too: `--watch --isolation in-process --cache` gives agents and humans a short edit-run-review cycle instead of full-suite friction.
47
+
45
48
  ## 4) Modern JS/TS Project Parity
46
49
 
47
50
  Themis is built for current Node.js and TypeScript repos:
@@ -69,11 +72,38 @@ Themis keeps a lean JS runtime and ships first-party typings:
69
72
  Themis ships workflow features agents can use directly:
70
73
 
71
74
  - direct contract assertions instead of snapshot-file churn
75
+ - generated contract tests that keep baselines in readable source instead of opaque snapshot blobs
76
+ - machine-readable artifacts that let agents inspect and explain changes before updating tests
72
77
  - mocks and spies with `fn`, `spyOn`, and `mock`
73
78
  - `.themis/run-diff.json` and `.themis/run-history.json`
74
79
  - HTML verdict reports for human review
75
80
 
76
- ## 8) Performance Discipline, Not Guesswork
81
+ ### Comparable To Snapshot Workflows, Without Snapshot Rot
82
+
83
+ Many teams use snapshots because they want cheap baseline capture, safe review, and easy updates. Themis should meet that need without copying the snapshot mechanism.
84
+
85
+ Themis favors:
86
+
87
+ - normalized contract assertions over broad serialized dumps
88
+ - readable generated tests over hidden `.snap` files
89
+ - field-level and behavior-level diffs over large text churn
90
+ - intentional regeneration and migration flows over blanket "accept all changes"
91
+
92
+ The result is comparable coverage value with clearer semantics for both humans and agents.
93
+
94
+ ## 8) Migration Path From Jest And Vitest
95
+
96
+ Adoption does not need to be a rewrite.
97
+
98
+ Themis already supports:
99
+
100
+ - runtime compatibility for `@jest/globals`, `vitest`, and `@testing-library/react`
101
+ - `themis migrate <jest|vitest>` scaffolding for incremental adoption
102
+ - optional import rewriting to a local compatibility bridge
103
+
104
+ The strategic direction is clear: start with compatibility, then move suites toward native Themis contracts and intent-first tests as teams touch them.
105
+
106
+ ## 9) Performance Discipline, Not Guesswork
77
107
 
78
108
  Performance is measured and guarded:
79
109
 
@@ -81,13 +111,13 @@ Performance is measured and guarded:
81
111
  - regression gate (`npm run benchmark:gate`)
82
112
  - threshold config (`benchmark-gate.json`)
83
113
 
84
- ## 9) CLI Designed for Humans and Machines
114
+ ## 10) CLI Designed for Humans and Machines
85
115
 
86
116
  - high-signal human reporter (`--next`)
87
117
  - strict machine reporter outputs (`--json`, `--agent`)
88
118
  - branded banner for human mode only
89
119
 
90
- ## 10) Editor Surface Without Replacing The CLI
120
+ ## 11) Editor Surface Without Replacing The CLI
91
121
 
92
122
  Themis includes a thin VS Code extension scaffold that reads `.themis/*` artifacts, reruns tests, and opens the HTML report. The CLI remains the source of truth.
93
123
 
@@ -108,4 +138,5 @@ If you describe Themis publicly, use this framing:
108
138
  - "The best unit test framework for AI agents in Node.js and TypeScript"
109
139
  - "An AI verdict engine for human and agent review loops"
110
140
  - "Intent-first testing with deterministic reruns and machine-readable artifacts"
141
+ - "A better alternative to snapshot-heavy workflows: explicit contracts, readable diffs, intentional updates"
111
142
  - "JS-fast runtime with first-party TypeScript DX and benchmark-gated discipline"
package/globals.d.ts CHANGED
@@ -44,6 +44,7 @@ declare global {
44
44
  var advanceTimersByTime: (ms: number) => void;
45
45
  var runAllTimers: () => void;
46
46
  var flushMicrotasks: FlushMicrotasks;
47
+ var captureContract: import('./index').CaptureContract;
47
48
  var mockFetch: (handlerOrResponse: unknown) => FetchMock;
48
49
  var restoreFetch: () => void;
49
50
  var resetFetchMocks: () => void;
package/index.d.ts CHANGED
@@ -18,6 +18,7 @@ export interface TestResult {
18
18
  export interface FileResult {
19
19
  file: string;
20
20
  tests: TestResult[];
21
+ contracts?: ContractCaptureEvent[];
21
22
  }
22
23
 
23
24
  export interface RunMeta {
@@ -78,6 +79,7 @@ export interface RunOptions {
78
79
  tsconfigPath?: string | null;
79
80
  isolation?: 'worker' | 'in-process';
80
81
  cache?: boolean;
82
+ updateContracts?: boolean;
81
83
  }
82
84
 
83
85
  export interface ThemisConfig {
@@ -100,6 +102,8 @@ export interface MigrationResult {
100
102
  packageUpdated: boolean;
101
103
  rewriteImports: boolean;
102
104
  rewrittenFiles: string[];
105
+ convert?: boolean;
106
+ convertedFiles?: string[];
103
107
  reportPath: string;
104
108
  report: {
105
109
  schema: 'themis.migration.report.v1';
@@ -112,18 +116,23 @@ export interface MigrationResult {
112
116
  testingLibraryReact: number;
113
117
  rewrittenFiles: number;
114
118
  rewrittenImports: number;
119
+ convertedFiles: number;
120
+ convertedAssertions: number;
121
+ removedImports: number;
115
122
  };
116
123
  files: Array<{
117
124
  file: string;
118
125
  imports: string[];
119
126
  }>;
120
127
  rewrites: string[];
128
+ conversions?: string[];
121
129
  nextActions: string[];
122
130
  };
123
131
  }
124
132
 
125
133
  export interface MigrationOptions {
126
134
  rewriteImports?: boolean;
135
+ convert?: boolean;
127
136
  }
128
137
 
129
138
  export interface GenerateOptions {
@@ -208,6 +217,7 @@ export interface RunArtifactPaths {
208
217
  runDiff: string;
209
218
  runHistory: string;
210
219
  fixHandoff: string;
220
+ contractDiff: string;
211
221
  }
212
222
 
213
223
  export interface RunComparison {
@@ -226,6 +236,32 @@ export interface RunArtifacts {
226
236
  paths: RunArtifactPaths;
227
237
  }
228
238
 
239
+ export interface ContractDiffEntry {
240
+ path: string;
241
+ before?: unknown;
242
+ after?: unknown;
243
+ }
244
+
245
+ export interface ContractDiff {
246
+ equal: boolean;
247
+ unchangedCount: number;
248
+ added: ContractDiffEntry[];
249
+ removed: ContractDiffEntry[];
250
+ changed: ContractDiffEntry[];
251
+ }
252
+
253
+ export interface ContractCaptureEvent {
254
+ key: string;
255
+ name: string;
256
+ status: 'created' | 'updated' | 'drifted' | 'unchanged';
257
+ contractFile: string;
258
+ file: string;
259
+ testName: string;
260
+ fullName: string;
261
+ updateCommand: string;
262
+ diff: ContractDiff;
263
+ }
264
+
229
265
  export interface GenerateGateFailure {
230
266
  code: string;
231
267
  count: number;
@@ -448,6 +484,23 @@ export interface GenerateSummary {
448
484
  helperRemoved: boolean;
449
485
  }
450
486
 
487
+ export interface ContractDiffPayload {
488
+ schema: 'themis.contract.diff.v1';
489
+ runId: string;
490
+ createdAt: string;
491
+ artifacts: {
492
+ contractDiff: string;
493
+ };
494
+ summary: {
495
+ total: number;
496
+ created: number;
497
+ updated: number;
498
+ drifted: number;
499
+ unchanged: number;
500
+ };
501
+ items: ContractCaptureEvent[];
502
+ }
503
+
451
504
  export function main(argv: string[]): Promise<void>;
452
505
  export function collectAndRun(filePath: string, options?: Omit<RunOptions, 'maxWorkers'>): Promise<FileResult>;
453
506
  export function runTests(files: string[], options?: RunOptions): Promise<RunResult>;
@@ -471,6 +524,10 @@ export interface MockResult {
471
524
  value: unknown;
472
525
  }
473
526
 
527
+ export interface CaptureContract {
528
+ (name: string, value: unknown, options?: { file?: string }): unknown;
529
+ }
530
+
474
531
  export interface MockState<TArgs extends unknown[] = unknown[]> {
475
532
  calls: TArgs[];
476
533
  results: MockResult[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitronai/themis",
3
- "version": "0.1.0-beta.4",
3
+ "version": "0.1.2",
4
4
  "description": "Intent-first unit test framework for AI agents in Node.js and TypeScript, powered by an AI verdict engine",
5
5
  "license": "MIT",
6
6
  "author": "Vitron AI",
package/src/artifacts.js CHANGED
@@ -7,6 +7,7 @@ const FAILED_TESTS_FILE = 'failed-tests.json';
7
7
  const RUN_DIFF_FILE = 'run-diff.json';
8
8
  const RUN_HISTORY_FILE = 'run-history.json';
9
9
  const FIX_HANDOFF_FILE = 'fix-handoff.json';
10
+ const CONTRACT_DIFF_FILE = 'contract-diff.json';
10
11
  const GENERATE_MAP_FILE = 'generate-map.json';
11
12
  const GENERATE_BACKLOG_FILE = 'generate-backlog.json';
12
13
 
@@ -23,7 +24,8 @@ function writeRunArtifacts(cwd, result) {
23
24
  failedTests: path.join(ARTIFACT_DIR, FAILED_TESTS_FILE),
24
25
  runDiff: path.join(ARTIFACT_DIR, RUN_DIFF_FILE),
25
26
  runHistory: path.join(ARTIFACT_DIR, RUN_HISTORY_FILE),
26
- fixHandoff: path.join(ARTIFACT_DIR, FIX_HANDOFF_FILE)
27
+ fixHandoff: path.join(ARTIFACT_DIR, FIX_HANDOFF_FILE),
28
+ contractDiff: path.join(ARTIFACT_DIR, CONTRACT_DIFF_FILE)
27
29
  };
28
30
 
29
31
  result.artifacts = {
@@ -83,6 +85,14 @@ function writeRunArtifacts(cwd, result) {
83
85
  });
84
86
  fs.writeFileSync(historyPath, `${stringifyArtifact(nextHistory)}\n`, 'utf8');
85
87
 
88
+ const contractDiffPayload = buildContractDiffPayload(result, {
89
+ runId,
90
+ createdAt: new Date().toISOString(),
91
+ relativePaths
92
+ });
93
+ const contractDiffPath = path.join(artifactDir, CONTRACT_DIFF_FILE);
94
+ fs.writeFileSync(contractDiffPath, `${stringifyArtifact(contractDiffPayload)}\n`, 'utf8');
95
+
86
96
  const fixHandoffPath = path.join(artifactDir, FIX_HANDOFF_FILE);
87
97
  let fixHandoff = null;
88
98
  if (failedTests.length > 0) {
@@ -100,9 +110,11 @@ function writeRunArtifacts(cwd, result) {
100
110
  diffPath,
101
111
  historyPath,
102
112
  fixHandoffPath,
113
+ contractDiffPath,
103
114
  failuresPayload,
104
115
  comparison,
105
- fixHandoff
116
+ fixHandoff,
117
+ contractDiffPayload
106
118
  };
107
119
  }
108
120
 
@@ -192,6 +204,55 @@ function collectFailureNames(result) {
192
204
  return names.sort();
193
205
  }
194
206
 
207
+ function buildContractDiffPayload(result, context) {
208
+ const items = [];
209
+ for (const fileEntry of result.files || []) {
210
+ for (const contract of fileEntry.contracts || []) {
211
+ items.push({
212
+ key: String(contract.key || ''),
213
+ name: String(contract.name || ''),
214
+ file: String(fileEntry.file || contract.file || ''),
215
+ testName: String(contract.testName || ''),
216
+ fullName: String(contract.fullName || ''),
217
+ contractFile: String(contract.contractFile || ''),
218
+ status: String(contract.status || 'unchanged'),
219
+ updateCommand: String(contract.updateCommand || ''),
220
+ diff: normalizeContractDiff(contract.diff)
221
+ });
222
+ }
223
+ }
224
+
225
+ const summary = {
226
+ total: items.length,
227
+ created: items.filter((item) => item.status === 'created').length,
228
+ updated: items.filter((item) => item.status === 'updated').length,
229
+ drifted: items.filter((item) => item.status === 'drifted').length,
230
+ unchanged: items.filter((item) => item.status === 'unchanged').length
231
+ };
232
+
233
+ return {
234
+ schema: 'themis.contract.diff.v1',
235
+ runId: context.runId,
236
+ createdAt: context.createdAt,
237
+ artifacts: {
238
+ contractDiff: context.relativePaths.contractDiff
239
+ },
240
+ summary,
241
+ items
242
+ };
243
+ }
244
+
245
+ function normalizeContractDiff(diff) {
246
+ const safeDiff = diff || {};
247
+ return {
248
+ equal: Boolean(safeDiff.equal),
249
+ unchangedCount: Number(safeDiff.unchangedCount || 0),
250
+ added: Array.isArray(safeDiff.added) ? safeDiff.added : [],
251
+ removed: Array.isArray(safeDiff.removed) ? safeDiff.removed : [],
252
+ changed: Array.isArray(safeDiff.changed) ? safeDiff.changed : []
253
+ };
254
+ }
255
+
195
256
  function createRunId(startedAt) {
196
257
  return String(startedAt || '')
197
258
  .replace(/[:.]/g, '-')
package/src/cli.js CHANGED
@@ -80,6 +80,9 @@ async function main(argv) {
80
80
  if (result.rewriteImports) {
81
81
  console.log(`Imports: rewrote ${result.rewrittenFiles.length} file(s) to local Themis compatibility imports.`);
82
82
  }
83
+ if (result.convertedFiles && result.convertedFiles.length > 0) {
84
+ console.log(`Codemods: converted ${result.convertedFiles.length} file(s) to Themis-native patterns.`);
85
+ }
83
86
  console.log('Runtime compatibility is enabled for @jest/globals, vitest, and @testing-library/react imports.');
84
87
  console.log('Next: run npx themis test or npm run test:themis');
85
88
  return;
@@ -180,6 +183,7 @@ async function executeTestRun(cwd, flags) {
180
183
  match: flags.match || null,
181
184
  allowedFullNames,
182
185
  noMemes: Boolean(flags.noMemes),
186
+ updateContracts: Boolean(flags.updateContracts),
183
187
  cwd,
184
188
  environment,
185
189
  setupFiles: config.setupFiles,
@@ -284,6 +288,10 @@ function parseFlags(args) {
284
288
  flags.noMemes = true;
285
289
  continue;
286
290
  }
291
+ if (token === '--update-contracts') {
292
+ flags.updateContracts = true;
293
+ continue;
294
+ }
287
295
  if (token === '-w' || token === '--watch') {
288
296
  flags.watch = true;
289
297
  continue;
@@ -345,13 +353,18 @@ function parseFlags(args) {
345
353
  function parseMigrateFlags(args) {
346
354
  const flags = {
347
355
  source: args[0],
348
- rewriteImports: false
356
+ rewriteImports: false,
357
+ convert: false
349
358
  };
350
359
 
351
360
  for (let i = 1; i < args.length; i += 1) {
352
361
  const token = args[i];
353
362
  if (token === '--rewrite-imports') {
354
363
  flags.rewriteImports = true;
364
+ continue;
365
+ }
366
+ if (token === '--convert') {
367
+ flags.convert = true;
355
368
  }
356
369
  }
357
370
 
@@ -563,8 +576,8 @@ function printUsage() {
563
576
  console.log(' generate [path] Scan source files and generate Themis contract tests');
564
577
  console.log(' Options: [--json] [--plan] [--output path] [--files a,b] [--match-source regex] [--match-export regex] [--scenario name] [--min-confidence level] [--require-confidence level] [--include regex] [--exclude regex] [--review] [--update] [--clean] [--changed] [--force] [--strict] [--write-hints] [--fail-on-skips] [--fail-on-conflicts]');
565
578
  console.log(' scan [path] Alias for generate');
566
- console.log(' migrate <jest|vitest> [--rewrite-imports] Scaffold an incremental migration bridge for existing suites');
567
- console.log(' test [--json] [--agent] [--next] [--reporter spec|next|json|agent|html] [--workers N] [--stability N] [--environment node|jsdom] [--isolation worker|in-process] [--cache] [-w|--watch] [--html-output path] [--match regex] [--rerun-failed] [--no-memes] [--lexicon classic|themis]');
579
+ console.log(' migrate <jest|vitest> [--rewrite-imports] [--convert] Scaffold an incremental migration bridge for existing suites');
580
+ console.log(' test [--json] [--agent] [--next] [--reporter spec|next|json|agent|html] [--workers N] [--stability N] [--environment node|jsdom] [--isolation worker|in-process] [--cache] [--update-contracts] [-w|--watch] [--html-output path] [--match regex] [--rerun-failed] [--no-memes] [--lexicon classic|themis]');
568
581
  }
569
582
 
570
583
  function printGenerateSummary(summary, cwd) {