dotnet-assembly-query-unity 2.0.0

dotnet tool install --global dotnet-assembly-query-unity --version 2.0.0
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local dotnet-assembly-query-unity --version 2.0.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=dotnet-assembly-query-unity&version=2.0.0
                    
nuke :add-package dotnet-assembly-query-unity --version 2.0.0
                    

Tesearis.DotNetAssemblyQuery.Unity

Unity conventions for daq: finds your repo's Library/ScriptAssemblies so agents/editors can query it with zero config.

A thin wrapper around daq that adds the one thing Unity projects need: finding the Unity project root (the folder containing Library/ScriptAssemblies) and pointing daq at it. Everything else (the actual query commands, and why reading compiled assemblies works better than a Roslyn workspace or decompiler for Unity) is daq's job; see its README.

Usage

Run from anywhere inside a Unity project (that has been built at least once, so Library/ScriptAssemblies exists):

daq-unity find-symbol <name> \
  [--path <path>]... [--kind type|method|field|property] [--namespace <ns>] \
  [--assembly-name <name>] [--contains] [--assets-only] [--project <path>] [--no-plugin-scan] \
  [--no-daemon] [--daemon-idle-timeout <seconds>] [--json] [--verbose]

daq-unity <hover|go-to-definition|find-references|list-members> <name> \
  [--path <path>]... [--kind type|method|field|property] [--namespace <ns>] \
  [--assembly-name <name>] [--assets-only] [--project <path>] [--no-plugin-scan] \
  [--no-daemon] [--daemon-idle-timeout <seconds>] [--json] [--verbose]

daq-unity implementations <interface-or-base-type> \
  [--path <path>]... [--namespace <ns>] [--assembly-name <name>] [--assets-only] \
  [--auto-framework] [--include-framework-results] [--project <path>] [--no-plugin-scan] \
  [--no-daemon] [--daemon-idle-timeout <seconds>] [--json] [--verbose]

daq-unity list-assemblies \
  [--path <path>]... [--project <path>] [--no-plugin-scan] \
  [--no-daemon] [--daemon-idle-timeout <seconds>] [--json] [--verbose]

daq-unity daemon status [--json]
daq-unity daemon stop [--project <path>] [--no-plugin-scan] [--json] [--verbose]
daq-unity daemon start [--foreground] [--project <path>] [--no-plugin-scan] \
  [--daemon-idle-timeout <seconds>] [--json] [--verbose]

daq-unity resolves the project root, then queries Library/ScriptAssemblies by calling the Tesearis.DotNetAssemblyQuery API directly (in-process, not by shelling out to daq's CLI) with the right --source-root value filled in for you (and Library/ScriptAssemblies folded into the --path set alongside anything you pass yourself). --dir was removed (mirroring daq's own --assembly/--dir merge into --path); --source-root is resolved automatically and rejected if passed manually. If Library/ScriptAssemblies doesn't exist yet, it errors and suggests building the project first.

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 but not --kind; list-assemblies takes neither. find-symbol also takes --contains for a case-insensitive substring match instead of an exact name match (there's no separate search command). Pass --json to any command for machine-readable output instead of text. See daq's own README for the exact shape, since daq-unity's JSON matches it field-for-field - except list-assemblies, whose origin field is a deliberate daq-unity addition (see below), and implementations, which is a single object (targetIndexed/hint/implementations) rather than a bare array, mirroring daq's own single-object exception for the same command.

implementations only searches types physically declared in an indexed assembly, including the interface/base type itself: looking up a framework/BCL type like IDisposable requires indexing its defining assembly alongside your project's own, or daq-unity reports it as not found - unless --auto-framework is passed, in which case a not-found target triggers a one-time discovery and load of the exact reference assemblies Unity's own build system last used to compile this project, before retrying. A target that is indexed but genuinely has no implementers is reported separately from one that isn't indexed at all. Once --auto-framework has resolved the target, types found only via that discovery are used to resolve it but omitted from the reported matches by default (to avoid flooding output with e.g. every BCL implementer of IDisposable) - pass --include-framework-results to report them too.

--auto-framework discovery

Unity ships its own bundled reference assemblies inside the Unity Editor installation, which is not the same runtime as any locally-installed dotnet SDK - so --auto-framework can't just point at a local Microsoft.NETCore.App shared framework the way daq's own CLI does. Instead it reads back the exact reference-assembly paths Unity's own Bee-based incremental compiler (Unity 2020.2+) already resolved the last time it compiled this project, from the Roslyn response file (.rsp) it writes under Library/Bee/artifacts/*.dag/<AssemblyName>.rsp - no version-matching or install-path guessing needed, since Unity has already done that resolution itself on this exact machine. Two more guess-based approaches were considered and rejected: scanning well-known Unity Hub install locations (Unity Hub's own install registry can be stale/empty even when editors are installed) and parsing IDE-generated .csproj <Reference>/<HintPath> entries (a third-party IDE-integration dependency, and gitignored by default).

This requires the project to have been compiled by the Unity Editor at least once (so a .rsp exists); otherwise --auto-framework reports that plainly instead of silently doing nothing, and that one call runs in-process (forcing --no-daemon behavior for it) since the diagnostic has nowhere to travel back over the daemon wire. A successful discovery, on the other hand, runs through a warm daemon like any other query: the discovered reference set is a list of exact file paths spanning multiple directories (the API-level facade folder and its nested Facades/ subfolder, etc.), which DaemonRequest.FrameworkPaths carries verbatim.

