Table of Contents

DBTools.App

Purpose: Main application assembly containing all tools and bootstrapping logic.
Output: DBTools.dll
Target Frameworks: net48, net8.0-windows


Overview

DBTools.App is the main application assembly that compiles to DBTools.dll. It is loaded by DBTools.Loader and serves as the host for all tool implementations, the dependency injection container, ribbon composition, and Revit lifecycle management.

This project uses file linking to compile tool source code from src/Tools/ into the single DBTools.dll assembly. Individual tool projects exist primarily for IDE organization, isolated testing, and XAML designer support, but at runtime everything executes within this unified assembly.

Source: src/DBTools.App/DBTools.App.csproj:1-13


Responsibilities

Responsibility Implementation
Application entry point AddinEntry implements IExternalApplication
Runtime initialization AppRuntimeFactory creates DiAppRuntime
DI container setup DbtServiceBootstrapper.Build() registers all services
Tool discovery DbtToolModuleCatalog.Discover() scans embedded YAML manifests
Ribbon composition DbtRibbonComposer creates tab, panels, and buttons
Hook coordination DbtHookHost dispatches view-activated and contextual ribbon events
Test API hosting TestApiHost enables API calls from test harnesses
File-linked tool compilation MSBuild items compile src/Tools/**/*.cs into this assembly

Source: src/DBTools.App/Addin/AddinEntry.cs:29-247


Key Components

AddinEntry (Application Entry Point)

The AddinEntry class is the inner application entry point, loaded by DBTools.Loader.AddinEntry after assembly resolution is configured.

public class AddinEntry : IExternalApplication
{
    public static IRevitTask? RevitTask { get; private set; }
    private static DbtHookHost? _hookHost;
    private static DbtToolRegistry? _toolRegistry;
    private static DbtRibbonComposer? _ribbonComposer;
    
    public Result OnStartup(UIControlledApplication application) { ... }
    public Result OnShutdown(UIControlledApplication application) { ... }
}

Source: src/DBTools.App/Addin/AddinEntry.cs:29-43

Key Static Fields:

  • RevitTask - The IRevitTask for marshaling work to Revit's UI thread
  • _hookHost - Coordinates view-activated and contextual ribbon hooks
  • _toolRegistry - Registry of discovered tool commands
  • _ribbonComposer - Manages ribbon tab/panel creation

Source: src/DBTools.App/Addin/AddinEntry.cs:33-36

Bootstrap System

AppRuntimeFactory

Factory class that creates the application runtime:

public static class AppRuntimeFactory
{
    public static IAppRuntime Create() => new DiAppRuntime();
}

Source: src/DBTools.App/Bootstrap/AppRuntimeFactory.cs:5-8

DiAppRuntime

The DI-based implementation of IAppRuntime that owns the service provider:

public sealed class DiAppRuntime : IAppRuntime
{
    private readonly IServiceProvider _root;
    
    public DiAppRuntime()
    {
        var root = DbtServiceBootstrapper.Build();
        _root = root.Services;
        _logger = _root.GetRequiredService<ILogger<DiAppRuntime>>();
        _settings = _root.GetRequiredService<ISettingsProvider>();
    }
    
    public IAppRunScope CreateRunScope(UIApplication uiapp, RevitRunScopeProfile profile)
    {
        var scope = _root.GetRequiredService<IServiceScopeFactory>().CreateScope();
        var rsFactory = scope.ServiceProvider.GetRequiredService<IRevitRunScopeFactory>();
        var accessor = scope.ServiceProvider.GetRequiredService<IRevitRunScopeAccessor>();
        var rs = rsFactory.CreateScope(uiapp, profile);
        accessor.Current = rs;
        return new DiRunScope(scope);
    }
}

Source: src/DBTools.App/Bootstrap/DiAppRuntime.cs:17-62

DbtServiceBootstrapper

The central DI container builder that registers all services:

public static class DbtServiceBootstrapper
{
    public static DbtServiceRoot Build(Action<IServiceCollection>? configure = null)
    {
        // 1. Create logging host
        // 2. Load configuration from settings.{YEAR}.json
        // 3. Register core services
        // 4. Discover and register tool modules
        // 5. Start hosted services
    }
}

Source: src/DBTools.App/Bootstrap/DbtServiceBootstrapper.cs:163-248

