katt 0.0.6 → 0.0.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.
@@ -0,0 +1,424 @@
1
+ import { fileURLToPath as X, pathToFileURL as Y } from "node:url";
2
+ import { readdir as V, readFile as D } from "node:fs/promises";
3
+ import { resolve as m, matchesGlob as E, dirname as Q } from "node:path";
4
+ import { AsyncLocalStorage as N } from "node:async_hooks";
5
+ import { readFileSync as _ } from "node:fs";
6
+ const q = /\.eval\.(js|ts)$/, tt = /* @__PURE__ */ new Set([".git", "node_modules"]);
7
+ function j(t, e) {
8
+ return e.some((n) => t === n || E(t, n));
9
+ }
10
+ async function Z(t, e = []) {
11
+ const n = await V(t, { withFileTypes: !0 }), o = [];
12
+ return await Promise.all(
13
+ n.map(async (i) => {
14
+ const a = m(t, i.name);
15
+ if (i.isDirectory()) {
16
+ if (tt.has(i.name) || j(a, e))
17
+ return;
18
+ o.push(...await Z(a, e));
19
+ return;
20
+ }
21
+ i.isFile() && q.test(i.name) && !j(a, e) && o.push(a);
22
+ })
23
+ ), o;
24
+ }
25
+ const x = new N(), et = {
26
+ describeStack: [],
27
+ itStack: [],
28
+ tokenUsageStack: [],
29
+ modelStack: []
30
+ };
31
+ let F = 0, G = 0;
32
+ const h = [], A = [];
33
+ let v = 0;
34
+ function s() {
35
+ return x.getStore() ?? et;
36
+ }
37
+ function U(t) {
38
+ return {
39
+ describeStack: [...t.describeStack],
40
+ itStack: [...t.itStack],
41
+ tokenUsageStack: [...t.tokenUsageStack],
42
+ modelStack: [...t.modelStack]
43
+ };
44
+ }
45
+ function nt() {
46
+ return F += 1, `d${F}`;
47
+ }
48
+ function ot() {
49
+ return G += 1, `i${G}`;
50
+ }
51
+ function Mt(t, e) {
52
+ const n = e ?? U(s());
53
+ return x.run(n, t);
54
+ }
55
+ function Ot() {
56
+ return U(s());
57
+ }
58
+ function Bt(t) {
59
+ s().describeStack.push({ id: nt(), description: t });
60
+ }
61
+ function zt() {
62
+ s().describeStack.pop();
63
+ }
64
+ function it() {
65
+ return s().describeStack.map((t) => t.description).join(" > ");
66
+ }
67
+ function Kt() {
68
+ return [...s().describeStack];
69
+ }
70
+ function Xt(t) {
71
+ s().itStack.push({ id: ot(), description: t }), s().tokenUsageStack.push(0), s().modelStack.push(void 0);
72
+ }
73
+ function Yt() {
74
+ s().itStack.pop(), s().tokenUsageStack.pop(), s().modelStack.pop();
75
+ }
76
+ function st() {
77
+ return s().itStack.map((t) => t.description).join(" > ");
78
+ }
79
+ function Vt() {
80
+ return [...s().itStack];
81
+ }
82
+ function Dt(t) {
83
+ if (!Number.isFinite(t) || t <= 0)
84
+ return;
85
+ const e = s(), n = e.tokenUsageStack.length - 1;
86
+ n < 0 || (e.tokenUsageStack[n] += t);
87
+ }
88
+ function rt() {
89
+ const t = s(), e = t.tokenUsageStack.length - 1;
90
+ return e < 0 ? 0 : t.tokenUsageStack[e] ?? 0;
91
+ }
92
+ function Et(t) {
93
+ if (t.length === 0)
94
+ return;
95
+ const e = s(), n = e.modelStack.length - 1;
96
+ n < 0 || (e.modelStack[n] = t);
97
+ }
98
+ function at() {
99
+ const t = s(), e = t.modelStack.length - 1;
100
+ if (!(e < 0))
101
+ return t.modelStack[e];
102
+ }
103
+ function Qt(t) {
104
+ h.push(t);
105
+ }
106
+ function _t() {
107
+ v += 1;
108
+ }
109
+ function ct() {
110
+ return v;
111
+ }
112
+ function lt() {
113
+ v = 0;
114
+ }
115
+ function qt(t) {
116
+ A.push(t);
117
+ }
118
+ function ut() {
119
+ return [...A];
120
+ }
121
+ function te() {
122
+ return A.length;
123
+ }
124
+ function gt() {
125
+ A.length = 0;
126
+ }
127
+ async function dt() {
128
+ const t = [];
129
+ for (; h.length > 0; ) {
130
+ const e = h.splice(0, h.length), n = await Promise.allSettled(e);
131
+ t.push(...n);
132
+ }
133
+ return t;
134
+ }
135
+ const ft = "\x1B[1;36m", It = "\x1B[33m", pt = "\x1B[38;5;208m", Ct = "\x1B[1;38;5;208m", S = "\x1B[0m";
136
+ function l(t) {
137
+ return `${ft}${t}${S}`;
138
+ }
139
+ function C(t) {
140
+ return `${It}${t}${S}`;
141
+ }
142
+ function J(t) {
143
+ return `${pt}${t}${S}`;
144
+ }
145
+ function ht(t) {
146
+ return `${Ct}${t}${S}`;
147
+ }
148
+ let w = "";
149
+ function mt() {
150
+ w = "";
151
+ }
152
+ function At({
153
+ suitePath: t,
154
+ casePath: e,
155
+ didPass: n,
156
+ durationMs: o,
157
+ model: i,
158
+ tokenUsage: a
159
+ }) {
160
+ const c = t.length > 0 ? t : "(root)", p = e.length > 0 ? e : "(root)";
161
+ w !== c && (console.log(`Suite "${l(c)}"`), w = c);
162
+ const g = n ? "✅ Passed in" : "❌ Failed in", d = [
163
+ `Test "${l(p)}"`,
164
+ `- ${g} ${l(`${o}ms`)}`
165
+ ];
166
+ i && d.push(`- Model ${l(i)}`), (a ?? 0) > 0 && d.push(`- Tokens used ${l(String(a))}`), d.push("---"), console.log(d.join(`
167
+ `));
168
+ }
169
+ function ee(t, e, n = "(root)") {
170
+ const o = st();
171
+ At({
172
+ suitePath: it(),
173
+ casePath: o.length > 0 ? o : n,
174
+ didPass: t,
175
+ durationMs: e,
176
+ model: at(),
177
+ tokenUsage: rt()
178
+ });
179
+ }
180
+ const St = new N(), b = new URL("data:application/json;base64,ewogICJuYW1lIjogImthdHQiLAogICJ2ZXJzaW9uIjogIjAuMC43IiwKICAiZGVzY3JpcHRpb24iOiAiQ0xJIHRvb2wgdGhhdCB0ZXN0cyB0aGUgb3V0cHV0IG9mIGFnZW50aWMgQUkgdG9vbHMiLAogICJrZXl3b3JkcyI6IFsKICAgICJjbGkiLAogICAgImFpIiwKICAgICJhZ2VudGljLWFpIiwKICAgICJ0ZXN0aW5nIiwKICAgICJldmFsdWF0aW9uIgogIF0sCiAgImF1dGhvciI6ICJSYXBoYWVsIFBvcnRvIChodHRwczovL2dpdGh1Yi5jb20vcmFwaGFlbHBvcikiLAogICJsaWNlbnNlIjogIk1JVCIsCiAgInR5cGUiOiAibW9kdWxlIiwKICAibWFpbiI6ICJkaXN0L2luZGV4LmpzIiwKICAiZXhwb3J0cyI6IHsKICAgICIuIjogewogICAgICAidHlwZXMiOiAiLi9kaXN0L2luZGV4LmQudHMiLAogICAgICAiaW1wb3J0IjogIi4vZGlzdC9pbmRleC5qcyIKICAgIH0KICB9LAogICJiaW4iOiB7CiAgICAia2F0dCI6ICJkaXN0L2thdHQuanMiCiAgfSwKICAic2NyaXB0cyI6IHsKICAgICJidWlsZCI6ICJ2aXRlIGJ1aWxkIiwKICAgICJkZXYiOiAidHN4IHNyYy9pbmRleC50cyIsCiAgICAibGludCI6ICJiaW9tZSBsaW50IC4vc3JjIiwKICAgICJmb3JtYXQiOiAiYmlvbWUgZm9ybWF0IC0td3JpdGUgLi9zcmMiLAogICAgInRlc3QiOiAidml0ZXN0IiwKICAgICJ0eXBlY2hlY2siOiAidHNjIC1wIHRzY29uZmlnLmpzb24gLS1ub0VtaXQiLAogICAgInRlc3Q6YnVpbGQiOiAibm9kZSAuL2Rpc3Qva2F0dC5qcyIsCiAgICAidGVzdDpidWlsZDpjb2RleCI6ICJub2RlIC4vZGlzdC9rYXR0LmpzIC0tY29uZmlnLWZpbGU9Li9rYXR0LWNvZGV4Lmpzb24iCiAgfSwKICAidHlwZXMiOiAiZGlzdC9pbmRleC5kLnRzIiwKICAiZGV2RGVwZW5kZW5jaWVzIjogewogICAgIkBiaW9tZWpzL2Jpb21lIjogIjIuNC4yIiwKICAgICJAdHlwZXMvbm9kZSI6ICIyNS4yLjMiLAogICAgInRzeCI6ICI0LjIxLjAiLAogICAgInR5cGVzY3JpcHQiOiAiNS45LjMiLAogICAgInZpdGUiOiAiNy4zLjEiLAogICAgInZpdGUtcGx1Z2luLWR0cyI6ICI0LjUuNCIsCiAgICAidml0ZXN0IjogIjQuMC4xOCIsCiAgICAidnNjb2RlLWpzb25ycGMiOiAiXjguMi4xIgogIH0sCiAgImRlcGVuZGVuY2llcyI6IHsKICAgICJAZ2l0aHViL2NvcGlsb3Qtc2RrIjogIjAuMS4yMyIKICB9LAogICJidWdzIjogewogICAgInVybCI6ICJodHRwczovL2dpdGh1Yi5jb20vcmFwaGFlbHBvci9rYXR0L2lzc3VlcyIKICB9LAogICJob21lcGFnZSI6ICJodHRwczovL2dpdGh1Yi5jb20vcmFwaGFlbHBvci9rYXR0Igp9Cg==", import.meta.url);
181
+ let f;
182
+ function bt() {
183
+ if (f !== void 0)
184
+ return f;
185
+ try {
186
+ const t = b.protocol === "data:" ? kt(b) : _(X(b), "utf8"), e = JSON.parse(t);
187
+ f = typeof e.version == "string" ? e.version : "unknown";
188
+ } catch {
189
+ f = "unknown";
190
+ }
191
+ return f;
192
+ }
193
+ function kt(t) {
194
+ const e = t.pathname.indexOf(",");
195
+ if (e < 0)
196
+ throw new Error("Invalid data URL.");
197
+ const n = t.pathname.slice(0, e), o = t.pathname.slice(e + 1);
198
+ return n.includes(";base64") ? Buffer.from(o, "base64").toString("utf8") : decodeURIComponent(o);
199
+ }
200
+ function k() {
201
+ const t = " ██╗ ██╗ █████╗ ████████╗████████╗", e = " ██║ ██╔╝██╔══██╗╚══██╔══╝╚══██╔══╝", n = " █████╔╝ ███████║ ██║ ██║", o = " ██╔═██╗ ██╔══██║ ██║ ██║", i = " ██║ ██╗██║ ██║ ██║ ██║", a = " ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝", c = `v${bt()}`, p = Math.max(
202
+ 0,
203
+ Math.floor((t.length - c.length) / 2)
204
+ ), g = `${" ".repeat(p)}${c}`;
205
+ console.log(`
206
+ ${C(t)}
207
+ ${C(e)}
208
+ ${C(n)}
209
+ ${J(o)}
210
+ ${J(i)}
211
+ ${ht(a)}
212
+ ${C(g)}
213
+ `);
214
+ }
215
+ let W = !1;
216
+ function wt(t) {
217
+ W = t;
218
+ }
219
+ function ne() {
220
+ return W;
221
+ }
222
+ const vt = "gh-copilot", R = "katt.json";
223
+ let I;
224
+ function P() {
225
+ return typeof I == "string" && I.length > 0 ? {
226
+ resolvedPath: m(process.cwd(), I),
227
+ label: I
228
+ } : {
229
+ resolvedPath: m(process.cwd(), R),
230
+ label: R
231
+ };
232
+ }
233
+ function T(t) {
234
+ I = t;
235
+ }
236
+ function Lt(t, e) {
237
+ return typeof t == "object" && t !== null && "code" in t && t.code === e;
238
+ }
239
+ function yt(t, e) {
240
+ try {
241
+ const n = JSON.parse(t);
242
+ return typeof n == "object" && n !== null ? n : void 0;
243
+ } catch (n) {
244
+ console.warn(`Failed to parse ${e}: ${String(n)}`);
245
+ return;
246
+ }
247
+ }
248
+ function $t(t, e) {
249
+ t && "copilot" in t && console.warn(
250
+ `Deprecated config property "copilot" found in ${e}. Use "agent" and "agentOptions" instead.`
251
+ );
252
+ }
253
+ async function H() {
254
+ const { resolvedPath: t, label: e } = P();
255
+ try {
256
+ const n = await D(t, "utf8"), o = yt(n, e);
257
+ return $t(o, e), o;
258
+ } catch (n) {
259
+ if (Lt(n, "ENOENT"))
260
+ return;
261
+ console.warn(`Failed to read ${e}: ${String(n)}`);
262
+ return;
263
+ }
264
+ }
265
+ function M(t) {
266
+ if (t === "gh-copilot" || t === "codex")
267
+ return t;
268
+ }
269
+ function jt(t, e) {
270
+ if (!t || M(t?.agent) !== e)
271
+ return;
272
+ const n = t.agentOptions;
273
+ if (typeof n != "object" || n === null || Array.isArray(n))
274
+ return;
275
+ const o = { ...n }, i = o.model;
276
+ return (typeof i != "string" || i.length === 0) && delete o.model, Object.keys(o).length > 0 ? o : void 0;
277
+ }
278
+ function Ft(t) {
279
+ if (!(typeof t != "number" || !Number.isFinite(t)) && !(t <= 0))
280
+ return Math.floor(t);
281
+ }
282
+ function Gt(t) {
283
+ const e = t?.prompt;
284
+ if (!(typeof e != "object" || e === null || Array.isArray(e)))
285
+ return Ft(e.timeoutMs);
286
+ }
287
+ function Jt(t) {
288
+ if (!t || !Array.isArray(t.ignorePatterns))
289
+ return [];
290
+ const e = Q(P().resolvedPath);
291
+ return t.ignorePatterns.filter((n) => typeof n == "string" && n.length > 0).map((n) => m(e, n));
292
+ }
293
+ async function oe() {
294
+ const t = await H(), e = M(t?.agent) ?? vt;
295
+ return {
296
+ agent: e,
297
+ agentOptions: jt(t, e),
298
+ promptTimeoutMs: Gt(t)
299
+ };
300
+ }
301
+ async function Rt() {
302
+ const t = await H();
303
+ return Jt(t);
304
+ }
305
+ function Tt(t) {
306
+ const e = String(t.getHours()).padStart(2, "0"), n = String(t.getMinutes()).padStart(2, "0"), o = String(t.getSeconds()).padStart(2, "0");
307
+ return `${e}:${n}:${o}`;
308
+ }
309
+ function Nt() {
310
+ console.log(
311
+ [
312
+ "Usage:",
313
+ " katt [options]",
314
+ "",
315
+ "Options:",
316
+ " -h, --help Show CLI usage information",
317
+ " -u, --update-snapshots Update snapshot files on mismatch",
318
+ " --config-file PATH Use a custom config file instead of katt.json"
319
+ ].join(`
320
+ `)
321
+ );
322
+ }
323
+ function Zt(t) {
324
+ let e;
325
+ for (let n = 0; n < t.length; n += 1) {
326
+ const o = t[n];
327
+ if (o === "--config-file") {
328
+ const i = t[n + 1];
329
+ if (i === void 0 || i.length === 0)
330
+ return { error: "Missing value for --config-file." };
331
+ e = i, n += 1;
332
+ continue;
333
+ }
334
+ if (o.startsWith("--config-file=")) {
335
+ const i = o.slice(14);
336
+ if (i.length === 0)
337
+ return { error: "Missing value for --config-file." };
338
+ e = i;
339
+ }
340
+ }
341
+ return { configFilePath: e };
342
+ }
343
+ async function ie() {
344
+ const t = process.argv.slice(2);
345
+ if (T(void 0), t.includes("--help") || t.includes("-h"))
346
+ return k(), Nt(), 0;
347
+ const n = Zt(t);
348
+ if (n.error)
349
+ return k(), console.error(n.error), 1;
350
+ T(n.configFilePath);
351
+ const o = t.includes("--update-snapshots") || t.includes("-u");
352
+ wt(o), k();
353
+ const i = /* @__PURE__ */ new Date();
354
+ mt(), gt(), lt();
355
+ const a = await Rt(), c = await Z(process.cwd(), a);
356
+ if (c.length === 0)
357
+ return console.log("No .eval.js or .eval.ts files found."), 1;
358
+ const g = (await Promise.allSettled(
359
+ c.map(
360
+ (r) => St.run(
361
+ { evalFile: r },
362
+ () => import(Y(r).href)
363
+ )
364
+ )
365
+ )).map((r, u) => ({ result: r, file: c[u] })).filter(({ result: r }) => r.status === "rejected");
366
+ if (g.length > 0) {
367
+ for (const r of g) {
368
+ const u = r.result.status === "rejected" ? r.result.reason : void 0;
369
+ console.error(`Error executing ${r.file}: ${String(u)}`);
370
+ }
371
+ return 1;
372
+ }
373
+ const L = (await dt()).filter(
374
+ (r) => r.status === "rejected"
375
+ );
376
+ if (L.length > 0) {
377
+ for (const r of L)
378
+ r.status === "rejected" && console.error(`Error executing async test: ${String(r.reason)}`);
379
+ return 1;
380
+ }
381
+ const y = ut();
382
+ if (y.length > 0) {
383
+ console.error("❌ Failed tests:");
384
+ for (const [r, u] of y.entries()) {
385
+ const $ = [u.describePath, u.itPath].filter((K) => K.length > 0).join(" > "), z = $.length > 0 ? `${$}: ` : "";
386
+ console.error(`${r + 1}. ${z}${u.message}`);
387
+ }
388
+ return 1;
389
+ }
390
+ const O = ct(), B = Date.now() - i.getTime();
391
+ return console.log(
392
+ [
393
+ "---",
394
+ `${l("Files")} ${c.length} passed`,
395
+ `${l("Evals")} ${O} passed`,
396
+ `${l("Start at")} ${Tt(i)}`,
397
+ `${l("Duration")} ${B}ms`
398
+ ].join(`
399
+ `)
400
+ ), 0;
401
+ }
402
+ export {
403
+ _t as a,
404
+ Qt as b,
405
+ Ot as c,
406
+ Yt as d,
407
+ Bt as e,
408
+ zt as f,
409
+ te as g,
410
+ oe as h,
411
+ Dt as i,
412
+ St as j,
413
+ qt as k,
414
+ ee as l,
415
+ st as m,
416
+ it as n,
417
+ ne as o,
418
+ Xt as p,
419
+ Kt as q,
420
+ Mt as r,
421
+ Et as s,
422
+ Vt as t,
423
+ ie as u
424
+ };
@@ -0,0 +1,4 @@
1
+ {
2
+ "agent": "codex",
3
+ "ignorePatterns": ["./examples/**"]
4
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "katt",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "CLI tool that tests the output of agentic AI tools",
5
5
  "keywords": [
6
6
  "cli",
@@ -29,21 +29,22 @@
29
29
  "format": "biome format --write ./src",
30
30
  "test": "vitest",
31
31
  "typecheck": "tsc -p tsconfig.json --noEmit",
32
- "test:build": "node ./dist/katt.js"
32
+ "test:build": "node ./dist/katt.js",
33
+ "test:build:codex": "node ./dist/katt.js --config-file=./katt-codex.json"
33
34
  },
34
35
  "types": "dist/index.d.ts",
35
36
  "devDependencies": {
36
- "@biomejs/biome": "1.9.4",
37
- "@types/node": "25.2.0",
37
+ "@biomejs/biome": "2.4.2",
38
+ "@types/node": "25.2.3",
38
39
  "tsx": "4.21.0",
39
- "typescript": "5.8.2",
40
+ "typescript": "5.9.3",
40
41
  "vite": "7.3.1",
41
42
  "vite-plugin-dts": "4.5.4",
42
- "vitest": "3.2.4",
43
+ "vitest": "4.0.18",
43
44
  "vscode-jsonrpc": "^8.2.1"
44
45
  },
45
46
  "dependencies": {
46
- "@github/copilot-sdk": "^0.1.21"
47
+ "@github/copilot-sdk": "0.1.23"
47
48
  },
48
49
  "bugs": {
49
50
  "url": "https://github.com/raphaelpor/katt/issues"
package/renovate.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
+ "extends": [
4
+ "config:recommended"
5
+ ]
6
+ }