@artemiskit/cli 0.1.6 → 0.1.8

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 (44) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/index.js +2653 -4832
  3. package/dist/src/__tests__/helpers/index.d.ts +6 -0
  4. package/dist/src/__tests__/helpers/index.d.ts.map +1 -0
  5. package/dist/src/__tests__/helpers/mock-adapter.d.ts +87 -0
  6. package/dist/src/__tests__/helpers/mock-adapter.d.ts.map +1 -0
  7. package/dist/src/__tests__/helpers/test-utils.d.ts +47 -0
  8. package/dist/src/__tests__/helpers/test-utils.d.ts.map +1 -0
  9. package/dist/src/cli.d.ts.map +1 -1
  10. package/dist/src/commands/compare.d.ts.map +1 -1
  11. package/dist/src/commands/history.d.ts.map +1 -1
  12. package/dist/src/commands/init.d.ts.map +1 -1
  13. package/dist/src/commands/redteam.d.ts.map +1 -1
  14. package/dist/src/commands/report.d.ts.map +1 -1
  15. package/dist/src/commands/run.d.ts.map +1 -1
  16. package/dist/src/ui/errors.d.ts.map +1 -1
  17. package/dist/src/ui/panels.d.ts.map +1 -1
  18. package/dist/src/ui/progress.d.ts.map +1 -1
  19. package/dist/src/ui/utils.d.ts.map +1 -1
  20. package/dist/src/utils/update-checker.d.ts +31 -0
  21. package/dist/src/utils/update-checker.d.ts.map +1 -0
  22. package/package.json +6 -6
  23. package/src/__tests__/helpers/mock-adapter.ts +22 -4
  24. package/src/__tests__/helpers/test-utils.ts +3 -3
  25. package/src/__tests__/integration/compare-command.test.ts +7 -7
  26. package/src/__tests__/integration/config.test.ts +2 -2
  27. package/src/__tests__/integration/history-command.test.ts +2 -2
  28. package/src/__tests__/integration/init-command.test.ts +3 -3
  29. package/src/__tests__/integration/report-command.test.ts +2 -2
  30. package/src/__tests__/integration/ui.test.ts +6 -6
  31. package/src/cli.ts +22 -1
  32. package/src/commands/compare.ts +2 -4
  33. package/src/commands/history.ts +2 -2
  34. package/src/commands/init.ts +52 -12
  35. package/src/commands/redteam.ts +6 -6
  36. package/src/commands/report.ts +3 -3
  37. package/src/commands/run.ts +4 -4
  38. package/src/commands/stress.ts +4 -4
  39. package/src/ui/errors.ts +1 -1
  40. package/src/ui/live-status.ts +1 -1
  41. package/src/ui/panels.ts +2 -2
  42. package/src/ui/progress.ts +1 -1
  43. package/src/ui/utils.ts +4 -3
  44. package/src/utils/update-checker.ts +121 -0
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Test helpers index
3
+ */
4
+ export * from './mock-adapter.js';
5
+ export * from './test-utils.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/helpers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Mock adapter for CLI integration tests
3
+ *
4
+ * This module provides mock types and adapters for testing CLI commands
5
+ * without making actual LLM API calls.
6
+ */
7
+ /** Message format for chat interactions */
8
+ export interface MockMessage {
9
+ role: 'system' | 'user' | 'assistant';
10
+ content: string;
11
+ }
12
+ /** Response from the mock adapter */
13
+ export interface MockLLMResponse {
14
+ content: string;
15
+ usage: {
16
+ input: number;
17
+ output: number;
18
+ };
19
+ }
20
+ /** Mock adapter interface */
21
+ export interface MockLLMAdapter {
22
+ chat: (messages: MockMessage[]) => Promise<MockLLMResponse>;
23
+ }
24
+ export interface MockResponse {
25
+ content: string;
26
+ latencyMs?: number;
27
+ tokens?: {
28
+ input: number;
29
+ output: number;
30
+ };
31
+ }
32
+ export interface MockAdapterOptions {
33
+ responses?: Map<string, MockResponse>;
34
+ defaultResponse?: MockResponse;
35
+ shouldFail?: boolean;
36
+ failureMessage?: string;
37
+ }
38
+ /**
39
+ * Creates a mock LLM adapter for testing
40
+ */
41
+ export declare function createMockAdapter(options?: MockAdapterOptions): MockLLMAdapter;
42
+ /**
43
+ * Preset responses for common test scenarios
44
+ */
45
+ export declare const mockResponses: {
46
+ greeting: {
47
+ content: string;
48
+ latencyMs: number;
49
+ tokens: {
50
+ input: number;
51
+ output: number;
52
+ };
53
+ };
54
+ capitals: {
55
+ content: string;
56
+ latencyMs: number;
57
+ tokens: {
58
+ input: number;
59
+ output: number;
60
+ };
61
+ };
62
+ math: {
63
+ content: string;
64
+ latencyMs: number;
65
+ tokens: {
66
+ input: number;
67
+ output: number;
68
+ };
69
+ };
70
+ json: {
71
+ content: string;
72
+ latencyMs: number;
73
+ tokens: {
74
+ input: number;
75
+ output: number;
76
+ };
77
+ };
78
+ code: {
79
+ content: string;
80
+ latencyMs: number;
81
+ tokens: {
82
+ input: number;
83
+ output: number;
84
+ };
85
+ };
86
+ };
87
+ //# sourceMappingURL=mock-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-adapter.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/helpers/mock-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qCAAqC;AACrC,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C;AAED,6BAA6B;AAC7B,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,YAAY,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,cAAc,CAoClF;AAED;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BzB,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Test utilities for CLI integration tests
3
+ */
4
+ /**
5
+ * Creates a temporary directory for test isolation
6
+ */
7
+ export declare function createTestDir(prefix?: string): Promise<string>;
8
+ /**
9
+ * Cleans up a test directory
10
+ */
11
+ export declare function cleanupTestDir(testDir: string): Promise<void>;
12
+ /**
13
+ * Creates a test scenario file
14
+ */
15
+ export declare function createScenarioFile(dir: string, name: string, content: string): Promise<string>;
16
+ /**
17
+ * Creates a test config file
18
+ */
19
+ export declare function createConfigFile(dir: string, config: Record<string, unknown>): Promise<string>;
20
+ /**
21
+ * Sample scenario templates for testing
22
+ */
23
+ export declare const scenarioTemplates: {
24
+ simple: string;
25
+ multiCase: string;
26
+ withProvider: string;
27
+ exactMatch: string;
28
+ regexMatch: string;
29
+ failing: string;
30
+ };
31
+ /**
32
+ * Captures console output during test execution
33
+ */
34
+ export declare class OutputCapture {
35
+ private originalLog;
36
+ private originalError;
37
+ private logs;
38
+ private errors;
39
+ start(): void;
40
+ stop(): {
41
+ logs: string[];
42
+ errors: string[];
43
+ };
44
+ getOutput(): string;
45
+ getErrors(): string;
46
+ }
47
+ //# sourceMappingURL=test-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/helpers/test-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,SAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAI5E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMnE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAMjB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,CAejB;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;CAgG7B,CAAC;AAEF;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,aAAa,CAAuC;IAC5D,OAAO,CAAC,IAAI,CAAgB;IAC5B,OAAO,CAAC,MAAM,CAAgB;IAE9B,KAAK,IAAI,IAAI;IAeb,IAAI,IAAI;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;IAM5C,SAAS,IAAI,MAAM;IAInB,SAAS,IAAI,MAAM;CAGpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wBAAgB,SAAS,IAAI,OAAO,CAmBnC"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,SAAS,IAAI,OAAO,CAuCnC"}
@@ -1 +1 @@
1
- {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../../src/commands/compare.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoHpC,wBAAgB,cAAc,IAAI,OAAO,CAgFxC"}
1
+ {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../../src/commands/compare.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2HpC,wBAAgB,cAAc,IAAI,OAAO,CAkFxC"}
@@ -1 +1 @@
1
- {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../../src/commands/history.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+FpC,wBAAgB,cAAc,IAAI,OAAO,CA2ExC"}
1
+ {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../../src/commands/history.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8FpC,wBAAgB,cAAc,IAAI,OAAO,CAiFxC"}
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+JpC,wBAAgB,WAAW,IAAI,OAAO,CA2ErC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyNpC,wBAAgB,WAAW,IAAI,OAAO,CAqFrC"}
@@ -1 +1 @@
1
- {"version":3,"file":"redteam.d.ts","sourceRoot":"","sources":["../../../src/commands/redteam.ts"],"names":[],"mappings":"AAAA;;GAEG;AA8BH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiCpC,wBAAgB,cAAc,IAAI,OAAO,CAqZxC"}
1
+ {"version":3,"file":"redteam.d.ts","sourceRoot":"","sources":["../../../src/commands/redteam.ts"],"names":[],"mappings":"AAAA;;GAEG;AA8BH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiCpC,wBAAgB,cAAc,IAAI,OAAO,CAyZxC"}
@@ -1 +1 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/commands/report.ts"],"names":[],"mappings":"AAAA;;GAEG;AAWH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoDpC,wBAAgB,aAAa,IAAI,OAAO,CAwEvC"}
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/commands/report.ts"],"names":[],"mappings":"AAAA;;GAEG;AAWH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoDpC,wBAAgB,aAAa,IAAI,OAAO,CA2EvC"}
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/commands/run.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmCpC,wBAAgB,UAAU,IAAI,OAAO,CA4MpC"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/commands/run.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmCpC,wBAAgB,UAAU,IAAI,OAAO,CA8MpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/ui/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,WAAW,YAAY;IAC3B,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CA0ErD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAoD5F;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,KAAK,GACX,YAAY,CAqEd"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/ui/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,WAAW,YAAY;IAC3B,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAgFrD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAsD5F;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,YAAY,CAqEpF"}
@@ -1 +1 @@
1
- {"version":3,"file":"panels.d.ts","sourceRoot":"","sources":["../../../src/ui/panels.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CA6B5D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAuDxE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CA4C1E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAsBpE"}
1
+ {"version":3,"file":"panels.d.ts","sourceRoot":"","sources":["../../../src/ui/panels.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAiC5D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CA0DxE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CA4C1E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAsBpE"}
@@ -1 +1 @@
1
- {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../../src/ui/progress.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,WAAW,kBAAkB;IACjC,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iCAAiC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAWD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAwBR;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,UAAU,CAAM;gBAEZ,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB;IAK3D;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK7B;;OAEG;IACH,SAAS,IAAI,IAAI;IAIjB;;OAEG;IACH,OAAO,CAAC,MAAM;IAYd;;OAEG;IACH,QAAQ,IAAI,IAAI;CAMjB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAO3F"}
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../../src/ui/progress.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,WAAW,kBAAkB;IACjC,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iCAAiC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAWD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAyBR;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,UAAU,CAAM;gBAEZ,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB;IAK3D;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK7B;;OAEG;IACH,SAAS,IAAI,IAAI;IAIjB;;OAEG;IACH,OAAO,CAAC,MAAM;IAYd;;OAEG;IACH,QAAQ,IAAI,IAAI;CAMjB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAO3F"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/ui/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,eAAO,MAAM,KAAK,SAAgC,CAAC;AAEnD;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAK9D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,GAAG,OAAO,GAAG,QAAiB,GAAG,MAAM,CAUxG;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI/D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/ui/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,eAAO,MAAM,KAAK,SAAgC,CAAC;AAEnD;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAK9D;AAED;;GAEG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAM,GAAG,OAAO,GAAG,QAAiB,GAC1C,MAAM,CAWR;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI/D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Non-blocking update checker for ArtemisKit CLI
3
+ */
4
+ export interface UpdateInfo {
5
+ currentVersion: string;
6
+ latestVersion: string;
7
+ updateAvailable: boolean;
8
+ }
9
+ /**
10
+ * Check for updates (non-blocking)
11
+ * Returns update info or null if check fails
12
+ */
13
+ export declare function checkForUpdate(): Promise<UpdateInfo | null>;
14
+ /**
15
+ * Get the current CLI version
16
+ */
17
+ export declare function getCurrentVersion(): string;
18
+ /**
19
+ * Format version display string
20
+ */
21
+ export declare function formatVersionDisplay(version: string): string;
22
+ /**
23
+ * Format update available message
24
+ */
25
+ export declare function formatUpdateMessage(current: string, latest: string): string;
26
+ /**
27
+ * Non-blocking update check that prints message if update available
28
+ * Use this to fire-and-forget an update check
29
+ */
30
+ export declare function checkForUpdateAndNotify(): void;
31
+ //# sourceMappingURL=update-checker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-checker.d.ts","sourceRoot":"","sources":["../../../src/utils/update-checker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,MAAM,WAAW,UAAU;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAgDD;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAYjE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAW9C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artemiskit/cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Command-line interface for ArtemisKit LLM evaluation toolkit",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -45,11 +45,11 @@
45
45
  "test": "bun test"
46
46
  },
47
47
  "dependencies": {
48
- "@artemiskit/adapter-openai": "workspace:*",
49
- "@artemiskit/adapter-vercel-ai": "workspace:*",
50
- "@artemiskit/core": "workspace:*",
51
- "@artemiskit/redteam": "workspace:*",
52
- "@artemiskit/reports": "workspace:*",
48
+ "@artemiskit/adapter-openai": "0.1.6",
49
+ "@artemiskit/adapter-vercel-ai": "0.1.6",
50
+ "@artemiskit/core": "0.1.6",
51
+ "@artemiskit/redteam": "0.1.6",
52
+ "@artemiskit/reports": "0.1.6",
53
53
  "chalk": "^5.3.0",
54
54
  "cli-table3": "^0.6.3",
55
55
  "commander": "^12.0.0",
@@ -1,8 +1,26 @@
1
1
  /**
2
2
  * Mock adapter for CLI integration tests
3
+ *
4
+ * This module provides mock types and adapters for testing CLI commands
5
+ * without making actual LLM API calls.
3
6
  */
4
7
 
5
- import type { LLMAdapter, LLMResponse } from '@artemiskit/core';
8
+ /** Message format for chat interactions */
9
+ export interface MockMessage {
10
+ role: 'system' | 'user' | 'assistant';
11
+ content: string;
12
+ }
13
+
14
+ /** Response from the mock adapter */
15
+ export interface MockLLMResponse {
16
+ content: string;
17
+ usage: { input: number; output: number };
18
+ }
19
+
20
+ /** Mock adapter interface */
21
+ export interface MockLLMAdapter {
22
+ chat: (messages: MockMessage[]) => Promise<MockLLMResponse>;
23
+ }
6
24
 
7
25
  export interface MockResponse {
8
26
  content: string;
@@ -20,7 +38,7 @@ export interface MockAdapterOptions {
20
38
  /**
21
39
  * Creates a mock LLM adapter for testing
22
40
  */
23
- export function createMockAdapter(options: MockAdapterOptions = {}): LLMAdapter {
41
+ export function createMockAdapter(options: MockAdapterOptions = {}): MockLLMAdapter {
24
42
  const {
25
43
  responses = new Map(),
26
44
  defaultResponse = {
@@ -33,7 +51,7 @@ export function createMockAdapter(options: MockAdapterOptions = {}): LLMAdapter
33
51
  } = options;
34
52
 
35
53
  return {
36
- chat: async (messages): Promise<LLMResponse> => {
54
+ chat: async (messages: MockMessage[]): Promise<MockLLMResponse> => {
37
55
  if (shouldFail) {
38
56
  throw new Error(failureMessage);
39
57
  }
@@ -47,7 +65,7 @@ export function createMockAdapter(options: MockAdapterOptions = {}): LLMAdapter
47
65
 
48
66
  // Simulate latency
49
67
  if (mockResponse.latencyMs) {
50
- await new Promise((resolve) => setTimeout(resolve, Math.min(mockResponse.latencyMs!, 50)));
68
+ await new Promise((resolve) => setTimeout(resolve, Math.min(mockResponse.latencyMs, 50)));
51
69
  }
52
70
 
53
71
  return {
@@ -3,8 +3,8 @@
3
3
  */
4
4
 
5
5
  import { mkdir, rm, writeFile } from 'node:fs/promises';
6
- import { join } from 'node:path';
7
6
  import { tmpdir } from 'node:os';
7
+ import { join } from 'node:path';
8
8
 
9
9
  /**
10
10
  * Creates a temporary directory for test isolation
@@ -169,8 +169,8 @@ cases:
169
169
  * Captures console output during test execution
170
170
  */
171
171
  export class OutputCapture {
172
- private originalLog: typeof console.log;
173
- private originalError: typeof console.error;
172
+ private originalLog: typeof console.log = console.log;
173
+ private originalError: typeof console.error = console.error;
174
174
  private logs: string[] = [];
175
175
  private errors: string[] = [];
176
176
 
@@ -2,11 +2,11 @@
2
2
  * Integration tests for compare command
3
3
  */
4
4
 
5
- import { describe, expect, it, beforeEach, afterEach } from 'bun:test';
5
+ import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
6
6
  import { mkdir, writeFile } from 'node:fs/promises';
7
7
  import { join } from 'node:path';
8
- import { createTestDir, cleanupTestDir } from '../helpers/test-utils.js';
9
8
  import { createStorage } from '../../utils/storage.js';
9
+ import { cleanupTestDir, createTestDir } from '../helpers/test-utils.js';
10
10
 
11
11
  describe('Compare Command', () => {
12
12
  let testDir: string;
@@ -73,7 +73,7 @@ describe('Compare Command', () => {
73
73
  },
74
74
  });
75
75
 
76
- const comparison = await storage.compare!('baseline-001', 'current-001');
76
+ const comparison = await storage.compare?.('baseline-001', 'current-001');
77
77
 
78
78
  expect(comparison.baseline.metrics.success_rate).toBe(0.8);
79
79
  expect(comparison.current.metrics.success_rate).toBe(1.0);
@@ -128,7 +128,7 @@ describe('Compare Command', () => {
128
128
  },
129
129
  });
130
130
 
131
- const comparison = await storage.compare!('baseline-002', 'current-002');
131
+ const comparison = await storage.compare?.('baseline-002', 'current-002');
132
132
 
133
133
  // Success rate dropped by 0.4 (40%)
134
134
  expect(comparison.delta.successRate).toBeCloseTo(-0.4, 5);
@@ -166,7 +166,7 @@ describe('Compare Command', () => {
166
166
  },
167
167
  });
168
168
 
169
- const comparison = await storage.compare!('same-001', 'same-001');
169
+ const comparison = await storage.compare?.('same-001', 'same-001');
170
170
 
171
171
  expect(comparison.delta.successRate).toBe(0);
172
172
  expect(comparison.delta.latency).toBe(0);
@@ -200,7 +200,7 @@ describe('Compare Command', () => {
200
200
  },
201
201
  });
202
202
 
203
- await expect(storage.compare!('non-existent', 'exists-001')).rejects.toThrow();
203
+ await expect(storage.compare?.('non-existent', 'exists-001')).rejects.toThrow();
204
204
  });
205
205
 
206
206
  it('should throw error for non-existent current', async () => {
@@ -230,7 +230,7 @@ describe('Compare Command', () => {
230
230
  },
231
231
  });
232
232
 
233
- await expect(storage.compare!('exists-002', 'non-existent')).rejects.toThrow();
233
+ await expect(storage.compare?.('exists-002', 'non-existent')).rejects.toThrow();
234
234
  });
235
235
  });
236
236
  });
@@ -2,11 +2,11 @@
2
2
  * Integration tests for CLI configuration loading
3
3
  */
4
4
 
5
- import { describe, expect, it, beforeEach, afterEach } from 'bun:test';
5
+ import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
6
6
  import { writeFile } from 'node:fs/promises';
7
7
  import { join } from 'node:path';
8
8
  import { loadConfig } from '../../config/loader.js';
9
- import { createTestDir, cleanupTestDir } from '../helpers/test-utils.js';
9
+ import { cleanupTestDir, createTestDir } from '../helpers/test-utils.js';
10
10
 
11
11
  describe('Config Loader', () => {
12
12
  let testDir: string;
@@ -2,11 +2,11 @@
2
2
  * Integration tests for history command
3
3
  */
4
4
 
5
- import { describe, expect, it, beforeEach, afterEach } from 'bun:test';
5
+ import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
6
6
  import { mkdir, writeFile } from 'node:fs/promises';
7
7
  import { join } from 'node:path';
8
- import { createTestDir, cleanupTestDir } from '../helpers/test-utils.js';
9
8
  import { createStorage } from '../../utils/storage.js';
9
+ import { cleanupTestDir, createTestDir } from '../helpers/test-utils.js';
10
10
 
11
11
  describe('History Command', () => {
12
12
  let testDir: string;
@@ -2,11 +2,11 @@
2
2
  * Integration tests for init command
3
3
  */
4
4
 
5
- import { describe, expect, it, beforeEach, afterEach } from 'bun:test';
5
+ import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
6
6
  import { existsSync } from 'node:fs';
7
7
  import { readFile, writeFile } from 'node:fs/promises';
8
8
  import { join } from 'node:path';
9
- import { createTestDir, cleanupTestDir } from '../helpers/test-utils.js';
9
+ import { cleanupTestDir, createTestDir } from '../helpers/test-utils.js';
10
10
 
11
11
  // Import init command internals for testing
12
12
  // We'll test the file creation logic directly
@@ -126,7 +126,7 @@ SOME_OTHER_VAR=value
126
126
  });
127
127
 
128
128
  if (missingKeys.length > 0) {
129
- const newContent = envContent + '\n# Added by ArtemisKit\n' + missingKeys.join('\n') + '\n';
129
+ const newContent = `${envContent}\n# Added by ArtemisKit\n${missingKeys.join('\n')}\n`;
130
130
  await writeFile(envPath, newContent);
131
131
  }
132
132
 
@@ -2,13 +2,13 @@
2
2
  * Integration tests for report command
3
3
  */
4
4
 
5
- import { describe, expect, it, beforeEach, afterEach } from 'bun:test';
5
+ import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
6
6
  import { existsSync } from 'node:fs';
7
7
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
8
8
  import { join } from 'node:path';
9
9
  import { generateHTMLReport, generateJSONReport } from '@artemiskit/reports';
10
- import { createTestDir, cleanupTestDir } from '../helpers/test-utils.js';
11
10
  import { createStorage } from '../../utils/storage.js';
11
+ import { cleanupTestDir, createTestDir } from '../helpers/test-utils.js';
12
12
 
13
13
  describe('Report Command', () => {
14
14
  let testDir: string;
@@ -4,15 +4,15 @@
4
4
 
5
5
  import { describe, expect, it } from 'bun:test';
6
6
  import {
7
- renderProgressBar,
8
- renderSummaryPanel,
9
- renderError,
10
- renderInfoBox,
7
+ colors,
11
8
  createSpinner,
9
+ formatDuration,
12
10
  icons,
13
- colors,
14
11
  padText,
15
- formatDuration,
12
+ renderError,
13
+ renderInfoBox,
14
+ renderProgressBar,
15
+ renderSummaryPanel,
16
16
  } from '../../ui/index.js';
17
17
 
18
18
  describe('UI Components', () => {
package/src/cli.ts CHANGED
@@ -11,6 +11,7 @@ import { redteamCommand } from './commands/redteam';
11
11
  import { reportCommand } from './commands/report';
12
12
  import { runCommand } from './commands/run';
13
13
  import { stressCommand } from './commands/stress';
14
+ import { checkForUpdate, formatUpdateMessage, formatVersionDisplay } from './utils/update-checker';
14
15
 
15
16
  export function createCLI(): Command {
16
17
  const program = new Command();
@@ -20,7 +21,27 @@ export function createCLI(): Command {
20
21
  .description(
21
22
  'ArtemisKit - Open-source Agent Reliability Toolkit - Test, validate, audit and evaluate LLMs and LLM-driven agents'
22
23
  )
23
- .version(version);
24
+ .version(version, '-v, --version', 'Output the current version')
25
+ .option('-V, --version-check', 'Output version and check for updates')
26
+ .action(async () => {
27
+ // Handle root command with --version-check flag
28
+ const opts = program.opts();
29
+ if (opts.versionCheck) {
30
+ console.log(formatVersionDisplay(version));
31
+ console.log('\nChecking for updates...');
32
+ const updateInfo = await checkForUpdate();
33
+ if (updateInfo?.updateAvailable) {
34
+ console.log(formatUpdateMessage(updateInfo.currentVersion, updateInfo.latestVersion));
35
+ } else if (updateInfo) {
36
+ console.log('You are using the latest version.');
37
+ } else {
38
+ console.log('Could not check for updates (network unavailable).');
39
+ }
40
+ } else {
41
+ // No subcommand provided, show help
42
+ program.help();
43
+ }
44
+ });
24
45
 
25
46
  program.addCommand(initCommand());
26
47
  program.addCommand(runCommand());
@@ -5,7 +5,7 @@
5
5
  import chalk from 'chalk';
6
6
  import { Command } from 'commander';
7
7
  import { loadConfig } from '../config/loader.js';
8
- import { createSpinner, renderError, icons, isTTY, padText } from '../ui/index.js';
8
+ import { createSpinner, icons, isTTY, padText, renderError } from '../ui/index.js';
9
9
  import { createStorage } from '../utils/storage.js';
10
10
 
11
11
  interface CompareOptions {
@@ -183,9 +183,7 @@ export function compareCommand(): Command {
183
183
 
184
184
  if (hasRegression) {
185
185
  console.log(
186
- `${icons.failed} ${chalk.red('Regression detected!')} ` +
187
- `Success rate dropped by ${chalk.bold(Math.abs(delta.successRate * 100).toFixed(1) + '%')} ` +
188
- chalk.dim(`(threshold: ${threshold * 100}%)`)
186
+ `${icons.failed} ${chalk.red('Regression detected!')} Success rate dropped by ${chalk.bold(`${Math.abs(delta.successRate * 100).toFixed(1)}%`)} ${chalk.dim(`(threshold: ${threshold * 100}%)`)}`
189
187
  );
190
188
  process.exit(1);
191
189
  } else {
@@ -5,7 +5,7 @@
5
5
  import chalk from 'chalk';
6
6
  import { Command } from 'commander';
7
7
  import { loadConfig } from '../config/loader.js';
8
- import { createSpinner, renderError, isTTY, padText } from '../ui/index.js';
8
+ import { createSpinner, isTTY, padText, renderError } from '../ui/index.js';
9
9
  import { createStorage } from '../utils/storage.js';
10
10
 
11
11
  interface HistoryOptions {
@@ -57,7 +57,7 @@ function renderHistoryTable(
57
57
  const runIdPad = padText(run.runId, runIdWidth);
58
58
  const truncScenario =
59
59
  run.scenario.length > scenarioWidth - 2
60
- ? run.scenario.slice(0, scenarioWidth - 3) + '…'
60
+ ? `${run.scenario.slice(0, scenarioWidth - 3)}…`
61
61
  : run.scenario;
62
62
  const scenarioPad = padText(truncScenario, scenarioWidth);
63
63