Skip to content

Deepstaging CLI

The deepstaging CLI generates all non-C# output from your compilation. It queries the same Deepstaging.Projection layer that source generators use — no intermediate formats, no staleness detection, just direct projection queries against your code.

Installation

The CLI is bundled inside the Deepstaging NuGet package. When a project sets <IsDeepstagingApp>true</IsDeepstagingApp>, MSBuild targets automatically:

  1. On restore — emit a ./deepstaging wrapper script in the project directory
  2. After build — run deepstaging sync to keep non-C# output current

The wrapper script always points to the CLI DLL from the currently-referenced package version — no separate tool install needed, no version to pin.

# In your Monolith project directory
./deepstaging sync              # one-shot sync
./deepstaging sync --dry-run    # preview changes
./deepstaging watch             # continuous sync in a terminal

The wrapper is auto-added to .gitignore (it contains a local NuGet cache path).

Global tool install

For use outside a project directory, the CLI is also available as a global dotnet tool:

dotnet tool install --global Deepstaging.Cli
deepstaging sync path/to/MyApp.Monolith.csproj

Commands

deepstaging watch — Continuous Sync

Holds the compilation warm and syncs on every file change. Run in a terminal alongside your dev server.

./deepstaging watch
  • Opens your project via MSBuildWorkspace once (warm start)
  • Watches source files with 200ms debounce
  • On change: incremental recompilation + all syncers run
  • Output updates within milliseconds of save

deepstaging sync — CI / One-Shot

Explicit one-shot sync for CI pipelines or manual runs.

deepstaging sync [PROJECT] [OPTIONS]
Option Description
[PROJECT] Path to .csproj (defaults to first .csproj in current directory)
--dry-run Show what would change without writing
--skip <TYPES> Skip specific syncers (comma-separated)

Skip types: wire-manifest, config, typescript, docs, ef-tooling, bruno

# Sync everything
deepstaging sync

# Sync only config and wire manifest
deepstaging sync --skip typescript,docs,ef-tooling,bruno

# Preview changes
deepstaging sync --dry-run

What Gets Generated

The CLI runs a set of syncers — each produces specific output files from the compilation:

Wire Manifest

File: wire-manifest.json

Tracks all [IntegrationEvent] wire names across the system. Append-only — new wire names are added automatically, existing entries are never removed. The DSEQ11 analyzer reads this manifest and flags removals or changes as errors.

[
  { "wireName": "ordering.order-placed", "clrType": "OrderPlaced", "registered": "2026-03-15", "status": "active" },
  { "wireName": "catalog.item-created", "clrType": "ItemCreated", "registered": "2026-03-16", "status": "active" }
]

Configuration

Files:

File Ownership Purpose
deepstaging.schema.json Machine-owned (always overwritten) JSON Schema for appsettings.json
deepstaging.secrets.schema.json Machine-owned JSON Schema for secrets (if any [Secret] properties exist)
deepstaging.settings.json Human-owned (intelligent merge) Settings template — new keys added, existing values preserved
deepstaging.secrets.json Human-owned (intelligent merge) Secrets template (if applicable)

Discovered from all [ConfigSection] types visible to the composition root.

TypeScript Client

Files: api-types.ts, api-client.ts

Auto-generated TypeScript types and client from [RestApi] projection models. Keeps your frontend type-safe against the C# API.

EF Tooling

File: ef.sh

Shell script wrapper for Entity Framework migrations across all discovered [DataStore] bounded contexts. Handles the --project, --context, and --output-dir flags automatically.

./scripts/ef.sh add AddOrderStatus Ordering
./scripts/ef.sh update Catalog

Documentation

Files: docs/{context}/index.md, docs/{context}/{entity}.md, docs/{context}/events.md, docs/{context}/api.md

Auto-generated documentation pages from [DocsModule] types — entity schemas, API routes, published/consumed events.

Bruno Collection

Files: bruno/opencollection.yml, bruno/environments/*.yml, bruno/{entity}/*.yml

Auto-generated Bruno API collection in OpenCollection YAML format. Per-route request definitions with environment-specific base URLs.

How It Works

deepstaging sync / watch
CompilationProvider opens .csproj via MSBuildWorkspace
Checks for [assembly: DeepstagingApp]
Syncers walk the compilation
    ├── Per-type syncers: WalkAllNamedTypes()
    └── Compilation-wide syncers: run once
Each syncer calls Deepstaging.Projection queries
(same queries the source generators use)
FileSync.WriteIfChanged() — only touches files that differ
SyncSummaryRenderer shows results table

The CLI targets the composition root project — the one with [assembly: DeepstagingApp]. It discovers all [Runtime], [DataStore], [RestApi], [ConfigSection], and [IntegrationEvent] types across all referenced assemblies, exactly as the source generators do.

CI Integration

Add to your CI pipeline after build:

- name: Sync non-C# artifacts
  run: dotnet deepstaging sync --dry-run  # Verify nothing is stale

- name: Check wire manifest
  run: |
    dotnet deepstaging sync
    git diff --exit-code wire-manifest.json  # Fail if manifest changed

Relationship to Source Generators

Source generators produce C# code (store implementations, DbContexts, dispatch handlers, etc.). The CLI produces everything else (JSON schemas, TypeScript, shell scripts, docs, API collections). Both consume Deepstaging.Projection — the single source of truth for attribute interpretation.

Output Producer Runs When
C# code Roslyn source generators Every compilation
Wire manifest, config, TypeScript, docs, ef.sh, Bruno deepstaging CLI watch daemon or sync command