@paretools/test 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +12 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/lib/detect.d.ts +7 -0
  7. package/dist/lib/detect.d.ts.map +1 -0
  8. package/dist/lib/detect.js +56 -0
  9. package/dist/lib/detect.js.map +1 -0
  10. package/dist/lib/formatters.d.ts +4 -0
  11. package/dist/lib/formatters.d.ts.map +1 -0
  12. package/dist/lib/formatters.js +23 -0
  13. package/dist/lib/formatters.js.map +1 -0
  14. package/dist/lib/parsers/index.d.ts +4 -0
  15. package/dist/lib/parsers/index.d.ts.map +1 -0
  16. package/dist/lib/parsers/index.js +4 -0
  17. package/dist/lib/parsers/index.js.map +1 -0
  18. package/dist/lib/parsers/jest.d.ts +18 -0
  19. package/dist/lib/parsers/jest.d.ts.map +1 -0
  20. package/dist/lib/parsers/jest.js +82 -0
  21. package/dist/lib/parsers/jest.js.map +1 -0
  22. package/dist/lib/parsers/pytest.d.ts +26 -0
  23. package/dist/lib/parsers/pytest.d.ts.map +1 -0
  24. package/dist/lib/parsers/pytest.js +105 -0
  25. package/dist/lib/parsers/pytest.js.map +1 -0
  26. package/dist/lib/parsers/vitest.d.ts +11 -0
  27. package/dist/lib/parsers/vitest.d.ts.map +1 -0
  28. package/dist/lib/parsers/vitest.js +74 -0
  29. package/dist/lib/parsers/vitest.js.map +1 -0
  30. package/dist/schemas/index.d.ts +195 -0
  31. package/dist/schemas/index.d.ts.map +1 -0
  32. package/dist/schemas/index.js +38 -0
  33. package/dist/schemas/index.js.map +1 -0
  34. package/dist/tools/coverage.d.ts +3 -0
  35. package/dist/tools/coverage.d.ts.map +1 -0
  36. package/dist/tools/coverage.js +55 -0
  37. package/dist/tools/coverage.js.map +1 -0
  38. package/dist/tools/index.d.ts +3 -0
  39. package/dist/tools/index.d.ts.map +1 -0
  40. package/dist/tools/index.js +7 -0
  41. package/dist/tools/index.js.map +1 -0
  42. package/dist/tools/run.d.ts +3 -0
  43. package/dist/tools/run.d.ts.map +1 -0
  44. package/dist/tools/run.js +89 -0
  45. package/dist/tools/run.js.map +1 -0
  46. package/package.json +44 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dave London
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { registerAllTools } from "./tools/index.js";
5
+ const server = new McpServer({
6
+ name: "@paretools/test",
7
+ version: "0.1.0",
8
+ });
9
+ registerAllTools(server);
10
+ const transport = new StdioServerTransport();
11
+ await server.connect(transport);
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,iBAAiB;IACvB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAEzB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ export type Framework = "pytest" | "jest" | "vitest";
2
+ /**
3
+ * Auto-detects the test framework in use at the given directory.
4
+ * Priority: vitest > jest > pytest (check most specific first).
5
+ */
6
+ export declare function detectFramework(cwd: string): Promise<Framework>;
7
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/lib/detect.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AA0BrD;;;GAGG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAqCrE"}
@@ -0,0 +1,56 @@
1
+ import { readFile, access } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ async function exists(path) {
4
+ try {
5
+ await access(path);
6
+ return true;
7
+ }
8
+ catch {
9
+ return false;
10
+ }
11
+ }
12
+ async function readPackageJson(cwd) {
13
+ try {
14
+ const raw = await readFile(join(cwd, "package.json"), "utf-8");
15
+ return JSON.parse(raw);
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ function hasDep(pkg, name) {
22
+ const deps = pkg.devDependencies;
23
+ const prodDeps = pkg.dependencies;
24
+ return !!(deps?.[name] || prodDeps?.[name]);
25
+ }
26
+ /**
27
+ * Auto-detects the test framework in use at the given directory.
28
+ * Priority: vitest > jest > pytest (check most specific first).
29
+ */
30
+ export async function detectFramework(cwd) {
31
+ const pkg = await readPackageJson(cwd);
32
+ // Vitest: config file or dependency
33
+ if ((await exists(join(cwd, "vitest.config.ts"))) ||
34
+ (await exists(join(cwd, "vitest.config.js"))) ||
35
+ (await exists(join(cwd, "vitest.config.mts"))) ||
36
+ (pkg && hasDep(pkg, "vitest"))) {
37
+ return "vitest";
38
+ }
39
+ // Jest: config file or dependency
40
+ if ((await exists(join(cwd, "jest.config.js"))) ||
41
+ (await exists(join(cwd, "jest.config.ts"))) ||
42
+ (await exists(join(cwd, "jest.config.mjs"))) ||
43
+ (pkg && hasDep(pkg, "jest"))) {
44
+ return "jest";
45
+ }
46
+ // Pytest: Python project markers
47
+ if ((await exists(join(cwd, "pytest.ini"))) ||
48
+ (await exists(join(cwd, "setup.cfg"))) ||
49
+ (await exists(join(cwd, "pyproject.toml"))) ||
50
+ (await exists(join(cwd, "conftest.py")))) {
51
+ return "pytest";
52
+ }
53
+ throw new Error("No supported test framework detected. Supported: vitest, jest, pytest. " +
54
+ "Ensure the project has the framework installed or a config file present.");
55
+ }
56
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/lib/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,KAAK,UAAU,MAAM,CAAC,IAAY;IAChC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,GAAW;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;QAC/D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,GAA4B,EAAE,IAAY;IACxD,MAAM,IAAI,GAAG,GAAG,CAAC,eAAqD,CAAC;IACvE,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAkD,CAAC;IACxE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW;IAC/C,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;IAEvC,oCAAoC;IACpC,IACE,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC,CAAC;QAC7C,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC,CAAC;QAC7C,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAC9C,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,EAC9B,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,kCAAkC;IAClC,IACE,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC;QAC3C,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC;QAC3C,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC;QAC5C,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,EAC5B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,iCAAiC;IACjC,IACE,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC;QACvC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC;QACtC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC;QAC3C,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,EACxC,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,KAAK,CACb,yEAAyE;QACvE,0EAA0E,CAC7E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { TestRun, Coverage } from "../schemas/index.js";
2
+ export declare function formatTestRun(r: TestRun): string;
3
+ export declare function formatCoverage(c: Coverage): string;
4
+ //# sourceMappingURL=formatters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../src/lib/formatters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE7D,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAYhD;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAWlD"}
@@ -0,0 +1,23 @@
1
+ export function formatTestRun(r) {
2
+ const status = r.summary.failed > 0 ? "FAIL" : "PASS";
3
+ const parts = [
4
+ `${status} (${r.framework}) ${r.summary.total} tests: ${r.summary.passed} passed, ${r.summary.failed} failed, ${r.summary.skipped} skipped [${r.summary.duration}s]`,
5
+ ];
6
+ for (const f of r.failures) {
7
+ const loc = f.file ? `${f.file}${f.line ? `:${f.line}` : ""}` : "";
8
+ parts.push(` FAIL ${f.name}${loc ? ` (${loc})` : ""}: ${f.message}`);
9
+ }
10
+ return parts.join("\n");
11
+ }
12
+ export function formatCoverage(c) {
13
+ const parts = [`Coverage (${c.framework}): ${c.summary.lines}% lines`];
14
+ if (c.summary.branches !== undefined)
15
+ parts[0] += `, ${c.summary.branches}% branches`;
16
+ if (c.summary.functions !== undefined)
17
+ parts[0] += `, ${c.summary.functions}% functions`;
18
+ for (const f of c.files) {
19
+ parts.push(` ${f.file}: ${f.lines}% lines`);
20
+ }
21
+ return parts.join("\n");
22
+ }
23
+ //# sourceMappingURL=formatters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatters.js","sourceRoot":"","sources":["../../src/lib/formatters.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa,CAAC,CAAU;IACtC,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IACtD,MAAM,KAAK,GAAG;QACZ,GAAG,MAAM,KAAK,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,CAAC,OAAO,CAAC,MAAM,YAAY,CAAC,CAAC,OAAO,CAAC,MAAM,YAAY,CAAC,CAAC,OAAO,CAAC,OAAO,aAAa,CAAC,CAAC,OAAO,CAAC,QAAQ,IAAI;KACrK,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,CAAW;IACxC,MAAM,KAAK,GAAG,CAAC,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC;IAEvE,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS;QAAE,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,YAAY,CAAC;IACtF,IAAI,CAAC,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS;QAAE,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,aAAa,CAAC;IAEzF,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { parsePytestOutput, parsePytestCoverage } from "./pytest.js";
2
+ export { parseJestJson, parseJestCoverage } from "./jest.js";
3
+ export { parseVitestJson, parseVitestCoverage } from "./vitest.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/parsers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { parsePytestOutput, parsePytestCoverage } from "./pytest.js";
2
+ export { parseJestJson, parseJestCoverage } from "./jest.js";
3
+ export { parseVitestJson, parseVitestCoverage } from "./vitest.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/parsers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { TestRun, Coverage } from "../../schemas/index.js";
2
+ /**
3
+ * Parses Jest JSON output (`jest --json`) into structured data.
4
+ */
5
+ export declare function parseJestJson(jsonStr: string): TestRun;
6
+ /**
7
+ * Parses Jest text coverage summary output.
8
+ *
9
+ * Expected format:
10
+ * ----------|---------|----------|---------|---------|
11
+ * File | % Stmts | % Branch | % Funcs | % Lines |
12
+ * ----------|---------|----------|---------|---------|
13
+ * All files | 85.71 | 66.67 | 100 | 85.71 |
14
+ * foo.ts | 85.71 | 66.67 | 100 | 85.71 |
15
+ * ----------|---------|----------|---------|---------|
16
+ */
17
+ export declare function parseJestCoverage(stdout: string): Coverage;
18
+ //# sourceMappingURL=jest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest.d.ts","sourceRoot":"","sources":["../../../src/lib/parsers/jest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAmChE;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAyCtD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAmC1D"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Parses Jest JSON output (`jest --json`) into structured data.
3
+ */
4
+ export function parseJestJson(jsonStr) {
5
+ const data = JSON.parse(jsonStr);
6
+ const endTime = Date.now();
7
+ const duration = (endTime - data.startTime) / 1000;
8
+ const failures = [];
9
+ for (const suite of data.testResults) {
10
+ for (const test of suite.testResults) {
11
+ if (test.status === "failed") {
12
+ const message = test.failureMessages.join("\n").trim();
13
+ // Try to extract expected/actual from Jest's diff output
14
+ const expectedMatch = message.match(/Expected[:\s]+(.+)/);
15
+ const actualMatch = message.match(/Received[:\s]+(.+)/);
16
+ failures.push({
17
+ name: test.fullName,
18
+ file: suite.testFilePath,
19
+ line: test.location?.line,
20
+ message: message.split("\n")[0] || "Test failed",
21
+ expected: expectedMatch?.[1]?.trim(),
22
+ actual: actualMatch?.[1]?.trim(),
23
+ stack: message,
24
+ });
25
+ }
26
+ }
27
+ }
28
+ return {
29
+ framework: "jest",
30
+ summary: {
31
+ total: data.numTotalTests,
32
+ passed: data.numPassedTests,
33
+ failed: data.numFailedTests,
34
+ skipped: data.numPendingTests,
35
+ duration: Math.round(duration * 100) / 100,
36
+ },
37
+ failures,
38
+ };
39
+ }
40
+ /**
41
+ * Parses Jest text coverage summary output.
42
+ *
43
+ * Expected format:
44
+ * ----------|---------|----------|---------|---------|
45
+ * File | % Stmts | % Branch | % Funcs | % Lines |
46
+ * ----------|---------|----------|---------|---------|
47
+ * All files | 85.71 | 66.67 | 100 | 85.71 |
48
+ * foo.ts | 85.71 | 66.67 | 100 | 85.71 |
49
+ * ----------|---------|----------|---------|---------|
50
+ */
51
+ export function parseJestCoverage(stdout) {
52
+ const lines = stdout.split("\n");
53
+ const files = [];
54
+ let summary = { lines: 0, branches: 0, functions: 0 };
55
+ for (const line of lines) {
56
+ // Match coverage table rows: " file | stmts | branch | funcs | lines | uncovered"
57
+ const match = line.match(/\s*(.+?)\s*\|\s*([\d.]+)\s*\|\s*([\d.]+)\s*\|\s*([\d.]+)\s*\|\s*([\d.]+)\s*\|/);
58
+ if (!match)
59
+ continue;
60
+ const [, name, , branch, funcs, linePct] = match;
61
+ const trimName = name.trim();
62
+ if (trimName === "File" || trimName.match(/^-+$/))
63
+ continue;
64
+ const entry = {
65
+ lines: parseFloat(linePct),
66
+ branches: parseFloat(branch),
67
+ functions: parseFloat(funcs),
68
+ };
69
+ if (trimName === "All files") {
70
+ summary = entry;
71
+ }
72
+ else {
73
+ files.push({ file: trimName, ...entry });
74
+ }
75
+ }
76
+ return {
77
+ framework: "jest",
78
+ summary,
79
+ files,
80
+ };
81
+ }
82
+ //# sourceMappingURL=jest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest.js","sourceRoot":"","sources":["../../../src/lib/parsers/jest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { TestRun, Coverage } from "../../schemas/index.js";
2
+ /**
3
+ * Parses pytest verbose output (-v) into structured data.
4
+ *
5
+ * Expected formats:
6
+ * tests/test_foo.py::test_bar PASSED
7
+ * tests/test_foo.py::test_baz FAILED
8
+ * ===== short test summary info =====
9
+ * FAILED tests/test_foo.py::test_baz - AssertionError: assert 1 == 2
10
+ * ===== 1 failed, 9 passed in 0.42s =====
11
+ */
12
+ export declare function parsePytestOutput(stdout: string): TestRun;
13
+ /**
14
+ * Parses pytest coverage output (from pytest-cov).
15
+ *
16
+ * Expected format:
17
+ * ---------- coverage: ... ----------
18
+ * Name Stmts Miss Cover
19
+ * -------------------------------------------
20
+ * src/foo.py 50 5 90%
21
+ * src/bar.py 30 10 67%
22
+ * -------------------------------------------
23
+ * TOTAL 80 15 81%
24
+ */
25
+ export declare function parsePytestCoverage(stdout: string): Coverage;
26
+ //# sourceMappingURL=pytest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pytest.d.ts","sourceRoot":"","sources":["../../../src/lib/parsers/pytest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CA8DzD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAgC5D"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Parses pytest verbose output (-v) into structured data.
3
+ *
4
+ * Expected formats:
5
+ * tests/test_foo.py::test_bar PASSED
6
+ * tests/test_foo.py::test_baz FAILED
7
+ * ===== short test summary info =====
8
+ * FAILED tests/test_foo.py::test_baz - AssertionError: assert 1 == 2
9
+ * ===== 1 failed, 9 passed in 0.42s =====
10
+ */
11
+ export function parsePytestOutput(stdout) {
12
+ const lines = stdout.split("\n");
13
+ // Parse summary line: "1 failed, 9 passed, 2 skipped in 0.42s"
14
+ const summaryMatch = stdout.match(/=+ (?:(\d+) failed)?[, ]*(?:(\d+) passed)?[, ]*(?:(\d+) skipped)?[, ]*(?:(\d+) error)?[, ]*in ([\d.]+)s/);
15
+ const failed = summaryMatch ? parseInt(summaryMatch[1] || "0", 10) : 0;
16
+ const passed = summaryMatch ? parseInt(summaryMatch[2] || "0", 10) : 0;
17
+ const skipped = summaryMatch ? parseInt(summaryMatch[3] || "0", 10) : 0;
18
+ const duration = summaryMatch ? parseFloat(summaryMatch[5] || "0") : 0;
19
+ // Parse failures from short test summary
20
+ const failures = [];
21
+ const summaryStart = lines.findIndex((l) => l.includes("short test summary info"));
22
+ const summaryEnd = lines.findIndex((l, i) => i > summaryStart && summaryStart >= 0 && l.match(/^=+ /));
23
+ if (summaryStart >= 0) {
24
+ const failLines = lines.slice(summaryStart + 1, summaryEnd > summaryStart ? summaryEnd : undefined);
25
+ for (const line of failLines) {
26
+ const match = line.match(/^FAILED\s+(.+?)(?:::(.+?))?\s*-\s*(.+)/);
27
+ if (match) {
28
+ failures.push({
29
+ file: match[1],
30
+ name: match[2] || match[1],
31
+ message: match[3].trim(),
32
+ });
33
+ }
34
+ }
35
+ }
36
+ // If no summary section, try to parse FAILED lines from verbose output
37
+ if (failures.length === 0 && failed > 0) {
38
+ for (const line of lines) {
39
+ const match = line.match(/^(.+?)::(.+?)\s+FAILED/);
40
+ if (match) {
41
+ failures.push({
42
+ file: match[1],
43
+ name: match[2],
44
+ message: "Test failed (details not captured in verbose output)",
45
+ });
46
+ }
47
+ }
48
+ }
49
+ return {
50
+ framework: "pytest",
51
+ summary: {
52
+ total: passed + failed + skipped,
53
+ passed,
54
+ failed,
55
+ skipped,
56
+ duration,
57
+ },
58
+ failures,
59
+ };
60
+ }
61
+ /**
62
+ * Parses pytest coverage output (from pytest-cov).
63
+ *
64
+ * Expected format:
65
+ * ---------- coverage: ... ----------
66
+ * Name Stmts Miss Cover
67
+ * -------------------------------------------
68
+ * src/foo.py 50 5 90%
69
+ * src/bar.py 30 10 67%
70
+ * -------------------------------------------
71
+ * TOTAL 80 15 81%
72
+ */
73
+ export function parsePytestCoverage(stdout) {
74
+ const lines = stdout.split("\n");
75
+ const files = [];
76
+ let totalPct = 0;
77
+ let inCoverage = false;
78
+ for (const line of lines) {
79
+ if (line.includes("Stmts") && line.includes("Miss") && line.includes("Cover")) {
80
+ inCoverage = true;
81
+ continue;
82
+ }
83
+ if (!inCoverage)
84
+ continue;
85
+ if (line.match(/^-+$/))
86
+ continue;
87
+ const match = line.match(/^(\S+)\s+(\d+)\s+(\d+)\s+(\d+)%/);
88
+ if (match) {
89
+ const [, file, , , cover] = match;
90
+ const pct = parseInt(cover, 10);
91
+ if (file === "TOTAL") {
92
+ totalPct = pct;
93
+ }
94
+ else {
95
+ files.push({ file, lines: pct });
96
+ }
97
+ }
98
+ }
99
+ return {
100
+ framework: "pytest",
101
+ summary: { lines: totalPct },
102
+ files,
103
+ };
104
+ }
105
+ //# sourceMappingURL=pytest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pytest.js","sourceRoot":"","sources":["../../../src/lib/parsers/pytest.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEjC,+DAA+D;IAC/D,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAC/B,yGAAyG,CAC1G,CAAC;IAEF,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,QAAQ,GAAG,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvE,yCAAyC;IACzC,MAAM,QAAQ,GAAwB,EAAE,CAAC;IACzC,MAAM,YAAY,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC,CAAC;IACnF,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAChC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,YAAY,IAAI,YAAY,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CACnE,CAAC;IAEF,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAC3B,YAAY,GAAG,CAAC,EAChB,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CACnD,CAAC;QACF,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;YACnE,IAAI,KAAK,EAAE,CAAC;gBACV,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;oBACd,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;oBAC1B,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;iBACzB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;YACnD,IAAI,KAAK,EAAE,CAAC;gBACV,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;oBACd,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;oBACd,OAAO,EAAE,sDAAsD;iBAChE,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE;YACP,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO;YAChC,MAAM;YACN,MAAM;YACN,OAAO;YACP,QAAQ;SACT;QACD,QAAQ;KACT,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,KAAK,GAAsB,EAAE,CAAC;IACpC,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9E,UAAU,GAAG,IAAI,CAAC;YAClB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,UAAU;YAAE,SAAS;QAC1B,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,SAAS;QAEjC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAC5D,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,EAAE,IAAI,EAAE,AAAD,EAAG,AAAD,EAAG,KAAK,CAAC,GAAG,KAAK,CAAC;YAClC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAEhC,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;gBACrB,QAAQ,GAAG,GAAG,CAAC;YACjB,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE;QAC5B,KAAK;KACN,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { TestRun, Coverage } from "../../schemas/index.js";
2
+ /**
3
+ * Parses Vitest JSON output (`vitest run --reporter=json`) into structured data.
4
+ * Vitest's JSON format is Jest-compatible, so the structure is very similar.
5
+ */
6
+ export declare function parseVitestJson(jsonStr: string): TestRun;
7
+ /**
8
+ * Parses Vitest text coverage output (uses c8/istanbul format, same as Jest).
9
+ */
10
+ export declare function parseVitestCoverage(stdout: string): Coverage;
11
+ //# sourceMappingURL=vitest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../../../src/lib/parsers/vitest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAwBhE;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAyCxD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAkC5D"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Parses Vitest JSON output (`vitest run --reporter=json`) into structured data.
3
+ * Vitest's JSON format is Jest-compatible, so the structure is very similar.
4
+ */
5
+ export function parseVitestJson(jsonStr) {
6
+ const data = JSON.parse(jsonStr);
7
+ const endTime = Date.now();
8
+ const duration = (endTime - data.startTime) / 1000;
9
+ const failures = [];
10
+ for (const suite of data.testResults) {
11
+ for (const test of suite.assertionResults) {
12
+ if (test.status === "failed") {
13
+ const message = test.failureMessages.join("\n").trim();
14
+ // Vitest uses similar assertion format to Jest
15
+ const expectedMatch = message.match(/Expected[:\s]+(.+)/);
16
+ const actualMatch = message.match(/Received[:\s]+(.+)/);
17
+ failures.push({
18
+ name: test.fullName,
19
+ file: suite.name,
20
+ line: test.location?.line,
21
+ message: message.split("\n")[0] || "Test failed",
22
+ expected: expectedMatch?.[1]?.trim(),
23
+ actual: actualMatch?.[1]?.trim(),
24
+ stack: message,
25
+ });
26
+ }
27
+ }
28
+ }
29
+ return {
30
+ framework: "vitest",
31
+ summary: {
32
+ total: data.numTotalTests,
33
+ passed: data.numPassedTests,
34
+ failed: data.numFailedTests,
35
+ skipped: data.numPendingTests + (data.numTodoTests ?? 0),
36
+ duration: Math.round(duration * 100) / 100,
37
+ },
38
+ failures,
39
+ };
40
+ }
41
+ /**
42
+ * Parses Vitest text coverage output (uses c8/istanbul format, same as Jest).
43
+ */
44
+ export function parseVitestCoverage(stdout) {
45
+ const lines = stdout.split("\n");
46
+ const files = [];
47
+ let summary = { lines: 0, branches: 0, functions: 0 };
48
+ for (const line of lines) {
49
+ const match = line.match(/\s*(.+?)\s*\|\s*([\d.]+)\s*\|\s*([\d.]+)\s*\|\s*([\d.]+)\s*\|\s*([\d.]+)\s*\|/);
50
+ if (!match)
51
+ continue;
52
+ const [, name, , branch, funcs, linePct] = match;
53
+ const trimName = name.trim();
54
+ if (trimName === "File" || trimName.match(/^-+$/))
55
+ continue;
56
+ const entry = {
57
+ lines: parseFloat(linePct),
58
+ branches: parseFloat(branch),
59
+ functions: parseFloat(funcs),
60
+ };
61
+ if (trimName === "All files") {
62
+ summary = entry;
63
+ }
64
+ else {
65
+ files.push({ file: trimName, ...entry });
66
+ }
67
+ }
68
+ return {
69
+ framework: "vitest",
70
+ summary,
71
+ files,
72
+ };
73
+ }
74
+ //# sourceMappingURL=vitest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../../../src/lib/parsers/vitest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,195 @@
1
+ import { z } from "zod";
2
+ export declare const TestFailureSchema: z.ZodObject<{
3
+ name: z.ZodString;
4
+ file: z.ZodOptional<z.ZodString>;
5
+ line: z.ZodOptional<z.ZodNumber>;
6
+ message: z.ZodString;
7
+ expected: z.ZodOptional<z.ZodString>;
8
+ actual: z.ZodOptional<z.ZodString>;
9
+ stack: z.ZodOptional<z.ZodString>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ name: string;
12
+ message: string;
13
+ file?: string | undefined;
14
+ line?: number | undefined;
15
+ expected?: string | undefined;
16
+ actual?: string | undefined;
17
+ stack?: string | undefined;
18
+ }, {
19
+ name: string;
20
+ message: string;
21
+ file?: string | undefined;
22
+ line?: number | undefined;
23
+ expected?: string | undefined;
24
+ actual?: string | undefined;
25
+ stack?: string | undefined;
26
+ }>;
27
+ export type TestFailure = z.infer<typeof TestFailureSchema>;
28
+ export declare const TestRunSchema: z.ZodObject<{
29
+ framework: z.ZodEnum<["pytest", "jest", "vitest"]>;
30
+ summary: z.ZodObject<{
31
+ total: z.ZodNumber;
32
+ passed: z.ZodNumber;
33
+ failed: z.ZodNumber;
34
+ skipped: z.ZodNumber;
35
+ duration: z.ZodNumber;
36
+ }, "strip", z.ZodTypeAny, {
37
+ total: number;
38
+ passed: number;
39
+ failed: number;
40
+ skipped: number;
41
+ duration: number;
42
+ }, {
43
+ total: number;
44
+ passed: number;
45
+ failed: number;
46
+ skipped: number;
47
+ duration: number;
48
+ }>;
49
+ failures: z.ZodArray<z.ZodObject<{
50
+ name: z.ZodString;
51
+ file: z.ZodOptional<z.ZodString>;
52
+ line: z.ZodOptional<z.ZodNumber>;
53
+ message: z.ZodString;
54
+ expected: z.ZodOptional<z.ZodString>;
55
+ actual: z.ZodOptional<z.ZodString>;
56
+ stack: z.ZodOptional<z.ZodString>;
57
+ }, "strip", z.ZodTypeAny, {
58
+ name: string;
59
+ message: string;
60
+ file?: string | undefined;
61
+ line?: number | undefined;
62
+ expected?: string | undefined;
63
+ actual?: string | undefined;
64
+ stack?: string | undefined;
65
+ }, {
66
+ name: string;
67
+ message: string;
68
+ file?: string | undefined;
69
+ line?: number | undefined;
70
+ expected?: string | undefined;
71
+ actual?: string | undefined;
72
+ stack?: string | undefined;
73
+ }>, "many">;
74
+ }, "strip", z.ZodTypeAny, {
75
+ framework: "pytest" | "jest" | "vitest";
76
+ summary: {
77
+ total: number;
78
+ passed: number;
79
+ failed: number;
80
+ skipped: number;
81
+ duration: number;
82
+ };
83
+ failures: {
84
+ name: string;
85
+ message: string;
86
+ file?: string | undefined;
87
+ line?: number | undefined;
88
+ expected?: string | undefined;
89
+ actual?: string | undefined;
90
+ stack?: string | undefined;
91
+ }[];
92
+ }, {
93
+ framework: "pytest" | "jest" | "vitest";
94
+ summary: {
95
+ total: number;
96
+ passed: number;
97
+ failed: number;
98
+ skipped: number;
99
+ duration: number;
100
+ };
101
+ failures: {
102
+ name: string;
103
+ message: string;
104
+ file?: string | undefined;
105
+ line?: number | undefined;
106
+ expected?: string | undefined;
107
+ actual?: string | undefined;
108
+ stack?: string | undefined;
109
+ }[];
110
+ }>;
111
+ export type TestRun = z.infer<typeof TestRunSchema>;
112
+ export declare const CoverageFileSchema: z.ZodObject<{
113
+ file: z.ZodString;
114
+ lines: z.ZodNumber;
115
+ branches: z.ZodOptional<z.ZodNumber>;
116
+ functions: z.ZodOptional<z.ZodNumber>;
117
+ uncoveredLines: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
118
+ }, "strip", z.ZodTypeAny, {
119
+ file: string;
120
+ lines: number;
121
+ branches?: number | undefined;
122
+ functions?: number | undefined;
123
+ uncoveredLines?: number[] | undefined;
124
+ }, {
125
+ file: string;
126
+ lines: number;
127
+ branches?: number | undefined;
128
+ functions?: number | undefined;
129
+ uncoveredLines?: number[] | undefined;
130
+ }>;
131
+ export declare const CoverageSchema: z.ZodObject<{
132
+ framework: z.ZodEnum<["pytest", "jest", "vitest"]>;
133
+ summary: z.ZodObject<{
134
+ lines: z.ZodNumber;
135
+ branches: z.ZodOptional<z.ZodNumber>;
136
+ functions: z.ZodOptional<z.ZodNumber>;
137
+ }, "strip", z.ZodTypeAny, {
138
+ lines: number;
139
+ branches?: number | undefined;
140
+ functions?: number | undefined;
141
+ }, {
142
+ lines: number;
143
+ branches?: number | undefined;
144
+ functions?: number | undefined;
145
+ }>;
146
+ files: z.ZodArray<z.ZodObject<{
147
+ file: z.ZodString;
148
+ lines: z.ZodNumber;
149
+ branches: z.ZodOptional<z.ZodNumber>;
150
+ functions: z.ZodOptional<z.ZodNumber>;
151
+ uncoveredLines: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
152
+ }, "strip", z.ZodTypeAny, {
153
+ file: string;
154
+ lines: number;
155
+ branches?: number | undefined;
156
+ functions?: number | undefined;
157
+ uncoveredLines?: number[] | undefined;
158
+ }, {
159
+ file: string;
160
+ lines: number;
161
+ branches?: number | undefined;
162
+ functions?: number | undefined;
163
+ uncoveredLines?: number[] | undefined;
164
+ }>, "many">;
165
+ }, "strip", z.ZodTypeAny, {
166
+ framework: "pytest" | "jest" | "vitest";
167
+ summary: {
168
+ lines: number;
169
+ branches?: number | undefined;
170
+ functions?: number | undefined;
171
+ };
172
+ files: {
173
+ file: string;
174
+ lines: number;
175
+ branches?: number | undefined;
176
+ functions?: number | undefined;
177
+ uncoveredLines?: number[] | undefined;
178
+ }[];
179
+ }, {
180
+ framework: "pytest" | "jest" | "vitest";
181
+ summary: {
182
+ lines: number;
183
+ branches?: number | undefined;
184
+ functions?: number | undefined;
185
+ };
186
+ files: {
187
+ file: string;
188
+ lines: number;
189
+ branches?: number | undefined;
190
+ functions?: number | undefined;
191
+ uncoveredLines?: number[] | undefined;
192
+ }[];
193
+ }>;
194
+ export type Coverage = z.infer<typeof CoverageSchema>;
195
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;EAQ5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUxB,CAAC;AAEH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;EAM7B,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQzB,CAAC;AAEH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { z } from "zod";
2
+ export const TestFailureSchema = z.object({
3
+ name: z.string(),
4
+ file: z.string().optional(),
5
+ line: z.number().optional(),
6
+ message: z.string(),
7
+ expected: z.string().optional(),
8
+ actual: z.string().optional(),
9
+ stack: z.string().optional(),
10
+ });
11
+ export const TestRunSchema = z.object({
12
+ framework: z.enum(["pytest", "jest", "vitest"]),
13
+ summary: z.object({
14
+ total: z.number(),
15
+ passed: z.number(),
16
+ failed: z.number(),
17
+ skipped: z.number(),
18
+ duration: z.number(),
19
+ }),
20
+ failures: z.array(TestFailureSchema),
21
+ });
22
+ export const CoverageFileSchema = z.object({
23
+ file: z.string(),
24
+ lines: z.number(),
25
+ branches: z.number().optional(),
26
+ functions: z.number().optional(),
27
+ uncoveredLines: z.array(z.number()).optional(),
28
+ });
29
+ export const CoverageSchema = z.object({
30
+ framework: z.enum(["pytest", "jest", "vitest"]),
31
+ summary: z.object({
32
+ lines: z.number(),
33
+ branches: z.number().optional(),
34
+ functions: z.number().optional(),
35
+ }),
36
+ files: z.array(CoverageFileSchema),
37
+ });
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC7B,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC/C,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAClB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAClB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;QACnB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;KACrB,CAAC;IACF,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC;CACrC,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC/C,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC/C,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC/B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KACjC,CAAC;IACF,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC;CACnC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerCoverageTool(server: McpServer): void;
3
+ //# sourceMappingURL=coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../src/tools/coverage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAuBzE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,QAuCrD"}
@@ -0,0 +1,55 @@
1
+ import { z } from "zod";
2
+ import { dualOutput, run } from "@paretools/shared";
3
+ import { detectFramework } from "../lib/detect.js";
4
+ import { parsePytestCoverage } from "../lib/parsers/pytest.js";
5
+ import { parseJestCoverage } from "../lib/parsers/jest.js";
6
+ import { parseVitestCoverage } from "../lib/parsers/vitest.js";
7
+ import { formatCoverage } from "../lib/formatters.js";
8
+ import { CoverageSchema } from "../schemas/index.js";
9
+ function getCoverageCommand(framework) {
10
+ switch (framework) {
11
+ case "pytest":
12
+ return {
13
+ cmd: "python",
14
+ cmdArgs: ["-m", "pytest", "--cov", "--cov-report=term-missing", "-q"],
15
+ };
16
+ case "jest":
17
+ return { cmd: "npx", cmdArgs: ["jest", "--coverage", "--coverageReporters=text"] };
18
+ case "vitest":
19
+ return { cmd: "npx", cmdArgs: ["vitest", "run", "--coverage", "--reporter=default"] };
20
+ }
21
+ }
22
+ export function registerCoverageTool(server) {
23
+ server.registerTool("coverage", {
24
+ title: "Test Coverage",
25
+ description: "Runs tests with coverage and returns structured coverage summary per file",
26
+ inputSchema: {
27
+ path: z.string().optional().describe("Project root path (default: cwd)"),
28
+ framework: z
29
+ .enum(["pytest", "jest", "vitest"])
30
+ .optional()
31
+ .describe("Force a specific framework instead of auto-detecting"),
32
+ },
33
+ outputSchema: CoverageSchema,
34
+ }, async ({ path, framework }) => {
35
+ const cwd = path || process.cwd();
36
+ const detected = framework || (await detectFramework(cwd));
37
+ const { cmd, cmdArgs } = getCoverageCommand(detected);
38
+ const result = await run(cmd, cmdArgs, { cwd, timeout: 120_000 });
39
+ const output = result.stdout + "\n" + result.stderr;
40
+ let coverage;
41
+ switch (detected) {
42
+ case "pytest":
43
+ coverage = parsePytestCoverage(output);
44
+ break;
45
+ case "jest":
46
+ coverage = parseJestCoverage(output);
47
+ break;
48
+ case "vitest":
49
+ coverage = parseVitestCoverage(output);
50
+ break;
51
+ }
52
+ return dualOutput(coverage, formatCoverage);
53
+ });
54
+ }
55
+ //# sourceMappingURL=coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../src/tools/coverage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAkB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,SAAS,kBAAkB,CAAC,SAAoB;IAC9C,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,QAAQ;YACX,OAAO;gBACL,GAAG,EAAE,QAAQ;gBACb,OAAO,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,EAAE,IAAI,CAAC;aACtE,CAAC;QACJ,KAAK,MAAM;YACT,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,0BAA0B,CAAC,EAAE,CAAC;QACrF,KAAK,QAAQ;YACX,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,oBAAoB,CAAC,EAAE,CAAC;IAC1F,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAiB;IACpD,MAAM,CAAC,YAAY,CACjB,UAAU,EACV;QACE,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,2EAA2E;QACxF,WAAW,EAAE;YACX,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;YACxE,SAAS,EAAE,CAAC;iBACT,IAAI,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;iBAClC,QAAQ,EAAE;iBACV,QAAQ,CAAC,sDAAsD,CAAC;SACpE;QACD,YAAY,EAAE,cAAc;KAC7B,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,SAAS,IAAI,CAAC,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAElE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC;QAEpD,IAAI,QAAQ,CAAC;QACb,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,QAAQ;gBACX,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;gBACvC,MAAM;YACR,KAAK,MAAM;gBACT,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;gBACrC,MAAM;YACR,KAAK,QAAQ;gBACX,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;gBACvC,MAAM;QACV,CAAC;QAED,OAAO,UAAU,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IAC9C,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerAllTools(server: McpServer): void;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,QAGjD"}
@@ -0,0 +1,7 @@
1
+ import { registerRunTool } from "./run.js";
2
+ import { registerCoverageTool } from "./coverage.js";
3
+ export function registerAllTools(server) {
4
+ registerRunTool(server);
5
+ registerCoverageTool(server);
6
+ }
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,UAAU,gBAAgB,CAAC,MAAiB;IAChD,eAAe,CAAC,MAAM,CAAC,CAAC;IACxB,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerRunTool(server: McpServer): void;
3
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/tools/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoBzE,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,QAkEhD"}
@@ -0,0 +1,89 @@
1
+ import { z } from "zod";
2
+ import { dualOutput, run } from "@paretools/shared";
3
+ import { detectFramework } from "../lib/detect.js";
4
+ import { parsePytestOutput } from "../lib/parsers/pytest.js";
5
+ import { parseJestJson } from "../lib/parsers/jest.js";
6
+ import { parseVitestJson } from "../lib/parsers/vitest.js";
7
+ import { formatTestRun } from "../lib/formatters.js";
8
+ import { TestRunSchema } from "../schemas/index.js";
9
+ function getRunCommand(framework, args) {
10
+ switch (framework) {
11
+ case "pytest":
12
+ return { cmd: "python", cmdArgs: ["-m", "pytest", "-v", ...args] };
13
+ case "jest":
14
+ return { cmd: "npx", cmdArgs: ["jest", "--json", ...args] };
15
+ case "vitest":
16
+ return { cmd: "npx", cmdArgs: ["vitest", "run", "--reporter=json", ...args] };
17
+ }
18
+ }
19
+ export function registerRunTool(server) {
20
+ server.registerTool("run", {
21
+ title: "Run Tests",
22
+ description: "Auto-detects test framework (pytest/jest/vitest), runs tests, returns structured results with failures",
23
+ inputSchema: {
24
+ path: z.string().optional().describe("Project root path (default: cwd)"),
25
+ framework: z
26
+ .enum(["pytest", "jest", "vitest"])
27
+ .optional()
28
+ .describe("Force a specific framework instead of auto-detecting"),
29
+ filter: z
30
+ .string()
31
+ .optional()
32
+ .describe("Test filter pattern (file path or test name pattern)"),
33
+ args: z
34
+ .array(z.string())
35
+ .optional()
36
+ .default([])
37
+ .describe("Additional arguments to pass to the test runner"),
38
+ },
39
+ outputSchema: TestRunSchema,
40
+ }, async ({ path, framework, filter, args }) => {
41
+ const cwd = path || process.cwd();
42
+ const detected = framework || (await detectFramework(cwd));
43
+ const extraArgs = [...(args || [])];
44
+ if (filter) {
45
+ switch (detected) {
46
+ case "pytest":
47
+ extraArgs.push("-k", filter);
48
+ break;
49
+ case "jest":
50
+ extraArgs.push("--testPathPattern", filter);
51
+ break;
52
+ case "vitest":
53
+ extraArgs.push(filter);
54
+ break;
55
+ }
56
+ }
57
+ const { cmd, cmdArgs } = getRunCommand(detected, extraArgs);
58
+ const result = await run(cmd, cmdArgs, { cwd, timeout: 120_000 });
59
+ // Combine stdout and stderr for parsing (some frameworks write to stderr)
60
+ const output = result.stdout + "\n" + result.stderr;
61
+ let testRun;
62
+ switch (detected) {
63
+ case "pytest":
64
+ testRun = parsePytestOutput(output);
65
+ break;
66
+ case "jest":
67
+ testRun = parseJestJson(extractJson(output));
68
+ break;
69
+ case "vitest":
70
+ testRun = parseVitestJson(extractJson(output));
71
+ break;
72
+ }
73
+ return dualOutput(testRun, formatTestRun);
74
+ });
75
+ }
76
+ /**
77
+ * Extracts the JSON object from mixed output that may include non-JSON text
78
+ * before or after the actual JSON data.
79
+ */
80
+ function extractJson(output) {
81
+ // Try to find JSON object boundaries
82
+ const start = output.indexOf("{");
83
+ const end = output.lastIndexOf("}");
84
+ if (start === -1 || end === -1 || end <= start) {
85
+ throw new Error("No JSON output found. Ensure the test runner is configured to output JSON.");
86
+ }
87
+ return output.slice(start, end + 1);
88
+ }
89
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/tools/run.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@paretools/test",
3
+ "version": "0.2.0",
4
+ "description": "MCP server for test runners with structured, token-efficient output",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "pare-test": "./dist/index.js"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/Dave-London/pare.git",
22
+ "directory": "packages/server-test"
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "dependencies": {
28
+ "@modelcontextprotocol/sdk": "^1.26.0",
29
+ "zod": "^3.25.0",
30
+ "@paretools/shared": "0.2.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.0.0",
34
+ "typescript": "^5.7.0",
35
+ "vitest": "^3.0.0",
36
+ "@paretools/tsconfig": "0.0.0"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest",
42
+ "lint": "eslint src/"
43
+ }
44
+ }