Service Categories Registered:

  • Logging - IDbtLoggingHost, ILoggerFactory, ILogger<T>
  • Debug - IDebugModeService (session-only)
  • Settings - IOptionsMonitor<T>, IOptionsWriter, ISettingsProvider
  • Revit Scope - IRevitRunScopeFactory, IRevitRunScopeAccessor, IRevitCallGate
  • Transactions - ITransactionRunner, ITransactionGroupService
  • Execution - ISafeExecutor, IAlertService
  • Hosted Services - ILoggerWindowManager, HostedServiceCoordinator

Source: src/DBTools.App/Bootstrap/DbtServiceBootstrapper.cs:376-437

DbtServiceRoot

Container holding all initialized services:

public sealed class DbtServiceRoot
{
    public IServiceProvider Services { get; }
    public IConfiguration Configuration { get; }
    public IDbtLoggingHost LoggingHost { get; }
    public DbtToolRegistry ToolRegistry { get; }
    public IReadOnlyList<DbtToolModule> ToolModules { get; }
}

Source: src/DBTools.App/Bootstrap/DbtServiceBootstrapper.cs:34-67

Ribbon Composition

DbtRibbonComposer

Composes the DB Tools ribbon from tool specifications:

public sealed class DbtRibbonComposer : IDisposable
{
    public void Compose(DbtToolRegistry registry, Flags flags)
    {
        // 1. Create ribbon tab
        // 2. Create panels in defined order
        // 3. Build control plans for each tool
        // 4. Execute plans in order (push buttons, split buttons, pulldowns, stacked)
        // 5. Subscribe to settings changes
    }
}

Source: src/DBTools.App/Features/Ribbon/DbtRibbonComposer.cs:19-102

Control Types Supported:

Control Kind Description
PushButton Standard button
SplitButtonItem Primary + dropdown items
PulldownButtonItem Dropdown menu
StackedButtonItem 2-3 buttons in vertical stack

Source: src/DBTools.App/Features/Ribbon/DbtRibbonComposer.cs:159-252

Panel Order:

  1. Settings
  2. Common
  3. Structural
  4. Testing
  5. Any additional panels (alphabetical)

Source: src/DBTools.App/Features/Ribbon/DbtRibbonComposer.cs:109-121

RibbonDefinition

Defines ribbon constants (tab name, panel names, command names):

public static class RibbonDefinition
{
    public const string TabName = SettingsConstants.Ribbon.TabName;
    
    public static class Panels
    {
        public const string Settings = SettingsConstants.Ribbon.Panels.Settings;
        public const string Structural = SettingsConstants.Ribbon.Panels.Structural;
        public const string Testing = SettingsConstants.Ribbon.Panels.Testing;
        public const string Common = SettingsConstants.Ribbon.Panels.Common;
    }
}

Source: src/DBTools.App/Features/Ribbon/RibbonDefinition.cs:5-21

Availability Predicates

Located in Tools/Availability/, these classes control when ribbon buttons are enabled.

DbtDocumentAvailability

Requires an active document to be open:

public sealed class DbtDocumentAvailability : IExternalCommandAvailability
{
    public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories)
    {
        return applicationData?.ActiveUIDocument?.Document != null;
    }
}

Source: src/DBTools.App/Tools/Availability/DbtDocumentAvailability.cs:10-16

DbtSelectionAvailability

Base class requiring element selection, with optional category filtering:

public class DbtSelectionAvailability : IExternalCommandAvailability
{
    protected virtual BuiltInCategory[]? RequiredCategories => null;
    protected virtual bool IsElementMatch(Element element) => true;
    
    public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories)
    {
        // Check for selection and optional category/element matching
    }
}

Source: src/DBTools.App/Tools/Availability/DbtSelectionAvailability.cs:13-52

DbtStructuralFramingSelectionAvailability

Requires structural framing selection:

public sealed class DbtStructuralFramingSelectionAvailability : DbtSelectionAvailability
{
    protected override BuiltInCategory[]? RequiredCategories 
        => new[] { BuiltInCategory.OST_StructuralFraming };
}

Source: src/DBTools.App/Tools/Availability/DbtSelectionAvailability.cs:57-60

Hooks System

AppHookModule

The application-level hook module that registers view-activated handlers:

public sealed class AppHookModule : DbtToolModule
{
    public override void RegisterServices(IServiceCollection services, DbtToolManifest manifest)
    {
        services.AddSingleton<ViewActivatedHookHandler, ViewActivatedHookHandler>();
    }

