Skip to content

Dart workspaces

Hyena switches to workspace analysis when the target root’s pubspec.yaml declares workspace. It analyzes the root package and every discovered member independently, while keeping report paths relative to the common workspace root.

A root can list package directories explicitly and with globs:

pubspec.yaml
name: company_workspace
environment:
sdk: ^3.10.0
workspace:
- apps/mobile
- packages/*
- tools/codegen

Every non-root member must opt into Dart workspace resolution:

packages/auth/pubspec.yaml
name: company_auth
resolution: workspace
environment:
sdk: ^3.10.0

Hyena supports explicit entries, nested workspace declarations, and glob entries. The accepted workspace syntax is ultimately determined by the installed Dart SDK, so use syntax that dart pub get accepts for the target project.

A discovered member can declare another workspace to add nested members:

packages/platform/pubspec.yaml
name: company_platform
resolution: workspace
environment:
sdk: ^3.10.0
workspace:
- plugins/*

Resolve the complete workspace before analysis:

Terminal window
dart pub get
dart run hyena_dart analyze .

Point the command at the directory that owns the root workspace: declaration. A package directory without that root declaration is analyzed as a single package.

Hyena builds one analysis result per package, including the root package. Descendant member directories are excluded from every ancestor scan. A source file is therefore analyzed once, under the nearest package boundary and that package’s configuration.

Workspace discovery rejects ambiguous or unsafe membership, including:

  • a missing member;
  • the same member listed more than once;
  • duplicate package names;
  • a member outside the target root;
  • a resolved member symlink that escapes the target root; and
  • a non-root member without resolution: workspace.

Fix the workspace declaration rather than excluding an invalid member from Hyena. The same package graph should be valid for both Dart and the analyzer.

A root in an app package can therefore keep the exact declaration it reaches in a shared package, along with that declaration’s reachable dependencies. Package-scoped reports are preserved: a live shared declaration remains in its package’s result rather than moving into the caller’s result.

Matching uses resolved declaration identity, not just a name. If two packages both declare SessionStore, a reference to one does not keep the other alive. References made only from an unreachable caller also do not make their targets reachable, including when caller and target are in different packages.

This joined graph applies to dead-code analysis. Complexity remains calculated and reported independently for each package.

Without --config, Hyena discovers configuration separately for each package:

  1. start at the package directory;
  2. prefer hyena.yaml over analysis_options.yaml in the same directory;
  3. walk upward to the nearest configuration; and
  4. stop at the workspace root.

A member can therefore override the root policy with its own configuration, while packages without a local file inherit the nearest parent configuration inside the workspace.

An explicit configuration is intentionally different:

Terminal window
dart run hyena_dart analyze . --config=tool/hyena-strict.yaml

The selected file applies to every package in the workspace. It may be outside the workspace root because the user, rather than automatic discovery, supplied the path. Review that file before using it; there is no per-member fallback when --config is explicit.

See Configuration for the complete schema.

The presentation preserves package boundaries:

Format Workspace behavior
Console Prints a separate analysis section for each package with workspace-relative target and finding paths.
JSON Adds workspace.packageCount and a packages array; every targetPath and filePath is workspace-relative.
Markdown Groups summaries and workspace-relative findings by package.
HTML Renders package-scoped sections with workspace-relative paths.
SARIF Keeps source locations relative to the common workspace root.

Baseline fingerprints also use workspace-relative paths, so matching remains stable across package sections without leaking absolute member locations.

With workspace support in v1.2.0, running dart run hyena_dart:hyena_mcp --root . from the workspace root accepts a relative target such as . and aggregates every workspace package. Returned finding paths remain relative to that configured workspace root.

The MCP server’s existing file, byte, finding, time, and concurrency limits still apply to the complete request. Workspace support does not broaden the configured root or create a second tool. See Safe AI and MCP for the read-only contract and trust boundary.