Skip to content

Releases

typeprobe 4.0.0

Choose a tag to compare

@clockblocker clockblocker released this 10 Oct 15:50

Major Changes

  • f489e58: prinfer is now typeprobe. The package, its commands, the MCP server, and the repository (clockblocker/typeprobe) are renamed; types, results, snapshots, and the JSON contract are unchanged. prinfer 3.5.0, the last prinfer release, depends on typeprobe and forwards to it, so existing setups keep working while you migrate:

    • Install: npm rm prinfer && npm i -D typeprobe.
    • Imports: prinfer/testing → typeprobe/testing, prinfer → typeprobe, and the deprecated prinfer/vitest → typeprobe/vitest.
    • Commands: prinfer → typeprobe, prinfer-mcp → typeprobe-mcp, npx -y prinfer mcp → npx -y typeprobe mcp. typeprobe has no prinfer bins; prinfer 3.5.0 provides them.
    • MCP config: re-run setup for each client, e.g. npx -y typeprobe setup claude (or codex, cursor, vscode, gemini, with --scope as before). The server is now named typeprobe, and setup replaces an old prinfer entry instead of adding a second one: claude mcp remove/codex mcp remove drop it, and JSON configs swap it for typeprobe in place, keeping its other keys such as env (with PRINFER_* names renamed to TYPEPROBE_*). code --add-mcp can't remove servers, so delete VS Code's user-scope prinfer server by hand. Configs written by hand: rename the prinfer key and use "args": ["-y", "typeprobe", "mcp"]. npx -y typeprobe setup agents-md replaces the old <!-- prinfer:start --> block.
    • Claude Code plugin: /plugin uninstall prinfer@prinfer, /plugin marketplace remove prinfer, then /plugin marketplace add clockblocker/typeprobe and /plugin install typeprobe@typeprobe.
    • Environment variables: PRINFER_BACKEND → TYPEPROBE_BACKEND, PRINFER_COMPILER → TYPEPROBE_COMPILER. The old names are deprecated but still read when the new ones are unset.
    • Errors: the error class is now TypeprobeError (error.name === "TypeprobeError"), exported from typeprobe and typeprobe/testing for the first time. PrinferError is exported too, as a deprecated alias of the same class, so instanceof PrinferError keeps matching. instanceof holds whichever entry point (typeprobe or typeprobe/testing, ESM or CommonJS) threw the error and whichever one the class came from, though each bundles its own copy.
    • Listings: MCP Registry io.github.clockblocker/typeprobe (was io.github.clockblocker/prinfer), Smithery clockblocker/typeprobe, homepage https://clockblocker.github.io/typeprobe/.

prinfer 3.4.0

Choose a tag to compare

@clockblocker clockblocker released this 10 Oct 11:26

Minor Changes

  • 1704d78: expectType takes costCompiler ("bundled", "project", or "auto"; default compiler's) to count budgets on another TypeScript 6 than the one that prints: { backend: "typescript7", costCompiler: "project" } prints on the bundled TypeScript 7 and counts on the project's TypeScript 6. A failed budget now names both compilers (counted on typescript 5.9.3, project; printed on typescript 7.0.2, bundled, or counted and printed on ... when they are the same), and a passing expectType result names the printing one in its non-enumerable compiler. inferredTypeCost, which only counts, throws on costCompiler: its compiler already picks the counting one.

  • 1704d78: New expectTypes in prinfer/testing runs expectType on several targets of one file, with a budget for all of them together, and throws one TypeExpectationError listing every failed check: each entry's, with its index, and the group budget's, with every entry's own count.

    expectTypes(import.meta.url, {
      types: [
        { name: "userSchema", maxInstantiations: 3_000 },
        { name: "User", printed: "{ id: string; }" },
      ],
      maxInstantiations: 8_000,
    });

    The group budget counts one fresh TypeScript 6 checker resolving every target, so work they share is counted once, as a type check of the module counts it. Targets are resolved in source order, so the total doesn't depend on the order of types (a checker's count can otherwise differ by a type with the order it meets them in). project, compiler, and costCompiler go on the group; backend, full, sort_unions, readable, and timeout there are defaults for every entry.

  • 1704d78: compiler: "project" also finds a project's @typescript/native (an alias of typescript 7, the name prinfer itself installs it under) for the TypeScript 7 backend. The nearest install wins, as before; side by side, typescript 7 comes first, then @typescript/native, then @typescript/native-preview. A dev build under any name is held to the same 7.0.0-dev.20260624.1 minimum.

    Fixed: when a package manager hoists prinfer's own typescript or @typescript/native into the project's node_modules, "project" no longer runs it as if it were the project's. It counts only if the project's nearest package.json, or its workspace root's, declares the package; otherwise "project" throws, naming the copy it skipped, and "auto" uses the bundled compiler.

