Tesearis.DotNetAssemblyQuery 2.0.0

dotnet add package Tesearis.DotNetAssemblyQuery --version 2.0.0
                    
NuGet\Install-Package Tesearis.DotNetAssemblyQuery -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Tesearis.DotNetAssemblyQuery" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tesearis.DotNetAssemblyQuery" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Tesearis.DotNetAssemblyQuery" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Tesearis.DotNetAssemblyQuery --version 2.0.0
                    
#r "nuget: Tesearis.DotNetAssemblyQuery, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Tesearis.DotNetAssemblyQuery@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Tesearis.DotNetAssemblyQuery&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Tesearis.DotNetAssemblyQuery&version=2.0.0
                    
Install as a Cake Tool

Tesearis.DotNetAssemblyQuery

A tiny, fast CLI that answers "where is this symbol / what calls it" by reading compiled .NET assemblies and their PDBs: no build, no workspace, no decompiler.

Symbol lookup, hover info, go-to-definition and find-references over one or more compiled .NET assemblies (with portable PDBs), resolving straight back to real source file:line via debug sequence points. Built for AI coding agents and scripting: instant startup, plain-text output, zero project-system dependency.

Features

  • find-symbol <name> - list every type/method/field/property matching a name. Pass --contains to match names containing <name> (case-insensitive substring match) instead of requiring an exact match, for discovery when you only know part of a name.
  • hover <name> - print each match's signature; overloaded methods are listed with their distinguishing parameter list. A [DllImport] (P/Invoke) method's signature also shows the native module and entry point it targets, e.g. // P/Invoke: user32.dll!MessageBoxW.
  • go-to-definition <name> - resolve each match to a file:line via PDB sequence points. A type/field/enum with no sequence points of its own resolves approximately (its own file, no line) via a nearby method; if the type has no methods anywhere in its own declaring-type chain either (e.g. a top-level enum), it falls back further to any type sharing its namespace
    • in that rarer case even the file is a guess, flagged as IsFileApproximate in --json.
  • find-references <name> - find IL-level usages (calls, casts, typeof, field loads/stores) plus type-position usages (base types, interfaces, field/parameter/return types).
  • list-members <type> - list a type's methods, fields, properties, and nested types.
  • implementations <interface-or-base-type> - find every type that implements an interface or derives from a base type, transitively (via a base class, or via interface-extends-interface, both count). Only types declared in an indexed assembly are searchable, including the target itself: a framework/BCL type like IDisposable needs its defining assembly indexed (e.g. System.Private.CoreLib.dll), or pass --framework-path (repeatable) to resolve it on demand instead - bare, it auto-discovers the local machine's matching Microsoft.NETCore.App shared framework; given one or more values, each is a directory (scanned like --path) or an explicit file path, mixed freely (e.g. for a Unity Editor install with reference assemblies scattered across several directories). By default --framework-path assemblies are reference-only: they help resolve the target and let ancestry walks cross into a framework base type, but their own types aren't reported as implementers (avoids flooding results with every BCL type that implements a common interface). Pass --include-framework-results to include those too. A target that's indexed but has no implementers is reported separately from one that isn't indexed at all, instead of a misleading "no implementations found" for both cases.
  • list-assemblies - list the loaded assemblies (name, version, file path).
  • Assembly discovery via repeatable --path <path>, auto-detected per entry: a directory is scanned non-recursively for *.dll; anything else resolves as an exact file or glob pattern. Falls back to the current directory when no --path is given. A directory scan silently skips native (non-.NET) DLLs it finds alongside managed ones (native AOT shims, SQLite, SkiaSharp, etc.), reporting one collapsed skipped N native (non-.NET) DLL(s) found via --path directory scan warning (hidden by default, expected and not actionable; pass --verbose to see it) instead of failing on each. A DLL named explicitly is never filtered this way - if it isn't managed, its load failure is still reported for real.
  • Explicit --source-root <path> for relativizing resolved source paths (defaults to the current directory).
  • Match filters: --kind type|method|field|property restricts matches to that member kind (on list-members, it restricts which kind of member is listed instead); --namespace <ns> and --assembly-name <name> restrict matches to a containing namespace/assembly (exact match). All three are optional and combine with each other and the name match. implementations takes --namespace/--assembly-name (to disambiguate the interface/base type) but not --kind; list-assemblies takes neither.

Packages

This repo ships as three NuGet packages:

  • dotnet-assembly-query - the daq global/local dotnet tool (CLI). This is what most people want.
  • Tesearis.DotNetAssemblyQuery - the underlying library (symbol lookup, hover, go-to-definition, find-references), for tools that want to query assemblies in-process instead of shelling out to daq and parsing stdout - e.g. a future editor plugin or MCP server. Its query methods (AssemblyQuery, AssemblyLoading) return plain data (IMemberDefinitions, SourceLocations, etc.), not console output or exit codes.
  • Tesearis.DotNetAssemblyQuery.Daemon - the warm-cache background daemon behind the CLI, factored out so other front ends can host it against their own already-loaded Mono.Cecil modules without reimplementing the pipe/process subsystem.

Usage

daq <find-symbol|hover|go-to-definition|find-references|list-members> <name> \
  [--path <path>]... [--source-root <path>] \
  [--kind type|method|field|property] [--namespace <ns>] [--assembly-name <name>] \
  [--no-daemon] [--daemon-idle-timeout <seconds>] [--json] [--verbose]

daq find-symbol <name> --contains \
  [--path <path>]... [--source-root <path>] \
  [--kind type|method|field|property] [--namespace <ns>] [--assembly-name <name>] \
  [--no-daemon] [--daemon-idle-timeout <seconds>] [--json] [--verbose]

