Repository navigation
ILens 10
- The CLAUDE.md block the README of ILens 9 and earlier asked for now names tools that no longer exist and can be deleted: Claude Code needs no block, and a client that does not read server instructions takes the README's new paragraph, which names no tool
- ILens now tells the client when it connects what it is for, which directories it can read and which tool answers what, so Claude Code knows when and how to use it without a block in the project's CLAUDE.md
find_typesnow finds the types an assembly defines bynamespace,name_contains,accessibilityandinclude_compiler_generated, and replaceslist_typesandsearch_typeslist_membersnow opens with the type's declaration, its base types and the number of members, such as7 members of public class CallShapes.Settings:, and replacessummarize_typedecompilenow decompiles a type or one of its members, and replacesdecompile_type,decompile_method,decompile_propertyanddecompile_eventcomparenow compares two assemblies from the types that differ down to one method's bodies, and replaceslist_changed_types,compare_typeandcompare_methodanalyzenow lists every call to a method with the arguments it passes, with kindcall_sites, resolving named, reordered, omitted andparamsarguments;parameter_namenarrows each line to one parameter, andnon_default_onlykeeps only the calls that pass something other than its default- Every tool parameter is now snake_case, such as
type_name,member_name,parameter_typesandassembly_a find_methodsnow takesnamespace,type_name,type_name_contains,return_typeandname_containsin place ofdeclaringNamespace,declaringType,declaringTypePattern,returnsandnamePatternlist_membersnow takesname_containsin place ofnamePattern,find_typestakesname_containsandnamespacewheresearch_typestookpatternandlist_typestooknamespaceName, andcomparetakesnamespacewherelist_changed_typestooknamespaceFilterfind_typesandcomparetakeinclude_compiler_generated, off by default, wherelist_types,search_typesandlist_changed_typestookexcludeCompilerGenerated- Enum values are now snake_case, such as
used_by,public_protectedandfield, and error messages name them that way; the PascalCase values are still accepted - Every tool that lists now takes
limit, lists up to 100 results unlesslimitsays otherwise, and ends a cut list with... (N more; raise limit to see all) - A parameter that several tools take is now described alike in each of them, and every tool that takes
parameter_typesdescribes the type patterns it accepts find_methods,list_membersandanalyzeno longer list the method Burst adds to start its wrapper,Initialize$...$BurstDirectCall, unlessinclude_compiler_generatedis true- Generic types now print with their type parameters, in C#'s unbound form such as
System.Func<,>orDictionary<,>.Enumerator, soTaskandTask<TResult>no longer print alike; a constructed type prints with its arguments - A type name without type parameters now means the non-generic type when one exists, otherwise the only type of that name; a name several generic types share, such as
System.Func, is an error listing them instead of an answer for whichever came first - Type names accept
Func<,>,Func`2andFunc<int, bool>, so every type can be named, including one nested in a generic type - Tools that take a type name or a type pattern now accept a nested type written the reflection way,
Outer+Inner, as well asOuter.Inner - Type patterns now compare argument lists:
List<int>matches onlyList<int>,List<>andList`1match anyList<T>, and a bareListstays as loose as before; this also works for types from referenced assemblies, fully qualified or not - A type nested in a generic type now prints with the types enclosing it,
Dictionary<TKey, TValue>.Entryinstead ofEntry<TKey, TValue>, and a type pattern may name a type by the end of its full name, such asDictionary.Entry - Tuple types now print with their element types, as
ValueTuple<int, int>, also in assemblies that defineSystem.ValueTuplethemselves, such as System.Private.CoreLib, where they printed as a bareValueTuple; the patternValueTuple<int, int>now finds them - Function pointers now print as C# writes them, such as
delegate* unmanaged<int, void>, instead of a baredelegate*, and the patterndelegate*matches any function pointer - Pointers now print with the C# keyword,
void*instead ofVoid*, and type patterns take a trailing*for a pointer list_members,find_methodsandcomparenow writeref,out,inandref readonlyparameters andrefandref readonlyreturns as C# does, such aspublic ref T Item[int index] { get; }onSpan<T>, instead of the plain typedecompile's first line,analyzeand error messages now name a method with itsref,out,inandref readonlyparameters and returns, such asTryGetValue(TKey, out TValue) → boolanalyzenow writes a by-reference parameter of a method from an assembly it cannot resolve asref, as inSystem.Threading.Interlocked.Increment(ref int)list_membersandcomparenow print a volatile field asvolatileand an init-only setter asinit, and no output shows the compiler'smodreqmarkers forref readonly,in,volatileorinit- A parameter type pattern that starts with
ref,out,inorref readonlynow matches only a parameter passed by reference, so a signature copied from ILens's output selects its overload - Type patterns now find
nintandnuintbyIntPtr,UIntPtr,System.IntPtrandSystem.UIntPtras well as by the keywords, infind_methods,decompileandanalyze, instead of answering that nothing matches analyzenow printsSystem.IntPtrandSystem.UIntPtrin the signature of a member from another assembly asnintandnuint, as it does for the assembly's own members, andnintandnuintselect such an overload inparameter_types- A type written exactly as ILens prints it now always matches as a type pattern
decompile,compareandanalyzenow find a method a class implements explicitly for an interface by its plain name, such asEqualsforIEquatable<T>.Equals, when no ordinary method of that name matches; an ordinary method of the name still wins, as in C#decompileandanalyzenow find an explicitly implemented property or event by its plain name the same way, and its accessors byget_Xor by the full nameanalyzeprints, such asICollection<T>.get_IsReadOnly- A plain name that several explicit implementations share, such as
GetEnumeratorforIEnumerableandIEnumerable<T>, is now an error that names each in full - The "No overload" error now lists the explicit implementations of the name among the available overloads
decompile,compareandanalyzenow select the indexer thatparameter_typesorparameter_countnames onget_Itemandset_Item, instead of always the first oneget_Itemorset_Itemwithout a hint on a type with several indexers is now an error listing them, as for other overloads, andcall_siteswithout a hint covers them allanalyzeon a property name that several indexers share is now an error listing them and pointing toget_Itemandset_Itemwithparameter_types, instead of an answer for the first indexer alone;decompile's error points there the same wayanalyzenow names an indexer with its index types, such asMatrix3x2.Item[int, int], in its results, headers and errors, so two indexers of one type are two lines instead of one lineItem- Naming a type the assembly only references no longer answers "Type not found": every tool now says the type is not defined in this assembly and names the assembly it comes from, and
comparedoes the same for a type neither assembly defines analyzenow answers for a type from another assembly, from the inspected assembly's own references to it:used_by,instantiated_by,exposed_by,extension_methods,applied_toandimplemented_byon the type, andused_by,read_byandassigned_byon its members, with overloads selected byparameter_countorparameter_typesas usualanalyze'simplemented_byon an interface from another assembly adds a line saying that types deriving from another assembly's class may implement it without the inspected assembly showing itanalyzeon a type nothing in the assembly names returns an explicit empty answer saying so instead of an error, and a member name nothing references lists the members of that type the assembly does referenceanalyzerefusesuses,implements, andoverridden_byorimplemented_byon a member of a type from another assembly, and says why: the answer is in the other assembly; it answerscall_sitesthere with an error that points toused_bydecompile,analyzeandcomparenow answer a member a class inherits from a base class in another assembly with an error naming the member and the file its code is in, instead of saying that neither the class nor its base types declare itdecompile,analyzeandcomparenow say, when a name is not found, which base class went unsearched because ILens could not load its assembly, and an unmatched overload hint also lists the overloads of base classes in other assembliesdecompileandanalyzenow find an extension method written for a base class from another assembly when it is asked for on a derived classfind_typeslists public and protected types unlessaccessibilitysays otherwise, sorted by full name and up tolimit, and with no filter every typefind_typesanswers a namespace that no type lies in with an error instead of an empty listfind_methodsnow opens with the number of methods, answersNo methodswhen nothing matches, and no longer indents its linesfind_methodsnow writes each method as the other tools name it, without parameter names, such asstatic CallShapes.Target.Record(string, int) → void, and sorts the list by that namefind_methodsnow lists constructors, as.ctorfind_methodsnow hides what the compiler generated, such as a local function, unlessinclude_compiler_generatedis true, and answers a namespace no type lies in with an errorfind_methodswithtype_namenow lists only that type's methods, not those of every type sharing its namelist_membersnow lists the constructors first among the methods, under the type's name, such aspublic Settings(int value)list_membersnow lists nested types in a group of their own, whichkindsselects asnested_typelist_membersnow marks an obsolete member with[Obsolete]list_membersnow hides what the compiler generated, such as an auto-property's backing field, unlessinclude_compiler_generatedis truelist_membersnow sorts each group by name, heads it with its count, such as3 properties:, and answersNo members ofand the type's declaration when nothing is leftlist_membersnow lists every indexer of a type, each with its indices, such asItem[int row]andItem[int row, int column]onMatrix3x2, instead of only the first one as a plain propertyItemlist_memberswithinclude_inheritednow shows a method overridden from a generic base class once, as the type's own, instead of also listing the base class's declaration of itlist_memberswithinclude_inheritednow lists what a class inherits from base classes in other assemblies, such as Unity'sMonoBehaviour, wherever ILens finds those assemblies: beside the inspected one, at the top of an allowed folder, or among the installed .NET librarieslist_memberswithinclude_inheritednow lists the members of core library base classes such asSystem.Exception,System.Attribute,System.ValueTypeandSystem.Enum, instead of nonelist_memberswithinclude_inheritednow ends with aNot shown:line naming a base class whose assembly ILens could not load, instead of stopping there without a signdecompilenow decompiles a field too, with the initializer the compiler moved into the constructors and only the using declarations the field needsdecompilereaches a constructor as.ctorand an accessor by its IL name, such asget_Countdecompilenow opens a property's or event's code with the name of the member it found, without(property)or(event): an indexer with its index types, such asSystem.String.Chars[int], and an explicit implementation by its full namedecompileon a property or event accessor now shows its keyword with its modifiers, such asget,readonly get,set,init,addorremove, instead of only its attributes and bodydecompileanswersparameter_countorparameter_typeswithoutmember_namewith an error- The error for a name several kinds of member share no longer points to a kind-specific tool, and says that the type's decompiled code shows them all
decompileandanalyzeno longer take an extension method written forTask<TResult>as one forTaskanalyzenow opens with a count in the words of the kind asked, such as3 readers of ExplicitMembers.Circle.Size [declared]:, and an empty answer readsNo users of X, followed by the reason where ILens knows oneanalyzenow sorts its results by name and hides what the compiler generated, such as an iterator's state machine, unlessinclude_compiler_generatedis trueanalyzenow reports a use made inside a lambda, local function, iterator or async method under the member it is written in, instead of under a compiler-generated name, forused_by,read_by,assigned_byandinstantiated_byanalyze'susesnow includes what a method's lambdas and local functions use, and the whole body of an iterator or async methodanalyzenow answersparameter_countorparameter_typeswithoutmember_namewith an error instead of ignoring them, and does the same forparameter_nameornon_default_onlywith a kind other thancall_sitesanalyze'simplemented_byno longer lists an interface among its own implementers, nor the implementers of other interfaces sharing its name, such asIAstVisitor<T>forIAstVisitoranalyze'sapplied_tonow finds attributes the compiler stores as flags, such as[Serializable]analyze'sused_byon an overriding member now lists only what refers to the override itself, the same at every depth of overriding- Every
analyzeanswer about the callers or users of an override now ends with a line naming the method code actually calls analyze'sused_by,read_by,assigned_byandcall_sitesnow say, for a member that implements an interface member explicitly, that code reaches it only through that interface member and name it, instead of an answer that read as proof the member is unusedassigned_byon a field no longer lists a method that only reads the field through its address, such as one that turns a number or an enum into text, calls a struct method that leaves it unchanged, or passes it to arefparameter that is only readread_byon a field no longer lists a method that only writes the field through its address, such as one that sets it tonullordefault, writes one of its members, or passes it asoutread_byandassigned_byon a field now mark a method that may read or write it through code they cannot see, such as a call into another assembly, with what received the field's address, as in(may write: calls UnityEngine.Rect.set_x(float) on it)read_byandassigned_byfollow the same rules on a field of a type from another assemblyread_byandassigned_byon a field now answer about five times faster on a large assemblycall_sitesreports a call the decompiled C# does not show as a call, rather than leaving it out, so an empty answer means no such call existscall_sitesshows the calls to a property, indexer or event accessor that code makes by reading, assigning, incrementing or subscribing, such asgauge.Level += 3orthis[row, column], with the indices and the assigned value as arguments, and passes the value a compound assignment or an increment computes as the setter's value argument, such asgauge.Level + 3call_siteswrites a call to an extension method as a static call with the receiver first, such asStamps.Stamp(box, "stamped", 2)call_siteson a virtual method shows the calls made on an object whose class overrides it, such asdog.Die(3)forCreature.Die, and on an overriding method, such asAnimal.Die, the calls made on an object of its class or a class deriving from itcomparenow marks a changed type as it marks a changed member, with+for what only B has,-for what only A has and~for a change, sorts both lists by name and lists up tolimitcomparetakesformatascsharporil, and answers a parameter that does not apply at the level asked with an error instead of ignoring itcompareanswers a property or event name with the names of its accessors to compare instead, and a namespace neither assembly has with an errorcompare's headers now name the type found, as ILens prints it, instead of repeating the caller's spellingcomparenow reports a type whose base class or interfaces moved to a same-named type of another arity or changed their type arguments, as a metadata changecomparenow reports a type whose only change is inside a property or event accessor, and names the changed accessor on the member's line, such as~ public int Level { get; set; } [get_Level]compareno longer reports a method as changed when only the numbers the compiler gives the code it generates have shifted, as they do for every method after one that was added: those of lambdas, local functions, iterators, async methods, closure classes, cached delegates and method groups, base calls from generated code, anonymous types and delegates, and Burst wrapperscomparenow reports a change inside a lambda, a local function, an iterator or an async method as a change of the method it is written in, including the changes it used to miss when the compiler moved that code into a class of its owncomparenow compares a method Burst compiles together with the copy of its code that Burst keeps,X$BurstManaged, and its C# drill-down shows that copycompare's member list no longer lists compiler-generated members, Burst's included, and now acceptsinclude_compiler_generatedto list them under their own namescomparenow prints an added or removed indexer with its indices, instead of as a plain propertyItemcomparenow writes a constructor under its type's name, such aspublic Foo(int x), instead ofpublic void .ctor(int x)comparenow shows an accessor's keyword with its modifiers, asdecompiledoes, when it prints an accessor's C#comparewithmember_namenow says when two bodies decompile alike while their IL differs, and answers such bodies, and identical ones, with the verdict line alone instead of a copy of the codecomparewithformatilnow shows a method's IL together with that of the code generated for itcomparewithmember_namenow shows the code of a method only one of the two builds has, under a header saying which build has it, such as "only B has it", instead of answering that the method does not existcomparewithmember_namenow answers a type neither build defines with "not found in either assembly", as it does withoutmember_namefind_harmony_dependenciesnow answers in text instead of JSON, one line per dependency sorted by target, such asPrefix HarmonyMod.TargetA.Compute(string) <- HarmonyMod.OverloadPinnedPatch.Prefix [TypedAttribute], and takeslimitfind_harmony_dependenciesnow writes types as the other tools do, such asint,List<int>andOuter.Inner, and names a type the patch code writes short by its full name when only one type has that namefind_harmony_dependenciesnow lists a patch method marked[HarmonyPrefix],[HarmonyPostfix],[HarmonyTranspiler]or[HarmonyFinalizer]with its patch type whatever its name, instead of as(none)with only the patched classfind_harmony_dependenciesnow takes each patch method's target from its own[HarmonyPatch]laid over the class's, so a class patching several methods or overloads lists each patched methodfind_harmony_dependenciesnow lists reverse patches, and findsTargetMethodandTargetMethodsmethods by their[HarmonyTargetMethod]and[HarmonyTargetMethods]attributes as well as by their namesfind_harmony_dependenciesnow also covers a class that names its targets only on its patch methods, such as one carrying only[HarmonyPatchCategory]find_harmony_dependenciesnow names a getter or setter patch's target by its accessor, such asget_Prop, and classifies itTypedAttributewhen the accessor is publicfind_harmony_dependenciesnow lists a getter or setter patch that names no property, which Harmony binds to an indexer, with the indexer's accessor, such asget_Item(int)orset_Item(string, int), instead of the class with its index types; an indexer of a class in another assembly carries C#'s default name,Item, and a setter's value type is written?find_harmony_dependenciesnow lists a Harmony 2.4 patch on an event's add or remove accessor, an operator or a finalizer with the method it patches, such asadd_Changed,op_AdditionorFinalize, instead of a method type printed asUnknown(8)or a similar number, and marks one on a public event's accessorTypedAttribute, as it marks one on a public property'sfind_harmony_dependenciesnow lists a constructor patch as.ctoror.cctoreven when its attribute also names a method, which Harmony ignores therefind_harmony_dependenciesnow marks a patch on a constructor, finalizer, operator or indexerTypedAttributewherever the patched class lives, unless a string names the class, since such a patch needs no member namefind_harmony_dependenciesnow lists a patch whose[HarmonyPatch]names its target class by a string,[HarmonyPatch("Namespace.Class", "Method", MethodType.Normal)], with that class and method instead of?, markedStringTargetedwherever the class livesfind_harmony_dependencies' description now says that a target without parameter types names no overload, and thatTypedAttributeandStringTargetedare also told apart for a constructor, finalizer, operator or indexerfind_harmony_dependenciesnow lists the target of aTargetMethodthat looks it up by reflection withtypeof(X).GetMethod("Y"), instead of?find_harmony_dependenciesnow lists the method aTargetMethodorTargetMethodsfinds by searching a type's methods for its name, markedTargetMethodSearchorTargetMethodsSearchbecause the patch's own tests pick the overload, instead of?find_harmony_dependenciesnow lists the target of aTargetMethodorTargetMethodsthat looks it up withAccessTools.DeclaredMethod, a property getter or setter lookup, a constructor lookup, or Harmony's extension spelling such astypeof(X).Method("Y"), instead of?find_harmony_dependenciesnow lists the target of aTargetMethodwhose code makes a generic method of the method it looks up withMakeGenericMethod, instead of?, and reads the same in aHarmony.Patchcallfind_harmony_dependenciesnow reads a target that aSymbolExtensions.GetMethodInfolambda names, pinned to the overload the lambda callsfind_harmony_dependenciesnow also lists a target whose class a lookup names as text, as"Namespace.Type:Method"or throughAccessTools.TypeByName, or holds in a local, and one thatTargetMethodreturns from a field thatPrepareor an initializer assignsfind_harmony_dependenciesnow lists a lookup wrapped inAccessTools.EnumeratorMoveNextorAsyncMoveNextwith(Enumerator)or(Async)after the method, as for a[HarmonyPatch]with that method typefind_harmony_dependenciesnow adds a?line beside the targets it reads when aTargetMethodorTargetMethodsalso looks a target up in a way it cannot read, such as a search for a compiler-generated method, instead of leaving that target out without a sign; aTargetMethodwith such a line isDynamicTargetMethodfind_harmony_dependenciesnow writes a constructor patch or lookup without parameter types as.ctor(), the parameterless constructor Harmony binds, and a lookup given an empty parameter-type array with()find_harmony_dependenciesnow lists the patches a mod applies by callingHarmony.Patch, each with its target, its patch method and the kind the argument's position or name gives, labeled with the member that writes the values out, such asPrefix HarmonyMod.TargetK.Run <- HarmonyMod.ManualPatches.BeforeRun [PatchCall in HarmonyMod.ManualPatches.Apply]find_harmony_dependenciesnow prints?for a target or patch method of aHarmony.Patchcall it cannot read, and? ? <- ?for a call it cannot find in the decompiled code, instead of leaving them outfind_harmony_dependenciesnow gives a target for each"Type:Method"name in a list the mod writes out and loops over, and lists a target found by searching one class's methods for an exact name asPatchCallSearch, naming no overload, such asPostfix HarmonyMod.TargetA.Compute <- HarmonyMod.LoopPatches.AfterAny [PatchCallSearch in HarmonyMod.LoopPatches.Apply]find_harmony_dependenciesnow follows a target, a patch method, or a looked-up field's class and name through a helper's parameters to each call of the helper, through a patch object's fields to each place that builds it, and through the mod's own methods to what they returnfind_harmony_dependenciesnow reads a nested class found throughAccessTools.InnerorType.GetNestedType, a class named as text throughType.GetTypeorAssembly.GetType,GetType()on the calling object as the class declaring the calling member, and a property accessor taken from itsPropertyInfo, inTargetMethodbodies and field lookups as well as inHarmony.Patchcallsfind_harmony_dependenciesnow lists as aTargetMethod's targets only the values it returns or yields, so a method it looks up only to test a condition or to compute its targets from is no longer listed as a target, and a target computed at run time prints?find_harmony_dependenciesnow lists the fields an assembly looks up by reflection in any class, such as the static holders mods keep their field references in, instead of only in patch classesfind_harmony_dependenciesnow reads Harmony's field lookups in every form that names class and field:FieldandDeclaredFieldbytypeof, by"Type:field"text, throughAccessTools.TypeByNameor a local holding a class,FieldRefAccess,StaticFieldRefAccessandStructFieldRefAccessin all their overloads, and Harmony's extension spelling of eachfind_harmony_dependenciesnow lists fields looked up through .NET'sType.GetFieldfind_harmony_dependenciesnow lists the fields Harmony hands a prefix, postfix or finalizer through a___nameparameter, as fields of the class the patch targets, labeledInjected, for a patch applied by attribute as for one applied by aHarmony.Patchcallfind_harmony_dependenciesnow names the class of aTraversefield access from the type of the object it reads, given directly, held in a local or created from a class, also for the genericField<T>, and prints?instead of<runtime>where that type is unknownfind_harmony_dependenciesnow lists aTraversefield access whose receiver the decompiler writes with several casts, instead of leaving it outfind_harmony_dependenciesnow lists a field lookup it can read only in part with?for the class or the field, and an enumeration of a class's fields such asType.GetFieldswith?for the field, instead of leaving them outfind_harmony_dependenciesnow labels each field line with the call that found it:AccessToolsField,DeclaredField,FieldRefAccess,StaticFieldRefAccess,StructFieldRefAccess,TraverseField,GetField,GetFields,GetDeclaredFields,GetFieldNamesorInjectedfind_harmony_dependenciesnow answers faster on a mod with many patch classes, since it decompiles only the methods that look up a fielddecompile,analyze'scall_sites,compare's C# view andfind_harmony_dependenciesnow find an assembly's references at the top of each allowed directory, after the assembly's own folder, so the code of a mod whose game assemblies sit in another allowed directory shows enum names and properties instead of numbers and "Unknown result type" comments- A reference file that cannot be read no longer makes every tool fail on the assembly that references it; the reference is left unresolved
- A reference file that links to a location outside every allowed directory is no longer opened, beside the inspected assembly as elsewhere
- The memory budget (
--max-total-size, default 200 MB) now bounds ILens's actual memory rather than the sum of the inspected files' sizes: when a call ends with ILens above it, the assemblies used longest ago are closed until it is back under three quarters of the budget, so a session over hundreds of assemblies no longer grows to gigabytes - A tool call no longer fails or reads corrupted data when a concurrent call makes ILens unload the assembly it is working on to stay within the memory budget
- ILens now releases the referenced assemblies it opened beside an assembly as soon as it unloads that assembly, instead of keeping them locked
- ILens now runs on .NET 10.0.12 instead of 10.0.10, with the .NET security fixes of August and September 2026
- The user guide and README now describe the eight tools by their current names and parameters, and ask Claude Code users to paste nothing
- The user guide now describes
find_harmony_dependencies' patches fromHarmony.Patchcalls, its field lookups in every class with their labels, the lookups it reads and what each?stands for, and names Harmony with a link to its repository - The user guide now describes
call_siteswriting an extension call as a static call,analyze's(may read: ...)and(may write: ...)marks on a field and its answers about overrides,compare's answers for a method only one build has and for two bodies that print alike, andlist_members' inherited members from base classes in other assemblies - The user guide's security model now describes the memory budget as the process's own memory and names every place outside the allowed folders from which decompilation reads an assembly's references
- The token-cost table in the README and the user guide is dated 2026-10-09