dialekt 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -10
- package/TESTING.md +29 -29
- package/dist/cli/main.d.mts +1 -1
- package/dist/cli/main.mjs +549 -362
- package/dist/formatters-De4Q-X1d.mjs +516 -435
- package/dist/index.d.mts +162 -25
- package/dist/index.mjs +119 -34
- package/package.json +3 -3
- package/pnpm-workspace.yaml +3 -3
- package/src/adapter/types.test.ts +57 -57
- package/src/adapter/types.ts +7 -4
- package/src/benchmark/metrics.test.ts +141 -69
- package/src/benchmark/metrics.ts +6 -6
- package/src/benchmark/report.test.ts +38 -38
- package/src/benchmark/report.ts +6 -6
- package/src/benchmark/runner.test.ts +70 -72
- package/src/benchmark/runner.ts +4 -4
- package/src/cli/commands/add.test.ts +90 -109
- package/src/cli/commands/add.ts +40 -28
- package/src/cli/commands/benchmark.test.ts +77 -64
- package/src/cli/commands/benchmark.ts +64 -41
- package/src/cli/commands/languages.test.ts +45 -42
- package/src/cli/commands/languages.ts +16 -12
- package/src/cli/commands/missing.test.ts +143 -92
- package/src/cli/commands/missing.ts +24 -17
- package/src/cli/commands/translate.test.ts +79 -79
- package/src/cli/commands/translate.ts +41 -31
- package/src/cli/commands/unused.test.ts +62 -51
- package/src/cli/commands/unused.ts +18 -14
- package/src/cli/commands/validate.test.ts +130 -72
- package/src/cli/commands/validate.ts +25 -20
- package/src/cli/config-resolution.test.ts +169 -49
- package/src/cli/config-resolution.ts +5 -7
- package/src/cli/format.test.ts +50 -50
- package/src/cli/format.ts +57 -60
- package/src/cli/formatters.test.ts +128 -106
- package/src/cli/formatters.ts +72 -95
- package/src/cli/main.ts +13 -13
- package/src/config/define-config.test.ts +44 -29
- package/src/config/define-config.ts +1 -1
- package/src/config/load-config.test.ts +21 -18
- package/src/config/load-config.ts +5 -5
- package/src/config/types.test.ts +50 -44
- package/src/config/types.ts +2 -2
- package/src/index.ts +22 -26
- package/src/keys/flatten.test.ts +52 -52
- package/src/keys/flatten.ts +7 -9
- package/src/sdk/file-io.test.ts +47 -59
- package/src/sdk/file-io.ts +2 -2
- package/src/sdk/node-layer.test.ts +18 -18
- package/src/sdk/node-layer.ts +2 -2
- package/src/sdk/php-array-reader.test.ts +49 -40
- package/src/sdk/php-array-reader.ts +5 -5
- package/src/translation/chunking.test.ts +52 -44
- package/src/translation/chunking.ts +1 -1
- package/src/translation/missing-keys.test.ts +86 -93
- package/src/translation/missing-keys.ts +4 -6
- package/src/translation/model-registry.test.ts +41 -32
- package/src/translation/model-registry.ts +9 -9
- package/src/translation/one-shot-strategy.test.ts +105 -86
- package/src/translation/one-shot-strategy.ts +10 -12
- package/src/translation/orchestrator.test.ts +90 -101
- package/src/translation/orchestrator.ts +26 -26
- package/src/translation/prompt.test.ts +76 -76
- package/src/translation/prompt.ts +2 -2
- package/src/translation/tool-loop-strategy.test.ts +134 -107
- package/src/translation/tool-loop-strategy.ts +14 -18
- package/src/translation/types.test.ts +22 -22
- package/src/translation/types.ts +3 -3
- package/tsdown.config.ts +3 -3
- package/vitest.config.ts +3 -3
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Command, Options } from
|
|
2
|
-
import { Effect, Console, Option } from
|
|
3
|
-
import { loadConfig } from
|
|
4
|
-
import { resolveEffectiveConfig } from
|
|
5
|
-
import { computeMissingKeys } from
|
|
6
|
-
import { detectFormat, type OutputFormat } from
|
|
7
|
-
import { formatValidate, formatError } from
|
|
8
|
-
import type { DialektConfig } from
|
|
9
|
-
import type { TranslationAdapter, ResourceRef } from
|
|
1
|
+
import { Command, Options } from "@effect/cli";
|
|
2
|
+
import { Effect, Console, Option } from "effect";
|
|
3
|
+
import { loadConfig } from "../../config/load-config.js";
|
|
4
|
+
import { resolveEffectiveConfig } from "../config-resolution.js";
|
|
5
|
+
import { computeMissingKeys } from "../../translation/missing-keys.js";
|
|
6
|
+
import { detectFormat, type OutputFormat } from "../format.js";
|
|
7
|
+
import { formatValidate, formatError } from "../formatters.js";
|
|
8
|
+
import type { DialektConfig } from "../../config/types.js";
|
|
9
|
+
import type { TranslationAdapter, ResourceRef } from "../../adapter/types.js";
|
|
10
10
|
|
|
11
11
|
export interface ValidateFlags {
|
|
12
12
|
readonly config: string;
|
|
@@ -30,7 +30,10 @@ export function runValidate(
|
|
|
30
30
|
adapter: TranslationAdapter,
|
|
31
31
|
sourceLocale: string,
|
|
32
32
|
targetLocales: readonly string[],
|
|
33
|
-
) => Effect.Effect<
|
|
33
|
+
) => Effect.Effect<
|
|
34
|
+
readonly MissingEntry[],
|
|
35
|
+
unknown
|
|
36
|
+
> = computeMissingKeys as unknown as typeof missingKeysComputer,
|
|
34
37
|
logger: (msg: string) => Effect.Effect<void> = (msg: string) => Console.log(msg),
|
|
35
38
|
): Effect.Effect<void, Error> {
|
|
36
39
|
return Effect.gen(function* () {
|
|
@@ -75,9 +78,7 @@ export function runValidate(
|
|
|
75
78
|
|
|
76
79
|
const passing = entries.length === 0;
|
|
77
80
|
|
|
78
|
-
yield* logger(
|
|
79
|
-
formatValidate({ passing, entries }, format),
|
|
80
|
-
);
|
|
81
|
+
yield* logger(formatValidate({ passing, entries }, format));
|
|
81
82
|
|
|
82
83
|
if (!passing) {
|
|
83
84
|
yield* Effect.sync(() => {
|
|
@@ -87,10 +88,14 @@ export function runValidate(
|
|
|
87
88
|
}).pipe(Effect.mapError((e) => e as Error)) as Effect.Effect<void, Error, never>;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
export const validateCommand = Command.make(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
export const validateCommand = Command.make(
|
|
92
|
+
"validate",
|
|
93
|
+
{
|
|
94
|
+
config: Options.text("config").pipe(Options.withDefault("./dialekt.config.ts")),
|
|
95
|
+
adapter: Options.optional(Options.text("adapter")),
|
|
96
|
+
baseLanguage: Options.optional(Options.text("base-language")),
|
|
97
|
+
language: Options.optional(Options.text("language")),
|
|
98
|
+
format: Options.optional(Options.text("format")),
|
|
99
|
+
},
|
|
100
|
+
(flags) => runValidate(flags),
|
|
101
|
+
);
|
|
@@ -1,77 +1,122 @@
|
|
|
1
|
-
import { describe, expect, it } from
|
|
2
|
-
import { resolveEffectiveConfig } from
|
|
3
|
-
import type { DialektConfig } from
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { resolveEffectiveConfig } from "./config-resolution.js";
|
|
3
|
+
import type { DialektConfig } from "../config/types.js";
|
|
4
4
|
|
|
5
5
|
const baseConfig: DialektConfig = {
|
|
6
|
-
sourceLocale:
|
|
7
|
-
targetLocales: [
|
|
8
|
-
strategy:
|
|
9
|
-
model: { provider:
|
|
10
|
-
fastModel: { provider:
|
|
6
|
+
sourceLocale: "en",
|
|
7
|
+
targetLocales: ["de", "fr"],
|
|
8
|
+
strategy: "one-shot",
|
|
9
|
+
model: { provider: "openai", modelId: "gpt-4o" },
|
|
10
|
+
fastModel: { provider: "openai", modelId: "gpt-4o-mini" },
|
|
11
11
|
chunking: { maxTokens: 3000, charsPerToken: 3.0, concurrency: 3 },
|
|
12
12
|
retry: { maxAttempts: 3, baseDelayMs: 1000 },
|
|
13
13
|
adapters: [],
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
describe(
|
|
17
|
-
it(
|
|
16
|
+
describe("resolveEffectiveConfig", () => {
|
|
17
|
+
it("uses loaded config when no flags given", () => {
|
|
18
18
|
const result = resolveEffectiveConfig({}, baseConfig);
|
|
19
|
-
expect(result.sourceLocale).toBe(
|
|
20
|
-
expect(result.targetLocales).toEqual([
|
|
19
|
+
expect(result.sourceLocale).toBe("en");
|
|
20
|
+
expect(result.targetLocales).toEqual(["de", "fr"]);
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
it(
|
|
24
|
-
const result = resolveEffectiveConfig({ baseLanguage:
|
|
25
|
-
expect(result.sourceLocale).toBe(
|
|
23
|
+
it("overrides sourceLocale with flag", () => {
|
|
24
|
+
const result = resolveEffectiveConfig({ baseLanguage: "fr" }, baseConfig);
|
|
25
|
+
expect(result.sourceLocale).toBe("fr");
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
it(
|
|
29
|
-
const result = resolveEffectiveConfig({ strategy:
|
|
30
|
-
expect(result.strategy).toBe(
|
|
28
|
+
it("overrides strategy with flag", () => {
|
|
29
|
+
const result = resolveEffectiveConfig({ strategy: "tool-loop-agent" }, baseConfig);
|
|
30
|
+
expect(result.strategy).toBe("tool-loop-agent");
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
it(
|
|
34
|
-
const adapterA = {
|
|
35
|
-
|
|
33
|
+
it("filters adapters by name flag", () => {
|
|
34
|
+
const adapterA = {
|
|
35
|
+
name: "a",
|
|
36
|
+
capabilities: { canCreateResource: true, unusedKeyDetection: false },
|
|
37
|
+
listLocales: () => {
|
|
38
|
+
throw new Error();
|
|
39
|
+
},
|
|
40
|
+
listResources: () => {
|
|
41
|
+
throw new Error();
|
|
42
|
+
},
|
|
43
|
+
readResource: () => {
|
|
44
|
+
throw new Error();
|
|
45
|
+
},
|
|
46
|
+
writeResource: () => {
|
|
47
|
+
throw new Error();
|
|
48
|
+
},
|
|
49
|
+
} as unknown as import("../config/types.js").DialektConfig["adapters"][number];
|
|
50
|
+
const adapterB = {
|
|
51
|
+
name: "b",
|
|
52
|
+
capabilities: { canCreateResource: true, unusedKeyDetection: false },
|
|
53
|
+
listLocales: () => {
|
|
54
|
+
throw new Error();
|
|
55
|
+
},
|
|
56
|
+
listResources: () => {
|
|
57
|
+
throw new Error();
|
|
58
|
+
},
|
|
59
|
+
readResource: () => {
|
|
60
|
+
throw new Error();
|
|
61
|
+
},
|
|
62
|
+
writeResource: () => {
|
|
63
|
+
throw new Error();
|
|
64
|
+
},
|
|
65
|
+
} as unknown as import("../config/types.js").DialektConfig["adapters"][number];
|
|
36
66
|
const config = { ...baseConfig, adapters: [adapterA, adapterB] };
|
|
37
|
-
const result = resolveEffectiveConfig({ adapter:
|
|
67
|
+
const result = resolveEffectiveConfig({ adapter: "b" }, config);
|
|
38
68
|
expect(result.adapters).toHaveLength(1);
|
|
39
|
-
expect(result.adapters[0]!.name).toBe(
|
|
69
|
+
expect(result.adapters[0]!.name).toBe("b");
|
|
40
70
|
});
|
|
41
71
|
|
|
42
|
-
it(
|
|
43
|
-
const adapterA = {
|
|
72
|
+
it("returns all adapters when adapter flag does not match any", () => {
|
|
73
|
+
const adapterA = {
|
|
74
|
+
name: "a",
|
|
75
|
+
capabilities: { canCreateResource: true, unusedKeyDetection: false },
|
|
76
|
+
listLocales: () => {
|
|
77
|
+
throw new Error();
|
|
78
|
+
},
|
|
79
|
+
listResources: () => {
|
|
80
|
+
throw new Error();
|
|
81
|
+
},
|
|
82
|
+
readResource: () => {
|
|
83
|
+
throw new Error();
|
|
84
|
+
},
|
|
85
|
+
writeResource: () => {
|
|
86
|
+
throw new Error();
|
|
87
|
+
},
|
|
88
|
+
} as unknown as import("../config/types.js").DialektConfig["adapters"][number];
|
|
44
89
|
const config = { ...baseConfig, adapters: [adapterA] };
|
|
45
|
-
const result = resolveEffectiveConfig({ adapter:
|
|
90
|
+
const result = resolveEffectiveConfig({ adapter: "nonexistent" }, config);
|
|
46
91
|
expect(result.adapters).toHaveLength(0);
|
|
47
92
|
});
|
|
48
93
|
|
|
49
|
-
it(
|
|
50
|
-
const result = resolveEffectiveConfig({ language: [
|
|
51
|
-
expect(result.targetLocales).toEqual([
|
|
94
|
+
it("overrides targetLocales with language flag", () => {
|
|
95
|
+
const result = resolveEffectiveConfig({ language: ["es"] }, baseConfig);
|
|
96
|
+
expect(result.targetLocales).toEqual(["es"]);
|
|
52
97
|
});
|
|
53
98
|
|
|
54
|
-
it(
|
|
99
|
+
it("preserves targetLocales when language flag is empty", () => {
|
|
55
100
|
const result = resolveEffectiveConfig({ language: [] }, baseConfig);
|
|
56
|
-
expect(result.targetLocales).toEqual([
|
|
101
|
+
expect(result.targetLocales).toEqual(["de", "fr"]);
|
|
57
102
|
});
|
|
58
103
|
|
|
59
|
-
it(
|
|
104
|
+
it("preserves targetLocales when language flag is undefined", () => {
|
|
60
105
|
const result = resolveEffectiveConfig({}, baseConfig);
|
|
61
|
-
expect(result.targetLocales).toEqual([
|
|
106
|
+
expect(result.targetLocales).toEqual(["de", "fr"]);
|
|
62
107
|
});
|
|
63
108
|
|
|
64
|
-
it(
|
|
109
|
+
it("does not override strategy when flag is undefined", () => {
|
|
65
110
|
const result = resolveEffectiveConfig({}, baseConfig);
|
|
66
|
-
expect(result.strategy).toBe(
|
|
111
|
+
expect(result.strategy).toBe("one-shot");
|
|
67
112
|
});
|
|
68
113
|
|
|
69
|
-
it(
|
|
114
|
+
it("does not override sourceLocale when baseLanguage is undefined", () => {
|
|
70
115
|
const result = resolveEffectiveConfig({}, baseConfig);
|
|
71
|
-
expect(result.sourceLocale).toBe(
|
|
116
|
+
expect(result.sourceLocale).toBe("en");
|
|
72
117
|
});
|
|
73
118
|
|
|
74
|
-
it(
|
|
119
|
+
it("preserves other config fields unchanged", () => {
|
|
75
120
|
const result = resolveEffectiveConfig({}, baseConfig);
|
|
76
121
|
expect(result.model).toEqual(baseConfig.model);
|
|
77
122
|
expect(result.fastModel).toEqual(baseConfig.fastModel);
|
|
@@ -79,21 +124,96 @@ describe('resolveEffectiveConfig', () => {
|
|
|
79
124
|
expect(result.retry).toEqual(baseConfig.retry);
|
|
80
125
|
});
|
|
81
126
|
|
|
82
|
-
it(
|
|
83
|
-
const a1 = {
|
|
84
|
-
|
|
127
|
+
it("handles multiple adapter filters matching one", () => {
|
|
128
|
+
const a1 = {
|
|
129
|
+
name: "laravel",
|
|
130
|
+
capabilities: { canCreateResource: true, unusedKeyDetection: false },
|
|
131
|
+
listLocales: () => {
|
|
132
|
+
throw new Error();
|
|
133
|
+
},
|
|
134
|
+
listResources: () => {
|
|
135
|
+
throw new Error();
|
|
136
|
+
},
|
|
137
|
+
readResource: () => {
|
|
138
|
+
throw new Error();
|
|
139
|
+
},
|
|
140
|
+
writeResource: () => {
|
|
141
|
+
throw new Error();
|
|
142
|
+
},
|
|
143
|
+
} as unknown as import("../config/types.js").DialektConfig["adapters"][number];
|
|
144
|
+
const a2 = {
|
|
145
|
+
name: "paraglide",
|
|
146
|
+
capabilities: { canCreateResource: true, unusedKeyDetection: false },
|
|
147
|
+
listLocales: () => {
|
|
148
|
+
throw new Error();
|
|
149
|
+
},
|
|
150
|
+
listResources: () => {
|
|
151
|
+
throw new Error();
|
|
152
|
+
},
|
|
153
|
+
readResource: () => {
|
|
154
|
+
throw new Error();
|
|
155
|
+
},
|
|
156
|
+
writeResource: () => {
|
|
157
|
+
throw new Error();
|
|
158
|
+
},
|
|
159
|
+
} as unknown as import("../config/types.js").DialektConfig["adapters"][number];
|
|
85
160
|
const config = { ...baseConfig, adapters: [a1, a2] };
|
|
86
|
-
const result = resolveEffectiveConfig({ adapter:
|
|
161
|
+
const result = resolveEffectiveConfig({ adapter: "laravel" }, config);
|
|
87
162
|
expect(result.adapters).toHaveLength(1);
|
|
88
|
-
expect(result.adapters[0]!.name).toBe(
|
|
163
|
+
expect(result.adapters[0]!.name).toBe("laravel");
|
|
89
164
|
});
|
|
90
165
|
|
|
91
|
-
it(
|
|
92
|
-
const a1 = {
|
|
93
|
-
|
|
94
|
-
|
|
166
|
+
it("preserves adapter order after filtering", () => {
|
|
167
|
+
const a1 = {
|
|
168
|
+
name: "first",
|
|
169
|
+
capabilities: { canCreateResource: true, unusedKeyDetection: false },
|
|
170
|
+
listLocales: () => {
|
|
171
|
+
throw new Error();
|
|
172
|
+
},
|
|
173
|
+
listResources: () => {
|
|
174
|
+
throw new Error();
|
|
175
|
+
},
|
|
176
|
+
readResource: () => {
|
|
177
|
+
throw new Error();
|
|
178
|
+
},
|
|
179
|
+
writeResource: () => {
|
|
180
|
+
throw new Error();
|
|
181
|
+
},
|
|
182
|
+
} as unknown as import("../config/types.js").DialektConfig["adapters"][number];
|
|
183
|
+
const a2 = {
|
|
184
|
+
name: "second",
|
|
185
|
+
capabilities: { canCreateResource: true, unusedKeyDetection: false },
|
|
186
|
+
listLocales: () => {
|
|
187
|
+
throw new Error();
|
|
188
|
+
},
|
|
189
|
+
listResources: () => {
|
|
190
|
+
throw new Error();
|
|
191
|
+
},
|
|
192
|
+
readResource: () => {
|
|
193
|
+
throw new Error();
|
|
194
|
+
},
|
|
195
|
+
writeResource: () => {
|
|
196
|
+
throw new Error();
|
|
197
|
+
},
|
|
198
|
+
} as unknown as import("../config/types.js").DialektConfig["adapters"][number];
|
|
199
|
+
const a3 = {
|
|
200
|
+
name: "third",
|
|
201
|
+
capabilities: { canCreateResource: true, unusedKeyDetection: false },
|
|
202
|
+
listLocales: () => {
|
|
203
|
+
throw new Error();
|
|
204
|
+
},
|
|
205
|
+
listResources: () => {
|
|
206
|
+
throw new Error();
|
|
207
|
+
},
|
|
208
|
+
readResource: () => {
|
|
209
|
+
throw new Error();
|
|
210
|
+
},
|
|
211
|
+
writeResource: () => {
|
|
212
|
+
throw new Error();
|
|
213
|
+
},
|
|
214
|
+
} as unknown as import("../config/types.js").DialektConfig["adapters"][number];
|
|
95
215
|
const config = { ...baseConfig, adapters: [a1, a2, a3] };
|
|
96
|
-
const result = resolveEffectiveConfig({ adapter:
|
|
97
|
-
expect(result.adapters[0]!.name).toBe(
|
|
216
|
+
const result = resolveEffectiveConfig({ adapter: "second" }, config);
|
|
217
|
+
expect(result.adapters[0]!.name).toBe("second");
|
|
98
218
|
});
|
|
99
219
|
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { DialektConfig } from
|
|
1
|
+
import type { DialektConfig } from "../config/types.js";
|
|
2
2
|
|
|
3
3
|
export interface CliFlags {
|
|
4
4
|
readonly config?: string | undefined;
|
|
5
5
|
readonly adapter?: string | undefined;
|
|
6
|
-
readonly strategy?:
|
|
6
|
+
readonly strategy?: "one-shot" | "tool-loop-agent" | undefined;
|
|
7
7
|
readonly baseLanguage?: string | undefined;
|
|
8
8
|
readonly language?: readonly string[] | undefined;
|
|
9
9
|
readonly name?: readonly string[] | undefined;
|
|
@@ -13,14 +13,12 @@ export interface CliFlags {
|
|
|
13
13
|
readonly langDir?: string | undefined;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export function resolveEffectiveConfig(
|
|
17
|
-
flags: CliFlags,
|
|
18
|
-
loaded: DialektConfig,
|
|
19
|
-
): DialektConfig {
|
|
16
|
+
export function resolveEffectiveConfig(flags: CliFlags, loaded: DialektConfig): DialektConfig {
|
|
20
17
|
return {
|
|
21
18
|
...loaded,
|
|
22
19
|
sourceLocale: flags.baseLanguage ?? loaded.sourceLocale,
|
|
23
|
-
targetLocales:
|
|
20
|
+
targetLocales:
|
|
21
|
+
flags.language && flags.language.length > 0 ? flags.language : loaded.targetLocales,
|
|
24
22
|
strategy: flags.strategy ?? loaded.strategy,
|
|
25
23
|
adapters: flags.adapter
|
|
26
24
|
? loaded.adapters.filter((a: { name: string }) => a.name === flags.adapter)
|
package/src/cli/format.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, it, afterEach } from
|
|
1
|
+
import { describe, expect, it, afterEach } from "vitest";
|
|
2
2
|
import {
|
|
3
3
|
detectFormat,
|
|
4
4
|
color,
|
|
@@ -10,9 +10,9 @@ import {
|
|
|
10
10
|
warning,
|
|
11
11
|
info,
|
|
12
12
|
keyValue,
|
|
13
|
-
} from
|
|
13
|
+
} from "./format.js";
|
|
14
14
|
|
|
15
|
-
describe(
|
|
15
|
+
describe("detectFormat", () => {
|
|
16
16
|
const originalTty = process.stdout.isTTY;
|
|
17
17
|
const originalEnv = { ...process.env };
|
|
18
18
|
|
|
@@ -24,94 +24,94 @@ describe('detectFormat', () => {
|
|
|
24
24
|
Object.assign(process.env, originalEnv);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
it(
|
|
28
|
-
expect(detectFormat(
|
|
29
|
-
expect(detectFormat(
|
|
27
|
+
it("returns explicit format when provided", () => {
|
|
28
|
+
expect(detectFormat("json")).toBe("json");
|
|
29
|
+
expect(detectFormat("pretty")).toBe("pretty");
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
it(
|
|
32
|
+
it("defaults to json when not a TTY", () => {
|
|
33
33
|
Object.assign(process.stdout, { isTTY: false });
|
|
34
|
-
expect(detectFormat()).toBe(
|
|
34
|
+
expect(detectFormat()).toBe("json");
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
it(
|
|
37
|
+
it("defaults to pretty when TTY and no agent env", () => {
|
|
38
38
|
Object.assign(process.stdout, { isTTY: true });
|
|
39
|
-
expect(detectFormat()).toBe(
|
|
39
|
+
expect(detectFormat()).toBe("pretty");
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
it(
|
|
42
|
+
it("defaults to json when agent env var is set", () => {
|
|
43
43
|
Object.assign(process.stdout, { isTTY: true });
|
|
44
|
-
process.env.CLAUDE_CODE =
|
|
45
|
-
expect(detectFormat()).toBe(
|
|
44
|
+
process.env.CLAUDE_CODE = "1";
|
|
45
|
+
expect(detectFormat()).toBe("json");
|
|
46
46
|
});
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
describe(
|
|
50
|
-
it(
|
|
51
|
-
expect(color(
|
|
49
|
+
describe("color", () => {
|
|
50
|
+
it("returns bare text when not a TTY", () => {
|
|
51
|
+
expect(color("hello", "\x1b[31m")).toBe("hello");
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
describe(
|
|
56
|
-
it(
|
|
55
|
+
describe("drawTable", () => {
|
|
56
|
+
it("renders a table with headers and rows", () => {
|
|
57
57
|
const table = drawTable(
|
|
58
|
-
[
|
|
58
|
+
["Name", "Score"],
|
|
59
59
|
[
|
|
60
|
-
[
|
|
61
|
-
[
|
|
60
|
+
["Alice", "10"],
|
|
61
|
+
["Bob", "8"],
|
|
62
62
|
],
|
|
63
63
|
);
|
|
64
|
-
expect(table).toContain(
|
|
65
|
-
expect(table).toContain(
|
|
66
|
-
expect(table).toContain(
|
|
67
|
-
expect(table).toContain(
|
|
64
|
+
expect(table).toContain("Name");
|
|
65
|
+
expect(table).toContain("Score");
|
|
66
|
+
expect(table).toContain("Alice");
|
|
67
|
+
expect(table).toContain("Bob");
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
it(
|
|
71
|
-
const table = drawTable([
|
|
72
|
-
expect(table).toContain(
|
|
70
|
+
it("handles empty rows", () => {
|
|
71
|
+
const table = drawTable(["A"], []);
|
|
72
|
+
expect(table).toContain("A");
|
|
73
73
|
});
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
describe(
|
|
77
|
-
it(
|
|
78
|
-
expect(banner(
|
|
76
|
+
describe("banner", () => {
|
|
77
|
+
it("includes the title", () => {
|
|
78
|
+
expect(banner("Results")).toContain("Results");
|
|
79
79
|
});
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
describe(
|
|
83
|
-
it(
|
|
84
|
-
expect(sectionHeader(
|
|
82
|
+
describe("sectionHeader", () => {
|
|
83
|
+
it("includes the label", () => {
|
|
84
|
+
expect(sectionHeader("Missing keys")).toContain("Missing keys");
|
|
85
85
|
});
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
describe(
|
|
89
|
-
it(
|
|
90
|
-
expect(success(
|
|
88
|
+
describe("success", () => {
|
|
89
|
+
it("includes the text", () => {
|
|
90
|
+
expect(success("Done")).toContain("Done");
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
describe(
|
|
95
|
-
it(
|
|
96
|
-
expect(failure(
|
|
94
|
+
describe("failure", () => {
|
|
95
|
+
it("includes the text", () => {
|
|
96
|
+
expect(failure("Failed")).toContain("Failed");
|
|
97
97
|
});
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
-
describe(
|
|
101
|
-
it(
|
|
102
|
-
expect(warning(
|
|
100
|
+
describe("warning", () => {
|
|
101
|
+
it("includes the text", () => {
|
|
102
|
+
expect(warning("Warn")).toContain("Warn");
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
describe(
|
|
107
|
-
it(
|
|
108
|
-
expect(info(
|
|
106
|
+
describe("info", () => {
|
|
107
|
+
it("returns dimmed text", () => {
|
|
108
|
+
expect(info("note")).toBe("note");
|
|
109
109
|
});
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
describe(
|
|
113
|
-
it(
|
|
114
|
-
expect(keyValue(
|
|
115
|
-
expect(keyValue(
|
|
112
|
+
describe("keyValue", () => {
|
|
113
|
+
it("formats key and value", () => {
|
|
114
|
+
expect(keyValue("Name:", "test")).toContain("Name:");
|
|
115
|
+
expect(keyValue("Name:", "test")).toContain("test");
|
|
116
116
|
});
|
|
117
117
|
});
|