    public override void RegisterHooks(DbtToolRegistry registry, DbtToolManifest manifest)
    {
        registry.RegisterHook<IViewActivatedHookHandler, ViewActivatedHookHandler>();
    }
}

Source: src/DBTools.App/Features/Hooks/AppHookModule.cs:7-22

TestApiHost

Static host for test harness integration, providing API access outside Revit command context:

public static class TestApiHost
{
    public static bool IsInitialized { get; private set; }
    
    public static void Initialize(UIControlledApplication app)
    {
        // Create ExternalEvent handler
    }
    
    public static Task<object?> RunAsync(Func<object, Task<object?>> work)
    {
        // Queue work and raise ExternalEvent.
        // Raise statuses:
        // - Accepted/Pending: work remains queued and will be processed
        // - other: work is removed and the returned task faults
    }
}

ProcessQueue drains up to 50 queued items per ExternalEvent tick and re-raises the ExternalEvent when backlog remains, so throttling does not strand queued work.

Source: src/DBTools.App/Addin/TestApiHost.cs:11


OnStartup Sequence

The complete startup flow with source references:

1. Set Revit year from VersionNumber (for per-instance log files)
   > Source: AddinEntry.cs:55-66

2. Initialize AppRuntime via AppRuntimeFactory.Create()
   > Source: AddinEntry.cs:72

3. Resolve ISafeExecutor, DbtToolRegistry, DbtHookHost
   > Source: AddinEntry.cs:74-76

4. Validate theme (mandatory - fails startup if broken)
   > Source: AddinEntry.cs:106-113

5. Detect RevitTest environment
   > Source: AddinEntry.cs:116, 573-629

6. Register deferred UI startup handlers (ApplicationInitialized, Idling)
   > Source: AddinEntry.cs:125-127

7. Execute startup task via ISafeExecutor.RunAsync:
   a. Try binding global window owner (may be deferred)
   b. Initialize DialogGuardianHook
   c. Initialize RevitTaskService (if not RevitTest)
   d. Attach DbtHookHost (if not RevitTest)
   e. Initialize TestApiHost (if not RevitTest)
   > Source: AddinEntry.cs:133-178

8. Create ribbon synchronously (Revit API requirement):
   a. Get Flags settings
   b. Create DbtRibbonComposer
   c. Call Compose() with registry and flags
   d. Publish warning changes
   > Source: AddinEntry.cs:217-244

9. Return Result.Succeeded
   > Source: AddinEntry.cs:246

Deferred UI Startup

Certain operations run after ApplicationInitialized/Idling to ensure Revit is fully ready:

private static Task RunDeferredUiStartupAsync(...)
{
    return executor.RunAsync(async () =>
    {
        // 1. Ensure global window owner is bound
        await EnsureGlobalWindowOwnerBoundAsync(logger);
        
        // 2. Apply ribbon special effects (visual styling)
        await RibbonSpecialEffects.ApplyAsync(logger);
        
        // 3. Show logger window if debug mode enabled
        loggerWindowManager.ShowIfDebugEnabled();
    }, ...);
}

Source: src/DBTools.App/Addin/AddinEntry.cs:421-479


OnShutdown Sequence

1. Wait for startup task completion (5s timeout)
   > Source: AddinEntry.cs:265-274

2. Unregister deferred UI handlers
   > Source: AddinEntry.cs:276

3. Detach DbtHookHost
   > Source: AddinEntry.cs:278

4. Shutdown DialogGuardianHook
   > Source: AddinEntry.cs:281

5. Shutdown TestApiHost
   > Source: AddinEntry.cs:284

6. Dispose RevitTaskService
   > Source: AddinEntry.cs:287

7. Dispose DbtRibbonComposer
   > Source: AddinEntry.cs:292

8. Stop hosted services
   > Source: AddinEntry.cs:303

9. Dispose logging host
   > Source: AddinEntry.cs:306

Source: src/DBTools.App/Addin/AddinEntry.cs:250-325


File Linking Strategy

DBTools.App compiles tool source code via MSBuild file linking. Source files remain in their respective src/Tools/ directories but are compiled into the single DBTools.dll assembly.

Source File Linking

<ItemGroup Label="Tool Source Files">
  <Compile Include="..\Tools\**\*.cs"
           Exclude="..\Tools\**\Tests\**\*.cs;..\Tools\**\obj\**\*.cs"
           Link="Tools\%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

