Repository navigation
Releases
typeprobe 4.0.0
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 deprecatedprinfer/vitest→typeprobe/vitest. - Commands:
prinfer→typeprobe,prinfer-mcp→typeprobe-mcp,npx -y prinfer mcp→npx -y typeprobe mcp. typeprobe has noprinferbins; prinfer 3.5.0 provides them. - MCP config: re-run setup for each client, e.g.
npx -y typeprobe setup claude(orcodex,cursor,vscode,gemini, with--scopeas before). The server is now namedtypeprobe, and setup replaces an oldprinferentry instead of adding a second one:claude mcp remove/codex mcp removedrop it, and JSON configs swap it fortypeprobein place, keeping its other keys such asenv(withPRINFER_*names renamed toTYPEPROBE_*).code --add-mcpcan't remove servers, so delete VS Code's user-scopeprinferserver by hand. Configs written by hand: rename theprinferkey and use"args": ["-y", "typeprobe", "mcp"].npx -y typeprobe setup agents-mdreplaces the old<!-- prinfer:start -->block. - Claude Code plugin:
/plugin uninstall prinfer@prinfer,/plugin marketplace remove prinfer, then/plugin marketplace add clockblocker/typeprobeand/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 fromtypeprobeandtypeprobe/testingfor the first time.PrinferErroris exported too, as a deprecated alias of the same class, soinstanceof PrinferErrorkeeps matching.instanceofholds whichever entry point (typeprobeortypeprobe/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(wasio.github.clockblocker/prinfer), Smitheryclockblocker/typeprobe, homepage https://clockblocker.github.io/typeprobe/.
- Install:
prinfer 3.4.0
Minor Changes
-
1704d78:
expectTypetakescostCompiler("bundled","project", or"auto"; defaultcompiler'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, orcounted and printed on ...when they are the same), and a passingexpectTyperesult names the printing one in its non-enumerablecompiler.inferredTypeCost, which only counts, throws oncostCompiler: itscompileralready picks the counting one. -
1704d78: New
expectTypesinprinfer/testingrunsexpectTypeon several targets of one file, with a budget for all of them together, and throws oneTypeExpectationErrorlisting every failed check: each entry's, with itsindex, 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, andcostCompilergo on the group;backend,full,sort_unions,readable, andtimeoutthere are defaults for every entry. -
1704d78:
compiler: "project"also finds a project's@typescript/native(an alias oftypescript7, the name prinfer itself installs it under) for the TypeScript 7 backend. The nearest install wins, as before; side by side,typescript7 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
typescriptor@typescript/nativeinto 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
Minor Changes
-
f1c359b:
inferredTypeCostcounts several targets of one file in one call:{ names: [...] }returns costs by name,{ targets: [...] }costs in order. Onlyproject,compiler, andstrictgo 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
expectTypeinprinfer/testingchecks a target's printed type, cost budget, and readability in one call, and throws aTypeExpectationErrorlisting 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 carriesactualandexpected, 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 checksprinted,maxInstantiations,maxTypes, andreadable. Costs are counted on TypeScript 6 even withbackend: "typescript7", which only picks where the text comes from. -
5df4ec2: Print and count with the project's own TypeScript. The new
compileroption 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 andprinfer/testingselectors, with--compileron the CLI, or withPRINFER_COMPILERon every surface, including the MCP server."project"resolvestypescript5.0 to 6.x for the TypeScript 6 backend, andtypescript7 or@typescript/native-preview7.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 aTYPESCRIPT_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 andprinfer/testingresults it is a non-enumerable property, so snapshots,toEqual, andJSON.stringifyoutput don't change. In the CLI's--jsonoutput and the MCP tools' structured content it is a field ofresult. 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, andexpectType'sreadablecheck. The default rules flag:- unresolved utility types (
Omit<User, "id">,Pick,Partial, and the rest ofDEFAULT_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 notstring & {}); - 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 takeutilityTypes(your own names, orfalse),objectIntersections,truncation, andallowfor fragments that are fine as printed. - unresolved utility types (
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/nativeis loaded only when a TypeScript 7 call needs it.
prinfer 3.2.0
Minor Changes
-
04a14ac: Type costs replace type-resolution timing.
include_cost(library, MCP hover tools,prinfer/testing),--cost(CLI), and the newinferredTypeCosthelper reportcost: { 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_costand nobackendruns on TypeScript 6. An explicittypescript7backend,--cost --backend typescript7, andinferredTypeCost/include_costwithbackend: "typescript7"fail withINVALID_ARGUMENT.Behavior change:
timingis 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_timingand theHoverTimingtype,HoverResult.timing, andhoverTimingSchemaare deprecated and have no effect.timingis gone from the contract's hover result schema and from the MCP output schema.--timingand-tare 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/--timingwithinclude_cost/--cost, and replace assertions ontiming.resolution_mswith a budget oncost.instantiations. -
35ea165: New
sort_unionsoption prints union members in a fixed order, the same on TypeScript 6 and TypeScript 7, which order members differently. Aprinfer/testingsnapshot written withinferredType(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, withnullandundefinedlast. Off by default; also available on the library's hover options and as--sort-unionson the CLI. -
421214f:
prinfer/testingcatches two easy mistakes in untyped test files. Options passed as a third argument, such asinferredType(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
strictselector option forinferredType,inferredTypeInfo,inferredTypeCostandinferredCompletions. Withstrict: true, an unknown selector key throws and names the closest valid key (includeDocsgets "Did you mean include_docs?"). Without it, unknown keys are ignored as before. -
6b0097b:
prinfer/testing: the TypeScript 7 helpers are now synchronous.inferredTypeandinferredTypeInfowithbackend: "typescript7", andinferredCompletions, return a plain value instead of a promise. Before, a missingawaitunderbun testmadetoMatchInlineSnapshot()recordPromise {}, 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/nativefails to load, the call throws aTYPESCRIPT_ERRORwith 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
timeoutselector 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 aTYPESCRIPT_ERRORand 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. Theawaitis now optional..resolvesno longer applies:await expect(inferredType(file, { name, backend: "typescript7" })).resolves.toBe(...)fails because the value is not a promise. Writeexpect(inferredType(file, { name, backend: "typescript7" })).toBe(...).- Errors are now thrown synchronously instead of rejecting, so
.rejectsand.catch()no longer see them. Useexpect(() => inferredType(...)).toThrow(...)ortry/catch. - The TypeScript types follow:
inferredTypereturnsstring,inferredTypeInforeturnsHoverResultandinferredCompletionsreturnsstring[]on both backends.
Patch Changes
-
1cb5b50:
prinfer --versionprints 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
| undefinedits?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 printz?: string | undefinedthere. The language server backend also writesArray<T>andReadonlyArray<T>asT[]andreadonly T[], and the TypeScript 7 API no longer prints tuples as[ a, b ]. - The TypeScript 7 language server backend keeps the
const,in, andoutmodifiers of a type alias's, class's, or interface's type parameters:Holder<const T extends 1 | 2>. It used to printHolder<T extends 1 | 2>.
- An optional property of an object type inside an array, tuple, type argument, union, intersection, or index or construct signature drops the
-
85fb65c:
prinfer/testing: on Node, a script that ran a TypeScript 7 lookup and then calledawait 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 whilecloseTestingSessions()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 callingcloseTestingSessions()is still optional. -
393b361:
prinfer/testing: if a TypeScript 7 compiler process exits mid-run, prinfer now starts a new one.bun testkills 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", andcloseTestingSessions()hung inafterAll. 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 (
Holderinlet h: Holder<1>,ns.T, an imported type, an enum, a type parameter,interface A extends B<1>) printedany; it now prints the declared type, as on TypeScript 6 (Holder<T>). The language-server backend'sunionMembersfor such a name (let p: Pair) now matches too.
prinfer 3.1.0
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
tscwrites them in declaration emit and quick info:(value: number, digits?: number): string, notdigits?: number | undefined. The TypeScript 7 language server already printed them this way; the TypeScript 6 backend (the library, the CLI, andprinfer/testingby default) and theprinfer/testingTypeScript 7 API now do too, insignature,returnType, andoverloads. Updateprinfer/testingsnapshots that hold the old form.- A
| undefinedthe source wrote stays:digits?: number | undefinedis printed as written. - A
| undefinedthe annotation does not account for stays too:y?: number | undefinedfory?: Tin a call with a number, and the properties ofPartial<T>withoutexactOptionalPropertyTypes. - Object types inside a signature follow the same rule:
(options?: { verbose?: boolean; }): void,(cb?: (x?: number) => void): void. - With
exactOptionalPropertyTypes, hovering a property declareddigits?: numberreportsnumberon every backend, as quick info does. TypeScript 6 and the TypeScript 7 API used to reportnumber | undefined. - On the TypeScript 7 language server,
overloadsnow matchsignature(they used to keep| undefined, so the hovered overload was listed again), and an optional method no longer reportsunionMembers.
- A
-
02ade0b: An explicit
projectnow works on the TypeScript 7 backend for any tsconfig that includes the file, not only the one the language server picks itself (the nearesttsconfig.json, or a project it references). An unreferencedtsconfig.test.jsonortsconfig.app.jsonused to fail withINVALID_ARGUMENT; it is now opened through the TypeScript 7 API in the same warm session, so hovers,hover_by_name,batch_hoveranddiagnostics(MCP,prinfer check --backend typescript7) answer with that tsconfig's options. Hovers in such a project come from the checker, as inprinfer/testing, and have nodisplay. Requests withoutproject, 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 thetypescript6backend, which adds the file to any project.
Patch Changes
- f23c25a:
prinfer/testing: TypeScript 7 sessions no longer depend silently on a private@typescript/nativefield 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 namingcloseTestingSessions()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 mcpbpacks it intoprinfer.mcpb, a bundle that launchesnpx -y prinfer@<version> mcp.bun run versionkeeps 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:inferredTypeandinferredTypeInforeturn 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 passfull: falseto keep the editor's shortened form.signatureis the type text alone, on one line, on every backend (TypeScript 6, the TypeScript 7 language server, and theprinfer/testingTypeScript 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 asconst names: string[]with object types over several lines; that text is now the newdisplayfield. Two TypeScript 6 andprinfer/testingsignatures change too: a variable initialized with a function reports its type,(x: number) => string, withkind: "const"instead of(x: number): stringandkind: "function"; and type aliases keep their type parameters,type Box<T extends string = "a"> = { value: T; }instead oftype 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,0for none. Structured content and--jsonoutput are never cut. - The CLI rejects unknown options, and options a command doesn't take (such as
--prefixon 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
hoverByNameMCP tool is removed. Usehover_by_name. - MCP tools no longer accept
include_timing. SetPRINFER_INCLUDE_TIMING=1on the server process to add timing to every hover result. The library option and the CLI--timingflag are unchanged. batch_hoverreports a missing, unreadable, or directoryfileas a per-itemFILE_NOT_FOUNDerror instead of failing the whole call, because items can now come from different files.fileis 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
completionsMCP tool andprinfer completereturn 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. Passprefix(--prefix) to filter by other text or""to turn filtering off, andlimit(--limit, up to 500 on MCP) for more entries. The text output ends with… N more; pass prefix to narrow, or raise limitwhen 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
kinduses the editor's labels on every backend. On TypeScript 6, variables areconst,let, orvarinstead ofvariable, a variable initialized with a function isconst(orlet,var) rather thanfunction, and a bare reference reports the kind of what it refers to (parameter,property,method,const, ...) instead ofidentifier. This affects the library, the CLI,prinfer/testing'sinferredTypeInfo, and both MCP backends. - A hover line or column outside the file is an
INVALID_ARGUMENTerror that gives the valid range, on both backends and in the CLI, instead ofSYMBOL_NOT_FOUND. - Error text starts with the code (
Error [SYMBOL_NOT_FOUND]: …) and addsDid you mean: …?(orNearby identifiers: …) andSuggestion: …lines, on MCP tools,batch_hoveritems, 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, andresultorerror) 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$schemafrom input schemas,tools/listis about a fifth smaller. - On the TypeScript 7 backend, a
projectits language server would not use for the file (anything other than the nearesttsconfig.jsonor a project it references) fails withINVALID_ARGUMENTinstead of silently using another tsconfig. Use thetypescript6backend for such projects.
Features
hoverandbatch_hoveritems can target a token withtextcopied from the line (plusoccurrencefor the nth match) instead of a column. Whole-identifier matches count first, so"user"skipsusers. A miss quotes the line back so the agent can retry.- The completion result adds
total(matches before the limit),truncated, andprefix(the filter applied); the librarycompletions()acceptsprefixandlimitoptions.inferredCompletionsinprinfer/testingstill 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
--jsonerrors includeproject, like the MCP server's. batch_hoveraccepts 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:userhovers whereuserstarts on line 75, matching whole identifiers first like thehovertool'stext.prinfer src/a.ts:75 --text user --occurrence 2does the same for text that is all digits or needs the nth match.prinfer completetakes the same targets and puts the cursor right after the text, sosrc/a.ts:75:user.lists the members ofuser. - CLI value options accept
--opt=value. Arguments the shell probably rewrote (an expanded$name, or a zsh:rmodifier 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,alternativeswith the others' positions and kinds. The text shows(+1 overload)with the overloads listed,...
v1.0.0
Major Changes
- 42376a6: Remove
inferTypeandinferTypeFromOptionsfrom public API. Usehover()instead for type lookups.
v0.6.0
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
hoverwith file, line, column parameters - Returns instantiated generic types at call sites
- JSDoc/TSDoc extraction with
include_docsoption - Returns symbol kind and name in results
- New
Patch Changes
- 0397e29: Rename
/check-typeskill command to/hoverfor consistency with the API naming
v0.5.3
Patch Changes
- fix: use
claude mcp addinstead of manual config file writing
v0.5.2
Patch Changes
- 3b9dd90: Fix: use settings.json for Claude Code MCP configuration instead of claude_desktop_config.json
v0.5.1
Patch Changes
- 7aeb91f: Fix postinstall script to skip when dist folder is not built yet