daq implementations <interface-or-base-type> \
  [--path <path>]... [--source-root <path>] [--namespace <ns>] \
  [--assembly-name <name>] [--framework-path [<path>...]] [--include-framework-results] \
  [--no-daemon] [--daemon-idle-timeout <seconds>] [--json] [--verbose]

daq list-assemblies \
  [--path <path>]... [--source-root <path>] [--no-daemon] \
  [--daemon-idle-timeout <seconds>] [--json] [--verbose]
daq find-symbol MyClass --path ./bin/Debug/net8.0
daq find-symbol Repo --contains --path ./bin/Debug/net8.0
daq hover DoWork --path ./bin/Debug/net8.0
daq go-to-definition MyClass --path ./bin/Debug/net8.0 --source-root .
daq find-references DoWork --path ./bin/Debug/net8.0 --source-root .
daq find-symbol Value --kind property --namespace MyApp.Models --path ./bin/Debug/net8.0
daq find-symbol MyClass --path ./bin/Debug/net8.0 --json
daq list-members MyClass --path ./bin/Debug/net8.0
daq implementations IMyInterface --path ./bin/Debug/net8.0
daq implementations IDisposable --path ./bin/Debug/net8.0 --framework-path
daq implementations IDisposable --path ./bin/Debug/net8.0 --framework-path --include-framework-results
daq implementations IUpdate --path ./Library/ScriptAssemblies --framework-path /path/to/custom/framework
daq implementations IUpdate --path ./Library/ScriptAssemblies \
  --framework-path /path/to/custom/framework \
  --framework-path /path/to/custom/module.dll
daq list-assemblies --path ./bin/Debug/net8.0

Pass --json to any command for machine-readable output: each command prints one JSON array (a single object for daemon start) to stdout instead of text, and exits 0 on a successful run even with zero matches (an empty array, not an error). Errors and warnings are unaffected by --json - they're always plain text on stderr with the usual exit codes, never JSON.

implementations --json is the other single-object exception: {"targetIndexed": bool, "hint": string|null, "implementations": [...]}. targetIndexed is false (with hint explaining why, and implementations empty) when the target type itself isn't indexed at all, as distinct from targetIndexed: true with an empty implementations array, which means the type was found but genuinely has no reported implementers.

Pass --verbose to any command to also show diagnostic warnings that are hidden by default, e.g. the native (non-.NET) DLL skip note described above.

Every flag also accepts --flag=value in addition to --flag value.

Exit codes: 0 on success (including a query that found nothing - that's a valid result, not an error), 2 on a command-line usage error (bad/unknown flag, missing value, unknown command), 1 on any other runtime failure (e.g. an assembly failed to load).

Background daemon

Repeated calls against the same, unchanged DLL set (e.g. an AI agent driving daq many times in a row) are slow mainly because every invocation reloads every DLL/PDB from scratch. To speed that up, daq transparently starts a background daemon the first time it's asked about a given DLL set: the first call runs exactly as before (no slower, no different output), and as a side effect spawns a daemon that keeps those modules loaded in memory. Every later call against the same DLL set is answered by that daemon instead of reloading anything - the daemon re-checks each file's size/timestamp before answering, so a rebuild is always picked up, never served stale. It shuts itself down after 30 minutes of inactivity (override with --daemon-idle-timeout <seconds> or the DAQ_DAEMON_IDLE_TIMEOUT_SECONDS env var).

daq daemon status [--json]                                  # list running daemons
daq daemon stop [--path <path>]... [--json]                 # stop one (or all, with no args)
daq daemon start --foreground --path ./bin/Debug/net8.0 [--json] [--verbose]   # run one in the foreground

Pass --no-daemon to any command to disable the mechanism entirely (no connect attempt, no spawn) - useful in CI or sandboxed environments.

Agent integration

Add an allowlist entry so an agent (e.g. Claude Code) can call this without a permission prompt each time, in .claude/settings.json:

{
  "permissions": {
    "allow": ["Bash(daq:*)"]
  }
}

For Claude Code specifically, this repo ships a daq skill as an installable plugin. From your project, run:

/plugin marketplace add marek-jelo-jelinek/dotnet-assembly-query
/plugin install daq@marek-jelo-jelinek

It documents preflight, the full command/flag cheatsheet, and the allowlist snippet above, so the agent picks up daq correctly without re-deriving usage from this README each time.

As a library

using Tesearis.DotNetAssemblyQuery;

var dllPaths = AssemblyLoading.DiscoverDllPaths(["./bin/Debug/net8.0"], out _);
var modules = AssemblyLoading.LoadModules(dllPaths, out _);
var allTypes = modules.SelectMany(m => m.GetTypes()).ToList();

foreach (var site in AssemblyQuery.FindReferences(modules, allTypes, "DoWork", sourceRoot: "."))
{
    Console.WriteLine($"{site.Site.FullName} ({site.Kind}) -> {site.Location}");
}

Building & testing

dotnet build Tesearis.DotNetAssemblyQuery.slnx
dotnet test Tesearis.DotNetAssemblyQuery.slnx

License

MIT - see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Tesearis.DotNetAssemblyQuery:

Package Downloads
Tesearis.DotNetAssemblyQuery.Daemon

The warm-cache background daemon behind the `dotnet-assembly-query` CLI, factored out so other front ends can host it against their own already-loaded Mono.Cecil modules without reimplementing the pipe/process subsystem.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 121 9/6/2026
1.0.0 120 9/4/2026