Source: src/DBTools.App/DBTools.App.csproj:63-67

XAML File Linking

<ItemGroup Label="Tool XAML Files">
  <Page Include="..\Tools\**\*.xaml"
        Exclude="..\Tools\**\obj\**\*.xaml;..\Tools\**\Properties\DesignTimeResources.xaml"
        Link="Tools\%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

Source: src/DBTools.App/DBTools.App.csproj:69-73

Tool Assets (Embedded Icons)

<ItemGroup Label="Tool Embedded Assets">
  <EmbeddedResource Include="..\Tools\**\Assets\*.png"
                    Link="Resources\Icons\%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

Source: src/DBTools.App/DBTools.App.csproj:75-78

Tool Manifests (Embedded YAML)

<ItemGroup>
  <EmbeddedResource Include="manifest.yml" LogicalName="DBTools.ToolManifests.DBTools.AppHooks.yml" />
  <EmbeddedResource Include="..\Tools\**\manifest.yml"
                    LogicalName="DBTools.ToolManifests.%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

Source: src/DBTools.App/DBTools.App.csproj:218-222

Why File Linking?

  1. Single assembly deployment - Reduces complexity and potential conflicts
  2. IDE organization - Tools can have separate projects for development
  3. Isolated testing - Test projects reference tool projects directly
  4. XAML designer support - Individual projects can have design-time resources

Tool Module Discovery

Tools are discovered via embedded YAML manifests during DbtServiceBootstrapper.Build():

var rootAssembly = typeof(DbtServiceBootstrapper).Assembly;
var discovery = DbtToolModuleCatalog.Discover(rootAssembly);

foreach (var entry in discovery.Entries)
{
    var module = entry.Module;
    module.RegisterSettings(services, configuration, entry.Manifest);
    module.RegisterServices(services, entry.Manifest);
    module.RegisterSettingsPacks(services, entry.Manifest);
    module.RegisterHooks(registry, entry.Manifest);
    RegisterRibbonToolsFromManifest(registry, rootAssembly, entry.Manifest);
}

Source: src/DBTools.App/Bootstrap/DbtServiceBootstrapper.cs:194-237

Manifest Structure

Example tool manifest (manifest.yml):

id: DBTools.GM
assembly: DBTools
moduleType: DBTools.GM.GmToolModule
order: 0
sandboxWindows:
  - id: DBTools.GM.Main
    displayName: "Global Mapper"
    windowType: "DBTools.GM.Shell.UI.Views.GmWindow"
tool:
  ribbonTools:
    - internalName: DBTools.GM
      commandType: DBTools.GM.Features.GmCommand
      availabilityType: DBTools.App.Tools.Availability.DbtDocumentAvailability
      runProfile: InlineUi
      displayText: "Global Mapper"
      iconBaseKey: gm
      tooltip: "Open Global Mapper"
      controlKind: PushButton
      order: 30

Source: src/Tools/Common/GM/manifest.yml:1-27


Dependencies

Project References

Project Purpose
DBTools.Core Core infrastructure and abstractions
DBTools.Themes WPF theme resources

Source: src/DBTools.App/DBTools.App.csproj:57-59

NuGet Packages

Package Purpose
Microsoft.Extensions.DependencyInjection DI container
Microsoft.Extensions.Options.ConfigurationExtensions Options pattern
Microsoft.Extensions.Configuration.* Configuration binding
ricaun.Revit.UI Ribbon utilities
ricaun.Revit.UI.Tasks RevitTask for async
CommunityToolkit.Mvvm MVVM for tools
JsonDiffPatch.Net JSON diff (tools)
AutoMapper Object mapping (tools)
HelixToolkit.Wpf.SharpDX 3D visualization (tools)

Source: src/DBTools.App/DBTools.App.csproj:27-87

Revit API References

net48:

<Reference Include="RevitAPI">
  <HintPath>$(REVIT2024_DIR)\RevitAPI.dll</HintPath>
  <Private>false</Private>
</Reference>

Source: src/DBTools.App/DBTools.App.csproj:114-134

net8.0-windows:

<Reference Include="RevitAPI" Condition="'$(REVIT_NET8_DIR)'!=''">
  <HintPath>$(REVIT_NET8_DIR)\RevitAPI.dll</HintPath>
  <Private>false</Private>