Project discovery

By default, daq-unity finds the Unity project root by walking up from the current directory until it finds one containing Library/ScriptAssemblies. Pass --project <path> to start that walk somewhere else instead of the current directory - useful when your cwd is a monorepo root (or a repo with more than one Unity project) rather than inside the project itself. It's still a starting point for the same upward walk, so it doesn't need to be the project root exactly - any directory inside it works.

Assets vs. Packages

Library/ScriptAssemblies mixes the developer's own scripts with every Unity/third-party package assembly. daq-unity classifies each assembly by where its PDB says its code came from: Assets (the project's own code), Packages (a local, embedded, or registry-resolved package - including code compiled from Library/PackageCache), or Unknown (no PDB or no usable debug info at all - typically a precompiled plugin DLL, see below). When a package's PDB source path doesn't resolve under the project root at all (e.g. Unity's shared/global package cache, or a package referenced via an external file: path), daq-unity falls back to matching the assembly's name against every .asmdef-declared name and precompiled .dll found under Packages/ and Library/PackageCache/, so it still classifies as Packages instead of Unknown.

  • list-assemblies prints this as a trailing [Origin] (or an origin JSON field) so you can see which is which without guessing from the name.
  • Pass --assets-only to any other query command to restrict matches to Assets-classified assemblies only, filtering out package/plugin noise. Limitation: --assets-only always runs in-process - it can't be forwarded to a background daemon (the shared daemon protocol's request shape has no field for it), so a daemon isn't tried for that one call, though it may still be spawned afterward for later, unscoped calls.

Precompiled Assets/ plugins

Unity never recompiles a precompiled managed plugin DLL (e.g. Assets/Plugins/Foo.dll) into Library/ScriptAssemblies (it just references it as-is). daq-unity auto-discovers every *.dll under Assets/ (recursively) and includes them alongside Library/ScriptAssemblies by default, so these are queryable with zero config too. Pass --no-plugin-scan to skip this. Native (non-.NET) DLLs commonly live alongside managed plugins under Assets/ (often duplicated per platform slice), so daq-unity checks each discovered file's PE header up front and silently excludes native ones from auto-discovery instead of one failure warning per file. This (and a couple of other expected, non-actionable conditions) is hidden by default; pass --verbose to see the collapsed Note: skipped N native (non-.NET) DLL(s) under Assets/ line and similar diagnostics

  • see Verbose diagnostics below. A native DLL passed explicitly via --path is unaffected by this filtering and still gets today's per-file failure warning, since that's a direct request about a specific file. --no-plugin-scan must be passed consistently between daemon start and later query calls - see below.

Verbose diagnostics

By default daq-unity only prints warnings to stderr for things worth acting on: an unreadable or unmatched --path, or a DLL that failed to load. Expected, non-actionable noise - most notably the native (non-.NET) DLLs routinely skipped under Assets/ (see above) - is hidden. Pass --verbose to any query or daemon start/daemon stop command to see it too, e.g. when debugging why a particular DLL isn't showing up in results.

Limitations

  • Stale/uncompiled sources: not-yet-compiled scripts (new, edited, or failing to build) won't show up until Unity recompiles the project.
  • Managed libraries outside Assets/, for now: not auto-discovered (e.g. packages under Packages/) - pass them explicitly with --path <path>.

Background daemon

Like daq itself, daq-unity keeps this project's assemblies warm in a background daemon between invocations, so repeated queries skip re-loading Library/ScriptAssemblies from scratch. The daemon (named-pipe transport, process lifecycle, staleness detection) is daq's own daemon library, shared rather than duplicated. See dotnet-assembly-query's README for how it behaves.

daq-unity daemon status [--json]                              # list running daemons (machine-wide)
daq-unity daemon stop [--project <path>] [--no-plugin-scan] [--json] [--verbose]   # stop this project's daemon
daq-unity daemon start [--foreground] [--project <path>] [--no-plugin-scan] \
  [--daemon-idle-timeout <seconds>] [--json] [--verbose]

Pass --no-daemon to any query command to run in-process only (never try or spawn a daemon), or --daemon-idle-timeout <seconds> to control how long an auto-spawned daemon stays warm before exiting on its own (default 1800s, or DAQ_DAEMON_IDLE_TIMEOUT_SECONDS). --daemon-idle-timeout is intentionally hidden from --help output but works like any other flag. Unlike daq's own daemon stop/daemon start, daq-unity's take no --path filter; they always target this Unity project's own Library/ScriptAssemblies, resolved the same way a query would (via --project, same as a query command).

A warm daemon is matched to a call by its exact resolved DLL set, so --no-plugin-scan must be passed the same way on every call against a given daemon - toggling it between calls (including between daemon start and the query calls that follow) spawns a second, separate daemon instead of reusing the first, same as passing a different --path set would.

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. project root or Library/ScriptAssemblies not found) - matching daq's own convention.

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-unity:*)"]
  }
}

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

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

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

Building & testing

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

License

MIT. See LICENSE.

Product Compatible and additional computed target framework versions.
.NET 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.

This package has no dependencies.

Version Downloads Last Updated
2.0.0 112 9/7/2026
1.0.0 103 9/5/2026