prinfer 3.3.0

Choose a tag to compare

@clockblocker clockblocker released this 10 Oct 08:28

Minor Changes

  • f1c359b: inferredTypeCost counts several targets of one file in one call: { names: [...] } returns costs by name, { targets: [...] } costs in order. Only project, compiler, and strict go next to them. Every count still gets a fresh checker, so a batch returns exactly what single calls would.

    Type costs are also faster to count everywhere, with the same numbers. TypeScript 6 programs for different files of one project now share their parsed files, so loading a second file no longer parses the project and its libraries again (about 300 ms down to 30 to 70 ms per file on prinfer's own source), and a target that is counted again while no file has changed is not recounted. Printed types are unchanged: each program keeps its own checker. On prinfer's own source, costing 17 targets across 6 files went from about 3 s to 1.6 s, and costing them again from 0.5 s to 30 ms.

  • f1c359b: New expectType in prinfer/testing checks a target's printed type, cost budget, and readability in one call, and throws a TypeExpectationError listing every failed check: the expected and actual text with the first difference marked, the count against its budget, and each readability issue. It only throws, so it works in any test runner; on a text mismatch the error carries actual and expected, so Vitest and Jest show their diff.

    expectType(import.meta.url, {
      name: "user",
      printed: "{ id: string; name: string; }",
      maxInstantiations: 500,
      readable: true,
    });

    The selector is inferredType's plus the checks printed, maxInstantiations, maxTypes, and readable. Costs are counted on TypeScript 6 even with backend: "typescript7", which only picks where the text comes from.

  • 5df4ec2: Print and count with the project's own TypeScript. The new compiler option picks the compilers: "bundled" (default, prinfer's own TypeScript 6 and 7, as before), "project", or "auto" (the project's when supported, else bundled). Set it in the library options and prinfer/testing selectors, with --compiler on the CLI, or with PRINFER_COMPILER on every surface, including the MCP server.

    "project" resolves typescript 5.0 to 6.x for the TypeScript 6 backend, and typescript 7 or @typescript/native-preview 7.0.0-dev.20260624.1 or later for the TypeScript 7 backend, from the directory of the file's tsconfig, and runs that package's own API client and compiler binary. A missing compiler throws a TYPESCRIPT_ERROR, and so does an unsupported one, naming its package, version, and path.

    Every result now names the compiler that produced it as compiler: { name, version, source }. On library and prinfer/testing results it is a non-enumerable property, so snapshots, toEqual, and JSON.stringify output don't change. In the CLI's --json output and the MCP tools' structured content it is a field of result. Cost lines name the compiler that counted: cost: 412 instantiations, 96 types (typescript 6.0.3, bundled).

  • f1c359b: New readability checks in prinfer/testing: typeReadabilityIssues(text, rules?) for printed type text, inferredTypeIssues(file, selector) for a target's type, and expectType's readable check. The default rules flag:

    • unresolved utility types (Omit<User, "id">, Pick, Partial, and the rest of DEFAULT_UTILITY_TYPES) at any depth, unless they apply to a type parameter of the printed signature;
    • intersections with an object type (User & { id: string; }, but not string & {});
    • truncation (... 3 more ..., { ...; }, text cut at the length limit).

    The text is parsed with the TypeScript scanner and parser, so a string literal type such as "Omit<" doesn't count. Rules take utilityTypes (your own names, or false), objectIntersections, truncation, and allow for fragments that are fine as printed.

Patch Changes

  • ac9ce4e: A file rewritten with the same size within one timestamp tick of being read is no longer served from a stale cached program. Timestamps are coarse on some systems (a clock tick on Linux without multigrain timestamps, a second on HFS+), so a test that wrote a fixture, looked up a type, and rewrote the fixture milliseconds later could get the old type. Files changed in the last 3 seconds are now also compared by content.
  • d9b798b: The package no longer ships sourcemaps (3.5 MiB smaller), and @typescript/native is loaded only when a TypeScript 7 call needs it.

prinfer 3.2.0

Choose a tag to compare

@clockblocker clockblocker released this 10 Oct 04:24
b71810c

Minor Changes

  • 04a14ac: Type costs replace type-resolution timing. include_cost (library, MCP hover tools, prinfer/testing), --cost (CLI), and the new inferredTypeCost helper report cost: { instantiations, types }: the type instantiations and types a fresh TypeScript 6 checker needs to resolve the type and write it out untruncated. A new checker for each count means no earlier lookup has done part of the work, so the numbers are the same on every run, in every process, in any order, and with any display option. A test can budget a type:

    expect(
      inferredTypeCost(import.meta.url, { name: "userSchema" }).instantiations
    ).toBeLessThan(5_000);

    Costs are TypeScript 6 only, because TypeScript 7 reports no instantiation counts. On the MCP server, a hover with include_cost and no backend runs on TypeScript 6. An explicit typescript7 backend, --cost --backend typescript7, and inferredTypeCost/include_cost with backend: "typescript7" fail with INVALID_ARGUMENT.

    Behavior change: timing is no longer reported. Identical runs of the same lookup measured from 13 to 91 ms, too noisy to compare. Code that passes the old options keeps compiling and running, but gets no timing:

    • include_timing and the HoverTiming type, HoverResult.timing, and hoverTimingSchema are deprecated and have no effect. timing is gone from the contract's hover result schema and from the MCP output schema.
    • --timing and -t are still accepted and print a deprecation warning on stderr (none with --json, which keeps stderr empty).
    • The MCP server no longer reads PRINFER_INCLUDE_TIMING.

    To migrate, replace include_timing/--timing with include_cost/--cost, and replace assertions on timing.resolution_ms with a budget on cost.instantiations.

  • 35ea165: New sort_unions option prints union members in a fixed order, the same on TypeScript 6 and TypeScript 7, which order members differently. A prinfer/testing snapshot written with inferredType(import.meta.url, { name: "x", sort_unions: true }) now holds on both backends. Every union in the type is sorted, at any depth: members by their printed text, with null and undefined last. Off by default; also available on the library's hover options and as --sort-unions on the CLI.

  • 421214f: prinfer/testing catches two easy mistakes in untyped test files. Options passed as a third argument, such as inferredType(file, { name }, { backend: "typescript7" }), used to be ignored without an error. Now they throw, with a message saying to move them into the selector.

    New strict selector option for inferredType, inferredTypeInfo, inferredTypeCost and inferredCompletions. With strict: true, an unknown selector key throws and names the closest valid key (includeDocs gets "Did you mean include_docs?"). Without it, unknown keys are ignored as before.

  • 6b0097b: prinfer/testing: the TypeScript 7 helpers are now synchronous. inferredType and inferredTypeInfo with backend: "typescript7", and inferredCompletions, return a plain value instead of a promise. Before, a missing await under bun test made toMatchInlineSnapshot() record Promise {}, and the test passed. Now the snapshot records the type.

    The TypeScript 7 compiler runs in a worker thread, and each call blocks until it answers. The worker and its compiler don't keep the process alive. If @typescript/native fails to load, the call throws a TYPESCRIPT_ERROR with the loader's reason right away instead of waiting for the timeout. The CLI, the MCP server and the library still use the async client.

    New timeout selector option (milliseconds, default 60000). A test runner's own timeout can't interrupt a blocked call. A TypeScript 7 call that gets no answer in time throws a TYPESCRIPT_ERROR and stops that compiler. The next call starts a new one, so a hung compiler fails one test instead of the whole run.

    closeTestingSessions() still returns a promise, settles on Node and Bun, and is still optional.

    This changes how TypeScript 7 results behave in tests that relied on them being promises:

    • expect(await inferredType(...)) keeps working, since awaiting a plain value returns it. The await is now optional.
    • .resolves no longer applies: await expect(inferredType(file, { name, backend: "typescript7" })).resolves.toBe(...) fails because the value is not a promise. Write expect(inferredType(file, { name, backend: "typescript7" })).toBe(...).
    • Errors are now thrown synchronously instead of rejecting, so .rejects and .catch() no longer see them. Use expect(() => inferredType(...)).toThrow(...) or try/catch.
    • The TypeScript types follow: inferredType returns string, inferredTypeInfo returns HoverResult and inferredCompletions returns string[] on both backends.

Patch Changes

  • 1cb5b50: prinfer --version prints the package version. It used to fail with "Unknown option --version".

  • 6ca7ab0: Signatures print the same on every backend in two more places:

    • An optional property of an object type inside an array, tuple, type argument, union, intersection, or index or construct signature drops the | undefined its ? implies on TypeScript 6 and the TypeScript 7 API too: (rows: { z?: string; }[]): void, as tsc and the TypeScript 7 language server print it. They used to print z?: string | undefined there. The language server backend also writes Array<T> and ReadonlyArray<T> as T[] and readonly T[], and the TypeScript 7 API no longer prints tuples as [ a, b ].
    • The TypeScript 7 language server backend keeps the const, in, and out modifiers of a type alias's, class's, or interface's type parameters: Holder<const T extends 1 | 2>. It used to print Holder<T extends 1 | 2>.
  • 85fb65c: prinfer/testing: on Node, a script that ran a TypeScript 7 lookup and then called await closeTestingSessions() at top level now exits normally. Before this fix the await never settled and Node exited with code 13 ("unsettled top-level await"). prinfer unrefs the idle compiler process so it can't keep your process alive, and that stayed in effect while closeTestingSessions() waited for the compiler to answer, so Node's event loop emptied out before the reply came. The compiler is now held only while it shuts down. Idle sessions still don't keep the process alive, and calling closeTestingSessions() is still optional.

  • 393b361: prinfer/testing: if a TypeScript 7 compiler process exits mid-run, prinfer now starts a new one. bun test kills every child process when any test times out, and that includes the shared compiler. Before this fix, every later TypeScript 7 call in the run hung or failed with "snapshot 1 not found", and closeTestingSessions() hung in afterAll. A call that was in flight when the process exited, or that fails while the process shuts down, now retries once with a fresh compiler. Teardown no longer waits on a dead process.

  • e88ef58: TypeScript 7 testing API: a name in a type position (Holder in let h: Holder<1>, ns.T, an imported type, an enum, a type parameter, interface A extends B<1>) printed any; it now prints the declared type, as on TypeScript 6 (Holder<T>). The language-server backend's unionMembers for such a name (let p: Pair) now matches too.

prinfer 3.1.0

Choose a tag to compare

@clockblocker clockblocker released this 09 Oct 06:22

3.1.0 is the first 3.x release on npm. It includes the breaking changes from 3.0.0 below.

3.1.0

Minor Changes

  • 962f411: Optional parameters and properties read the same on every backend, as tsc writes them in declaration emit and quick info: (value: number, digits?: number): string, not digits?: number | undefined. The TypeScript 7 language server already printed them this way; the TypeScript 6 backend (the library, the CLI, and prinfer/testing by default) and the prinfer/testing TypeScript 7 API now do too, in signature, returnType, and overloads. Update prinfer/testing snapshots that hold the old form.

    • A | undefined the source wrote stays: digits?: number | undefined is printed as written.
    • A | undefined the annotation does not account for stays too: y?: number | undefined for y?: T in a call with a number, and the properties of Partial<T> without exactOptionalPropertyTypes.
    • Object types inside a signature follow the same rule: (options?: { verbose?: boolean; }): void, (cb?: (x?: number) => void): void.
    • With exactOptionalPropertyTypes, hovering a property declared digits?: number reports number on every backend, as quick info does. TypeScript 6 and the TypeScript 7 API used to report number | undefined.
    • On the TypeScript 7 language server, overloads now match signature (they used to keep | undefined, so the hovered overload was listed again), and an optional method no longer reports unionMembers.
  • 02ade0b: An explicit project now works on the TypeScript 7 backend for any tsconfig that includes the file, not only the one the language server picks itself (the nearest tsconfig.json, or a project it references). An unreferenced tsconfig.test.json or tsconfig.app.json used to fail with INVALID_ARGUMENT; it is now opened through the TypeScript 7 API in the same warm session, so hovers, hover_by_name, batch_hover and diagnostics (MCP, prinfer check --backend typescript7) answer with that tsconfig's options. Hovers in such a project come from the checker, as in prinfer/testing, and have no display. Requests without project, or with the tsconfig the language server picks, are unchanged.

    A tsconfig that doesn't include the file still fails with INVALID_ARGUMENT; the message now says so and suggests the typescript6 backend, which adds the file to any project.

Patch Changes

  • f23c25a: prinfer/testing: TypeScript 7 sessions no longer depend silently on a private @typescript/native field to let the test process exit. If a future release moves the compiler process so prinfer can't unref it, prinfer prints a one-time warning to stderr naming closeTestingSessions() as the fix, then closes each session after 1s idle so the process still exits. The next call restarts the compiler.
  • 0bc9959: Add an MCPB manifest (mcpb/manifest.json) for listing the MCP server on Smithery. bun run mcpb packs it into prinfer.mcpb, a bundle that launches npx -y prinfer@<version> mcp. bun run version keeps the bundle's version and pinned package in sync. The npm package itself is unchanged.

3.0.0 (not published to npm separately; included in 3.1.0)

Major Changes

  • 62d47d8: prinfer 3.0 makes the MCP server easier for agents to find, install, and call correctly, gives every backend the same hover result, and adds single-file type checking and an annotations check.

    Breaking changes

    • prinfer/testing: inferredType and inferredTypeInfo return untruncated types by default, on both backends. A snapshot that held { ...; } or ... 12 more ... now holds the whole type, so changes deep inside a type fail the test. Update the snapshots, or pass full: false to keep the editor's shortened form.
    • signature is the type text alone, on one line, on every backend (TypeScript 6, the TypeScript 7 language server, and the prinfer/testing TypeScript 7 API). On the TypeScript 7 language server (the MCP default, and the CLI's --backend typescript7) it used to be the editor's hover text, such as const names: string[] with object types over several lines; that text is now the new display field. Two TypeScript 6 and prinfer/testing signatures change too: a variable initialized with a function reports its type, (x: number) => string, with kind: "const" instead of (x: number): string and kind: "function"; and type aliases keep their type parameters, type Box<T extends string = "a"> = { value: T; } instead of type Box = { value: T; }.
    • Hover text from the MCP hover tools and CLI type lookups is capped at 4000 characters per type, ending with a line that gives the total length and the union member count. Pass max_chars (MCP) or --max-chars (CLI) to raise the cap, 0 for none. Structured content and --json output are never cut.
    • The CLI rejects unknown options, and options a command doesn't take (such as --prefix on a type lookup), with exit code 1. Type lookups used to ignore them. Malformed targets print a short error with the accepted forms instead of the whole help text.
    • The deprecated hoverByName MCP tool is removed. Use hover_by_name.
    • MCP tools no longer accept include_timing. Set PRINFER_INCLUDE_TIMING=1 on the server process to add timing to every hover result. The library option and the CLI --timing flag are unchanged.
    • batch_hover reports a missing, unreadable, or directory file as a per-item FILE_NOT_FOUND error instead of failing the whole call, because items can now come from different files. file is optional when every item names its own.
    • Lookups by name report the position of the name token on both backends. On TypeScript 6 this used to be the start of the declaration (for example export).
    • Name lookups prefer declarations (functions, variables, types, calls, then parameters, members, and other declarations) and never match comments or strings. Both backends pick the same node, which can differ from what earlier versions returned for a repeated name.
    • The completions MCP tool and prinfer complete return at most 50 entries by default, and filter by the text already typed left of the cursor (an identifier's start, or a string literal's contents), as an editor does. Pass prefix (--prefix) to filter by other text or "" to turn filtering off, and limit (--limit, up to 500 on MCP) for more entries. The text output ends with … N more; pass prefix to narrow, or raise limit when entries were cut.
    • Completion entries are ranked by TypeScript's sortText with keywords after other entries of the same rank, in the library completions() too.
    • Hover kind uses the editor's labels on every backend. On TypeScript 6, variables are const, let, or var instead of variable, a variable initialized with a function is const (or let, var) rather than function, and a bare reference reports the kind of what it refers to (parameter, property, method, const, ...) instead of identifier. This affects the library, the CLI, prinfer/testing's inferredTypeInfo, and both MCP backends.
    • A hover line or column outside the file is an INVALID_ARGUMENT error that gives the valid range, on both backends and in the CLI, instead of SYMBOL_NOT_FOUND.
    • Error text starts with the code (Error [SYMBOL_NOT_FOUND]: …) and adds Did you mean: …? (or Nearby identifiers: …) and Suggestion: … lines, on MCP tools, batch_hover items, and CLI stderr. Many MCP clients show the model only text content, so the recovery hints used to be invisible there.
    • MCP outputSchemas are one compact envelope per tool (version, ok, and result or error) instead of a union, and list only the error fields an agent recovers with. Structured content is unchanged. Together with dropping zod's safe-integer bounds and $schema from input schemas, tools/list is about a fifth smaller.
    • On the TypeScript 7 backend, a project its language server would not use for the file (anything other than the nearest tsconfig.json or a project it references) fails with INVALID_ARGUMENT instead of silently using another tsconfig. Use the typescript6 backend for such projects.

    Features

    • hover and batch_hover items can target a token with text copied from the line (plus occurrence for the nth match) instead of a column. Whole-identifier matches count first, so "user" skips users. A miss quotes the line back so the agent can retry.
    • The completion result adds total (matches before the limit), truncated, and prefix (the filter applied); the library completions() accepts prefix and limit options. inferredCompletions in prinfer/testing still returns every name, unfiltered, so snapshots stay exhaustive.
    • CLI name targets accept any JavaScript identifier, such as prinfer 'src/store.ts:$store' or non-ASCII names.
    • CLI --json errors include project, like the MCP server's.
    • batch_hover accepts up to 100 items across any number of files, mixing {name, line?}, {line, text, occurrence?}, and {line, column} targets.
    • CLI text targets: prinfer src/a.ts:75:user hovers where user starts on line 75, matching whole identifiers first like the hover tool's text. prinfer src/a.ts:75 --text user --occurrence 2 does the same for text that is all digits or needs the nth match. prinfer complete takes the same targets and puts the cursor right after the text, so src/a.ts:75:user. lists the members of user.
    • CLI value options accept --opt=value. Arguments the shell probably rewrote (an expanded $name, or a zsh :r modifier in "$F:root") get a quoting hint.
    • Hover results add overloads (every call signature of an overloaded function), unionMembers (the size of a union type), and, for name lookups that matched one of several declarations, alternatives with the others' positions and kinds. The text shows (+1 overload) with the overloads listed,...
Read more

v1.0.0

Choose a tag to compare

@github-actions github-actions released this 01 Feb 11:17

Major Changes

  • 42376a6: Remove inferType and inferTypeFromOptions from public API. Use hover() instead for type lookups.

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 01 Feb 10:21

Minor Changes

  • 9f5cec0: Replace name-based lookup with position-based hover API

    BREAKING CHANGE: The API has changed from name-based to position-based lookup.

    • New hover(file, line, column, options?) function replacing name-based lookup
    • CLI syntax changed to prinfer file:line:column [--docs] [--project path]
    • MCP tool changed to hover with file, line, column parameters
    • Returns instantiated generic types at call sites
    • JSDoc/TSDoc extraction with include_docs option
    • Returns symbol kind and name in results

Patch Changes

  • 0397e29: Rename /check-type skill command to /hover for consistency with the API naming

v0.5.3

Choose a tag to compare

@github-actions github-actions released this 01 Feb 09:28

Patch Changes

  • fix: use claude mcp add instead of manual config file writing

v0.5.2

Choose a tag to compare

@github-actions github-actions released this 01 Feb 08:58

Patch Changes

  • 3b9dd90: Fix: use settings.json for Claude Code MCP configuration instead of claude_desktop_config.json

v0.5.1

Choose a tag to compare

@github-actions github-actions released this 01 Feb 08:42

Patch Changes

  • 7aeb91f: Fix postinstall script to skip when dist folder is not built yet