</Reference>

Source: src/DBTools.App/DBTools.App.csproj:143-165


Assembly Embedding

All CopyLocal dependencies (except forbidden host assemblies) are embedded as resources for single-DLL deployment:

<Target Name="DBT_EmbedCopyLocalAssemblies" AfterTargets="ResolveReferences">
  <ItemGroup>
    <_EmbedCandidate Include="@(ReferenceCopyLocalPaths)" ... />
    <!-- Exclude Revit host assemblies -->
    <_EmbedCandidate Remove="..." Condition="'%(Filename)%(Extension)' == 'RevitAPI.dll'" />
    <!-- Exclude WPF theme assemblies (need file Location for pack:// URIs) -->
    <_EmbedCandidate Remove="..." Condition="$([System.String]::Copy('%(Filename)').StartsWith('DBTools.Themes'))" />
  </ItemGroup>
  <EmbeddedResource Include="%(_EmbedLogicalDistinct.SourcePath)"
                    LogicalName="DBTools.EmbeddedAssemblies.%(Name).dll" />
</Target>

Source: src/DBTools.App/DBTools.App.csproj:234-270

Forbidden Host Assemblies

These assemblies must never be in the output (Revit provides them):

<ItemGroup>
  <ForbiddenHostAssembly Include="RevitAPI.dll" />
  <ForbiddenHostAssembly Include="RevitAPIUI.dll" />
  <ForbiddenHostAssembly Include="AdWindows.dll" />
  <ForbiddenHostAssembly Include="UIFramework.dll" />
  <ForbiddenHostAssembly Include="Newtonsoft.Json.dll" />
</ItemGroup>

Source: src/DBTools.App/DBTools.App.csproj:169-177


Build Configuration

Project Properties

<PropertyGroup>
  <TargetFrameworks>$(DBT_RevitTargetFrameworks)</TargetFrameworks>
  <AssemblyName>DBTools</AssemblyName>
  <RootNamespace>DBTools.App</RootNamespace>
  <UseWPF>true</UseWPF>
  <ILRepackEnabled>false</ILRepackEnabled>
</PropertyGroup>

Source: src/DBTools.App/DBTools.App.csproj:2-13

InternalsVisibleTo

Test assemblies can access internal types:

<ItemGroup>
  <InternalsVisibleTo Include="DBTools.GM.Tests" />
  <InternalsVisibleTo Include="DBTools.SGT.Tests" />
  <InternalsVisibleTo Include="DBTools.TDV.Tests" />
  <InternalsVisibleTo Include="DBTools.Testing.Tests" />
</ItemGroup>

Source: src/DBTools.App/DBTools.App.csproj:15-20

Warning Suppressions

Tool code suppressions consolidated from individual tool projects:

<NoWarn>$(NoWarn);CS0618;CS0649;CS8600;CS8602;CS8603;CS8604;CS8619;CS8620;
  MA0038;MA0051;MA0048;MA0016;MA0098;MA0004;MA0008;MA0015;
  CA1068;CA1707;CA1716;CA1720;CA1722;CA1725;CA1822</NoWarn>

Source: src/DBTools.App/DBTools.App.csproj:136-138


File Structure

src/DBTools.App/
+-- DBTools.App.csproj
+-- manifest.yml               # AppHooks module manifest
+-- GlobalUsings.cs
+-- GlobalSuppressions.cs
+-- AssemblyInfo.cs
+-- Addin/
|   +-- AddinEntry.cs         # IExternalApplication entry point
|   +-- TestApiHost.cs        # Test harness API host
+-- Bootstrap/
|   +-- AppRuntimeFactory.cs  # Creates DiAppRuntime
|   +-- DiAppRuntime.cs       # IAppRuntime implementation
|   +-- DbtServiceBootstrapper.cs  # DI container builder
|   +-- Startup.cs            # Legacy startup helper
+-- Features/
|   +-- Hooks/
|   |   +-- AppHookModule.cs  # Application-level hooks
|   |   +-- ViewActivatedHookHandler.cs
|   +-- Ribbon/
|       +-- DbtRibbonComposer.cs  # Ribbon composition
|       +-- RibbonDefinition.cs    # Tab/panel constants
|       +-- RibbonRegistry.cs      # Runtime button registry
|       +-- RibbonSettingsListener.cs
|       +-- RibbonSpecialEffects.cs
|       +-- RevitRibbonBuilder.cs
+-- Tools/
|   +-- Availability/
|       +-- DbtDocumentAvailability.cs
|       +-- DbtSelectionAvailability.cs
|       +-- DbtActiveViewAvailability.cs
+-- Resources/
    +-- (Ribbon icons - embedded resources)

RevitTest Environment Detection

AddinEntry detects when running under ricaun.RevitTest and adjusts behavior:

private static bool DetectRevitTestEnvironment(UIControlledApplication application, ILogger logger)
{
    bool hostBoundToRevitTest = RevitTaskAccessor.RevitTask != null;
    bool hasRevitTestAssembly = AppDomain.CurrentDomain.GetAssemblies()
        .Any(a => a.GetName().Name == "ricaun.RevitTest.Application" || 
                  a.GetName().Name == "ricaun.RevitTest");
    bool isRevitTestFlag = RevitExecutionContext.IsRevitTest;
    
    return hostBoundToRevitTest || hasRevitTestAssembly || isRevitTestFlag;
}

Source: src/DBTools.App/Addin/AddinEntry.cs:573-629

Skipped in RevitTest:

  • RevitTaskService initialization (test host provides its own)
  • DbtHookHost attachment
  • TestApiHost initialization

Error Handling

Startup Errors

Fatal errors during startup show a message box and return Result.Failed:

private static void TryShowStartupError(string message)
{
    // Try WPF MessageBox first
    MessageBox.Show(message, "DB Tools", MessageBoxButton.OK, MessageBoxImage.Error, ...);
    
    // Fall back to Win32 MessageBoxW if WPF fails
    MessageBoxW(IntPtr.Zero, text, caption, MB_OK | MB_ICONERROR);
}

Source: src/DBTools.App/Addin/AddinEntry.cs:481-535

Theme Validation

Theme validation is mandatory - if it fails, startup aborts:

try
{
    DbtThemeValidator.ValidateOrThrow();
}
catch (Exception ex)
{
    logger.LogError(ex, "[AddinEntry] Theme validation failed; aborting add-in startup.");
    TryShowStartupError($"DB Tools theme failed to load:\n\n{ex.GetBaseException().Message}");
    return Result.Failed;
}

Source: src/DBTools.App/Addin/AddinEntry.cs:104-113


Troubleshooting

"Theme validation failed" Error

Cause: WPF theme assemblies (DBTools.Themes, DBTools.HandyControl) not found.

Solution:

  1. Ensure %APPDATA%/DBTools/vendor/ contains theme assemblies
  2. Or rebuild with bash build.sh --clean BuildAll

Ribbon Not Appearing

Possible Causes:

  1. Tool manifests not embedded (check build output)
  2. Tool command type not found (check namespace/assembly in manifest)
  3. Availability type resolution failed (check availabilityType in manifest)

Diagnosis: Enable debug mode and check log file for errors during Compose().

Tools Disabled Unexpectedly

Check:

  1. Availability predicate in manifest.yml
  2. Flags settings (some tools check flags.EnableTesting)
  3. IExternalCommandAvailability implementation returning false


Source Files Reviewed

File Purpose
src/DBTools.App/DBTools.App.csproj Project configuration
src/DBTools.App/Addin/AddinEntry.cs Application entry point
src/DBTools.App/Addin/TestApiHost.cs Test harness API
src/DBTools.App/Bootstrap/AppRuntimeFactory.cs Runtime factory
src/DBTools.App/Bootstrap/DiAppRuntime.cs DI-based runtime
src/DBTools.App/Bootstrap/DbtServiceBootstrapper.cs DI container builder
src/DBTools.App/Bootstrap/Startup.cs Legacy startup helper
src/DBTools.App/Features/Ribbon/DbtRibbonComposer.cs Ribbon composition
src/DBTools.App/Features/Ribbon/RibbonDefinition.cs Ribbon constants
src/DBTools.App/Features/Hooks/AppHookModule.cs Application hooks
src/DBTools.App/Tools/Availability/DbtDocumentAvailability.cs Document availability
src/DBTools.App/Tools/Availability/DbtSelectionAvailability.cs Selection availability
src/DBTools.App/manifest.yml AppHooks manifest
src/Tools/Common/GM/manifest.yml Example tool manifest
src/DBTools.Core/Tools/DbtHookHost.cs Hook coordination (Core)

UNVERIFIED Items