Part XI: The .Design CLI + Interactive TUI
Overview
The CMF provides two interfaces for developers:
- Standard CLI -- non-interactive commands for scripting, CI pipelines, and quick scaffolding. Similar to
dotnet ef,dotnet new, orng generate. - Interactive TUI -- a terminal-based modeling session using Spectre.Console. Visual designers for aggregates, features, workflows, and requirements with live code preview.
Both are packaged as a single .NET global tool:
dotnet tool install --global Cmf.Clidotnet tool install --global Cmf.CliAfter installation, the cmf command is available system-wide.
Standard CLI Commands
Command Hierarchy
cmf new -- Scaffold a New Project
Creates a complete solution structure from a template.
cmf new MyStore --template ecommercecmf new MyStore --template ecommerceTemplates available:
| Template | What It Creates | Use Case |
|---|---|---|
ecommerce |
Catalog, Ordering, Identity contexts; Product, Order, Customer aggregates | Online store |
blog |
Content, Editorial contexts; BlogPost, Category, Author aggregates | Publishing platform |
saas |
Tenancy, Subscription, Identity contexts; Tenant, Plan, User aggregates | Multi-tenant SaaS |
Generated structure:
MyStore/
├── MyStore.sln
├── Directory.Build.props ← Quality gates config, analyzer settings
├── .editorconfig ← REQ diagnostic severities
├── src/
│ ├── MyStore.Requirements/ ← Empty Feature/Epic stubs per template
│ │ ├── MyStore.Requirements.csproj
│ │ ├── Epics/
│ │ └── Features/
│ ├── MyStore.SharedKernel/ ← Domain value types
│ │ ├── MyStore.SharedKernel.csproj
│ │ └── Result.cs
│ ├── MyStore.Specifications/ ← Empty, waiting for specs
│ │ └── MyStore.Specifications.csproj
│ ├── MyStore.Lib/ ← Domain model (aggregates, entities)
│ │ ├── MyStore.Lib.csproj
│ │ └── Ordering/
│ ├── MyStore.Infrastructure.Postgres/ ← EF Core + generated repos
│ │ └── MyStore.Infrastructure.Postgres.csproj
│ ├── MyStore.Server/ ← ASP.NET host (API + admin + routing)
│ │ ├── MyStore.Server.csproj
│ │ └── Program.cs
│ └── MyStore.Client/ ← Blazor WASM (page widgets)
│ └── MyStore.Client.csproj
├── test/
│ └── MyStore.Tests/ ← Test scaffolds
│ └── MyStore.Tests.csproj
└── .github/
└── workflows/
└── build.yml ← CI pipeline with quality gatesMyStore/
├── MyStore.sln
├── Directory.Build.props ← Quality gates config, analyzer settings
├── .editorconfig ← REQ diagnostic severities
├── src/
│ ├── MyStore.Requirements/ ← Empty Feature/Epic stubs per template
│ │ ├── MyStore.Requirements.csproj
│ │ ├── Epics/
│ │ └── Features/
│ ├── MyStore.SharedKernel/ ← Domain value types
│ │ ├── MyStore.SharedKernel.csproj
│ │ └── Result.cs
│ ├── MyStore.Specifications/ ← Empty, waiting for specs
│ │ └── MyStore.Specifications.csproj
│ ├── MyStore.Lib/ ← Domain model (aggregates, entities)
│ │ ├── MyStore.Lib.csproj
│ │ └── Ordering/
│ ├── MyStore.Infrastructure.Postgres/ ← EF Core + generated repos
│ │ └── MyStore.Infrastructure.Postgres.csproj
│ ├── MyStore.Server/ ← ASP.NET host (API + admin + routing)
│ │ ├── MyStore.Server.csproj
│ │ └── Program.cs
│ └── MyStore.Client/ ← Blazor WASM (page widgets)
│ └── MyStore.Client.csproj
├── test/
│ └── MyStore.Tests/ ← Test scaffolds
│ └── MyStore.Tests.csproj
└── .github/
└── workflows/
└── build.yml ← CI pipeline with quality gatescmf add aggregate -- Create an Aggregate
cmf add aggregate Order --context Orderingcmf add aggregate Order --context OrderingCreates src/MyStore.Lib/Ordering/Order.cs:
namespace MyStore.Lib.Ordering;
[AggregateRoot("Order", BoundedContext = "Ordering")]
public partial class Order
{
[EntityId]
public partial OrderId Id { get; }
// TODO: Add [Property] and [Composition] members
// TODO: Add [Invariant] methods returning Result
[Invariant("Order must have a valid state")]
private Result IsValid()
=> Result.Success(); // Placeholder -- replace with real invariant
}namespace MyStore.Lib.Ordering;
[AggregateRoot("Order", BoundedContext = "Ordering")]
public partial class Order
{
[EntityId]
public partial OrderId Id { get; }
// TODO: Add [Property] and [Composition] members
// TODO: Add [Invariant] methods returning Result
[Invariant("Order must have a valid state")]
private Result IsValid()
=> Result.Success(); // Placeholder -- replace with real invariant
}Also creates src/MyStore.Lib/Ordering/OrderId.cs:
namespace MyStore.Lib.Ordering;
public readonly record struct OrderId(Guid Value)
{
public static OrderId New() => new(Guid.NewGuid());
}namespace MyStore.Lib.Ordering;
public readonly record struct OrderId(Guid Value)
{
public static OrderId New() => new(Guid.NewGuid());
}cmf add command -- Create a Command
cmf add command PlaceOrder --target Ordercmf add command PlaceOrder --target OrderCreates src/MyStore.Lib/Ordering/Commands/PlaceOrderCommand.cs:
namespace MyStore.Lib.Ordering.Commands;
[Command("PlaceOrder", AggregateRoot = "Order")]
public partial record PlaceOrderCommand
{
// TODO: Add [Property] members for command data
}namespace MyStore.Lib.Ordering.Commands;
[Command("PlaceOrder", AggregateRoot = "Order")]
public partial record PlaceOrderCommand
{
// TODO: Add [Property] members for command data
}cmf add event -- Create a Domain Event
cmf add event OrderPlaced --source Ordercmf add event OrderPlaced --source OrderCreates src/MyStore.Lib/Ordering/Events/OrderPlacedEvent.cs:
namespace MyStore.Lib.Ordering.Events;
[DomainEvent("OrderPlaced", SourceAggregate = "Order")]
public partial record OrderPlacedEvent
{
// TODO: Add [Property] members for event payload
}namespace MyStore.Lib.Ordering.Events;
[DomainEvent("OrderPlaced", SourceAggregate = "Order")]
public partial record OrderPlacedEvent
{
// TODO: Add [Property] members for event payload
}cmf add feature -- Create a Feature Requirement
cmf add feature OrderFulfillment --epic DomainModelingEpiccmf add feature OrderFulfillment --epic DomainModelingEpicCreates src/MyStore.Requirements/Features/OrderFulfillmentFeature.cs:
namespace MyStore.Requirements.Features;
using MyStore.Requirements.Epics;
/// <summary>
/// Feature: Order Fulfillment
/// Add acceptance criteria as abstract methods returning
/// AcceptanceCriterionResult.
/// </summary>
public abstract record OrderFulfillmentFeature
: Feature<DomainModelingEpic>
{
public override string Title => "Order Fulfillment";
public override RequirementPriority Priority
=> RequirementPriority.Medium;
public override string Owner => "team";
// TODO: Add acceptance criteria as abstract methods
// Example:
// public abstract AcceptanceCriterionResult OrderCanBePlaced(
// UserId customer, ProductId product, int quantity);
}namespace MyStore.Requirements.Features;
using MyStore.Requirements.Epics;
/// <summary>
/// Feature: Order Fulfillment
/// Add acceptance criteria as abstract methods returning
/// AcceptanceCriterionResult.
/// </summary>
public abstract record OrderFulfillmentFeature
: Feature<DomainModelingEpic>
{
public override string Title => "Order Fulfillment";
public override RequirementPriority Priority
=> RequirementPriority.Medium;
public override string Owner => "team";
// TODO: Add acceptance criteria as abstract methods
// Example:
// public abstract AcceptanceCriterionResult OrderCanBePlaced(
// UserId customer, ProductId product, int quantity);
}cmf add story -- Create a Story
cmf add story ProcessPayment --feature OrderFulfillmentFeaturecmf add story ProcessPayment --feature OrderFulfillmentFeatureCreates src/MyStore.Requirements/Stories/ProcessPaymentStory.cs:
namespace MyStore.Requirements.Stories;
using MyStore.Requirements.Features;
public abstract record ProcessPaymentStory
: Story<OrderFulfillmentFeature>
{
public override string Title => "Process Payment";
public override RequirementPriority Priority
=> RequirementPriority.Medium;
public override string Owner => "team";
// TODO: Add acceptance criteria as abstract methods
}namespace MyStore.Requirements.Stories;
using MyStore.Requirements.Features;
public abstract record ProcessPaymentStory
: Story<OrderFulfillmentFeature>
{
public override string Title => "Process Payment";
public override RequirementPriority Priority
=> RequirementPriority.Medium;
public override string Owner => "team";
// TODO: Add acceptance criteria as abstract methods
}cmf add saga -- Create a Saga
cmf add saga OrderFulfillmentcmf add saga OrderFulfillmentCreates src/MyStore.Lib/Sagas/OrderFulfillmentSaga.cs:
namespace MyStore.Lib.Sagas;
[Saga("OrderFulfillment")]
public partial class OrderFulfillmentSaga
{
// TODO: Add [SagaStep] methods with compensating actions
// Example:
// [SagaStep(1, Command = "ReserveInventory",
// CompensateWith = "ReleaseInventory")]
// public partial void Step1_ReserveInventory();
}namespace MyStore.Lib.Sagas;
[Saga("OrderFulfillment")]
public partial class OrderFulfillmentSaga
{
// TODO: Add [SagaStep] methods with compensating actions
// Example:
// [SagaStep(1, Command = "ReserveInventory",
// CompensateWith = "ReleaseInventory")]
// public partial void Step1_ReserveInventory();
}cmf generate -- Run Source Generation Pipeline
cmf generatecmf generateExplicitly triggers all pipeline stages (equivalent to dotnet build but with verbose stage output):
[Stage 0] Metamodel Registration
Registered: AggregateRoot, Entity, ValueObject, ContentPart,
AdminModule, PageWidget, Workflow, Feature, Story...
[Stage 1] DSL Validation & Collection
Found: 3 aggregates, 2 content types, 1 workflow, 4 features
Validated: 0 errors, 2 warnings (DDD100: Order has no invariants)
[Stage 2] Core Generation
Generated: Order.g.cs, OrderBuilder.g.cs, OrderConfiguration.g.cs
Generated: Product.g.cs, ProductBuilder.g.cs, ProductConfiguration.g.cs
Generated: RequirementRegistry.g.cs (4 features, 12 ACs)
[Stage 3] Cross-Cutting Generation
Generated: OrdersAdminModule.g.razor, ProductsAdminModule.g.razor
Generated: OrdersController.g.cs, ProductsController.g.cs
Generated: EditorialWorkflowStateMachine.g.cs
[Stage 4] Traceability & Diagnostics
Generated: TraceabilityMatrix.g.cs
REQ301: OrderFulfillmentFeature.OrderCanBePlaced has no [Verifies] test
Build succeeded. 47 files generated. 1 warning.[Stage 0] Metamodel Registration
Registered: AggregateRoot, Entity, ValueObject, ContentPart,
AdminModule, PageWidget, Workflow, Feature, Story...
[Stage 1] DSL Validation & Collection
Found: 3 aggregates, 2 content types, 1 workflow, 4 features
Validated: 0 errors, 2 warnings (DDD100: Order has no invariants)
[Stage 2] Core Generation
Generated: Order.g.cs, OrderBuilder.g.cs, OrderConfiguration.g.cs
Generated: Product.g.cs, ProductBuilder.g.cs, ProductConfiguration.g.cs
Generated: RequirementRegistry.g.cs (4 features, 12 ACs)
[Stage 3] Cross-Cutting Generation
Generated: OrdersAdminModule.g.razor, ProductsAdminModule.g.razor
Generated: OrdersController.g.cs, ProductsController.g.cs
Generated: EditorialWorkflowStateMachine.g.cs
[Stage 4] Traceability & Diagnostics
Generated: TraceabilityMatrix.g.cs
REQ301: OrderFulfillmentFeature.OrderCanBePlaced has no [Verifies] test
Build succeeded. 47 files generated. 1 warning.cmf validate -- Check Metamodel Constraints Without Generating
cmf validatecmf validateRuns Stage 0 and Stage 1 only. Validates all MetaConstraints. Does not generate code.
Validating metamodel constraints...
Order (AggregateRoot)
MustHaveId: PASS (has [EntityId] property)
MustHaveInvariant: WARN (0 [Invariant] methods)
OrderLine (Entity)
MustBeComposed: PASS (reachable from Order via [Composition])
BlogPost (AggregateRoot)
MustHaveId: PASS
MustHaveInvariant: PASS (1 invariant)
Validation complete: 0 errors, 1 warning.Validating metamodel constraints...
Order (AggregateRoot)
MustHaveId: PASS (has [EntityId] property)
MustHaveInvariant: WARN (0 [Invariant] methods)
OrderLine (Entity)
MustBeComposed: PASS (reachable from Order via [Composition])
BlogPost (AggregateRoot)
MustHaveId: PASS
MustHaveInvariant: PASS (1 invariant)
Validation complete: 0 errors, 1 warning.cmf migrate -- Generate EF Core Migrations
cmf migrate --name InitialCreatecmf migrate --name InitialCreateWrapper around dotnet ef migrations add with CMF-aware configuration:
Running: dotnet ef migrations add InitialCreate
--project src/MyStore.Infrastructure.Postgres
--startup-project src/MyStore.Server
Migration 'InitialCreate' created.
Tables: Orders, OrderLines, Products, BlogPosts
Owned types: ShippingAddress (embedded in Orders)Running: dotnet ef migrations add InitialCreate
--project src/MyStore.Infrastructure.Postgres
--startup-project src/MyStore.Server
Migration 'InitialCreate' created.
Tables: Orders, OrderLines, Products, BlogPosts
Owned types: ShippingAddress (embedded in Orders)cmf report -- Generate Reports
# Markdown requirement hierarchy
cmf report requirements
# Traceability matrix (markdown, JSON, or CSV)
cmf report traceability --format markdown
cmf report traceability --format json
cmf report traceability --format csv
# Coverage statistics
cmf report coverage# Markdown requirement hierarchy
cmf report requirements
# Traceability matrix (markdown, JSON, or CSV)
cmf report traceability --format markdown
cmf report traceability --format json
cmf report traceability --format csv
# Coverage statistics
cmf report coveragecmf report requirements output:
# Requirements Hierarchy
## Epic: DomainModelingEpic
- **OrderFulfillmentFeature** (3 ACs) [High]
- ProcessPaymentStory (2 ACs) [Medium]
- ShipOrderStory (1 AC) [Medium]
- **InventoryManagementFeature** (2 ACs) [Medium]
## Epic: ContentManagementEpic
- **BlogContentFeature** (2 ACs) [High]
Total: 2 epics, 3 features, 3 stories, 10 ACs# Requirements Hierarchy
## Epic: DomainModelingEpic
- **OrderFulfillmentFeature** (3 ACs) [High]
- ProcessPaymentStory (2 ACs) [Medium]
- ShipOrderStory (1 AC) [Medium]
- **InventoryManagementFeature** (2 ACs) [Medium]
## Epic: ContentManagementEpic
- **BlogContentFeature** (2 ACs) [High]
Total: 2 epics, 3 features, 3 stories, 10 ACscmf report traceability --format markdown output:
# Traceability Matrix
| Requirement | AC | Spec Method | Implementation | Tests | Coverage |
|-------------|-----|-------------|----------------|-------|----------|
| OrderFulfillmentFeature | OrderCanBePlaced | IOrderFulfillmentSpec.PlaceOrder | OrderService.PlaceOrder | 2 | 91% |
| OrderFulfillmentFeature | OrderCanBeCancelled | IOrderFulfillmentSpec.CancelOrder | OrderService.CancelOrder | 1 | 78% |
| OrderFulfillmentFeature | OrderTotalIsCorrect | IOrderFulfillmentSpec.CalculateTotal | OrderService.CalculateTotal | 3 | 95% |# Traceability Matrix
| Requirement | AC | Spec Method | Implementation | Tests | Coverage |
|-------------|-----|-------------|----------------|-------|----------|
| OrderFulfillmentFeature | OrderCanBePlaced | IOrderFulfillmentSpec.PlaceOrder | OrderService.PlaceOrder | 2 | 91% |
| OrderFulfillmentFeature | OrderCanBeCancelled | IOrderFulfillmentSpec.CancelOrder | OrderService.CancelOrder | 1 | 78% |
| OrderFulfillmentFeature | OrderTotalIsCorrect | IOrderFulfillmentSpec.CalculateTotal | OrderService.CalculateTotal | 3 | 95% |cmf report coverage output:
Requirement Coverage Report
===========================
Overall: 8/10 ACs covered (80%)
OrderFulfillmentFeature: 3/3 ACs (100%) ████████████████████
InventoryManagementFeature: 1/2 ACs ( 50%) ██████████░░░░░░░░░░
BlogContentFeature: 2/2 ACs (100%) ████████████████████
ProcessPaymentStory: 1/2 ACs ( 50%) ██████████░░░░░░░░░░
ShipOrderStory: 1/1 ACs (100%) ████████████████████Requirement Coverage Report
===========================
Overall: 8/10 ACs covered (80%)
OrderFulfillmentFeature: 3/3 ACs (100%) ████████████████████
InventoryManagementFeature: 1/2 ACs ( 50%) ██████████░░░░░░░░░░
BlogContentFeature: 2/2 ACs (100%) ████████████████████
ProcessPaymentStory: 1/2 ACs ( 50%) ██████████░░░░░░░░░░
ShipOrderStory: 1/1 ACs (100%) ████████████████████Interactive TUI: cmf design
The interactive TUI launches a terminal-based modeling session using Spectre.Console. It provides visual designers for the four core CMF activities: domain modeling, feature definition, workflow configuration, and compliance monitoring.
cmf designcmf designTUI Screen Flow
Main Menu
┌─────────────────────────────────────────────────────────┐
│ CMF Design Studio │
│ MyStore (ecommerce) │
├─────────────────────────────────────────────────────────┤
│ │
│ > [1] Aggregate Designer 3 aggregates defined │
│ [2] Feature Wizard 4 features, 12 ACs │
│ [3] Workflow Builder 1 workflow, 4 stages │
│ [4] Requirement Dashboard 80% AC coverage │
│ │
│ [Q] Quit │
│ │
├─────────────────────────────────────────────────────────┤
│ Last build: 2s ago | 0 errors | 2 warnings │
└─────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────┐
│ CMF Design Studio │
│ MyStore (ecommerce) │
├─────────────────────────────────────────────────────────┤
│ │
│ > [1] Aggregate Designer 3 aggregates defined │
│ [2] Feature Wizard 4 features, 12 ACs │
│ [3] Workflow Builder 1 workflow, 4 stages │
│ [4] Requirement Dashboard 80% AC coverage │
│ │
│ [Q] Quit │
│ │
├─────────────────────────────────────────────────────────┤
│ Last build: 2s ago | 0 errors | 2 warnings │
└─────────────────────────────────────────────────────────┘Aggregate Designer
The Aggregate Designer provides a tree view of all aggregates in the solution. Selecting an aggregate opens editors for properties, compositions, and invariants, with a live code preview panel.
┌─ Aggregate Designer ────────────────────────────────────┐
│ │
│ Aggregates │ Properties │
│ ───────── │ ────────── │
│ ▼ Ordering │ Order │
│ > Order ◄ │ │
│ └─ OrderLine │ [EntityId] OrderId Id │
│ └─ PaymentDetails │ [P] DateTime OrderDate │
│ └─ ShippingAddress (VO) │ [P] OrderStatus Status │
│ ▼ Catalog │ [C] List<OrderLine> │
│ > Product │ [C] PaymentDetails │
│ ▼ Identity │ [C] ShippingAddress │
│ > Customer │ [A] CustomerId │
│ │ │
│ [A]dd [E]dit [D]elete │ 4 Invariants defined │
│ │
├─ Live Code Preview ─────────────────────────────────────┤
│ │
│ [AggregateRoot("Order", BoundedContext = "Ordering")] │
│ public partial class Order │
│ { │
│ [EntityId] │
│ public partial OrderId Id { get; } │
│ │
│ [Property("OrderDate", Required = true)] │
│ public partial DateTime OrderDate { get; } │
│ │
│ [Composition] │
│ public partial IReadOnlyList<OrderLine> Lines ... │
│ ... │
│ } │
│ │
├─ Generated EF Core ─────────────────────────────────────┤
│ builder.ToTable("Orders"); │
│ builder.HasKey(x => x.Id); │
│ builder.HasMany(x => x.Lines).OnDelete(Cascade); │
│ builder.OwnsOne(x => x.ShippingAddress, sa => { │
│ sa.Property(a => a.Street).HasMaxLength(200); │
│ }); │
│ │
│ [P]roperty [C]omposition [I]nvariant [B]ack │
└─────────────────────────────────────────────────────────┘┌─ Aggregate Designer ────────────────────────────────────┐
│ │
│ Aggregates │ Properties │
│ ───────── │ ────────── │
│ ▼ Ordering │ Order │
│ > Order ◄ │ │
│ └─ OrderLine │ [EntityId] OrderId Id │
│ └─ PaymentDetails │ [P] DateTime OrderDate │
│ └─ ShippingAddress (VO) │ [P] OrderStatus Status │
│ ▼ Catalog │ [C] List<OrderLine> │
│ > Product │ [C] PaymentDetails │
│ ▼ Identity │ [C] ShippingAddress │
│ > Customer │ [A] CustomerId │
│ │ │
│ [A]dd [E]dit [D]elete │ 4 Invariants defined │
│ │
├─ Live Code Preview ─────────────────────────────────────┤
│ │
│ [AggregateRoot("Order", BoundedContext = "Ordering")] │
│ public partial class Order │
│ { │
│ [EntityId] │
│ public partial OrderId Id { get; } │
│ │
│ [Property("OrderDate", Required = true)] │
│ public partial DateTime OrderDate { get; } │
│ │
│ [Composition] │
│ public partial IReadOnlyList<OrderLine> Lines ... │
│ ... │
│ } │
│ │
├─ Generated EF Core ─────────────────────────────────────┤
│ builder.ToTable("Orders"); │
│ builder.HasKey(x => x.Id); │
│ builder.HasMany(x => x.Lines).OnDelete(Cascade); │
│ builder.OwnsOne(x => x.ShippingAddress, sa => { │
│ sa.Property(a => a.Street).HasMaxLength(200); │
│ }); │
│ │
│ [P]roperty [C]omposition [I]nvariant [B]ack │
└─────────────────────────────────────────────────────────┘Interactions:
- Arrow keys navigate the aggregate tree
[A]ddopens a prompt: "Add property, composition, or association?"- Adding a property prompts for name, type, required, max length
- The Live Code Preview updates in real-time as properties are added
- The Generated EF Core panel shows what the source generator will produce
[I]nvariantopens the invariant editor
Adding a property:
┌─ Add Property ──────────────────────────────────────────┐
│ │
│ Name: [Notes ] │
│ Type: [string ] (tab: suggest) │
│ Required: [x] Yes [ ] No │
│ MaxLength: [500 ] │
│ │
│ Preview: │
│ [Property("Notes", Required = true, MaxLength = 500)] │
│ public partial string Notes { get; } │
│ │
│ [Enter] Confirm [Esc] Cancel │
└─────────────────────────────────────────────────────────┘┌─ Add Property ──────────────────────────────────────────┐
│ │
│ Name: [Notes ] │
│ Type: [string ] (tab: suggest) │
│ Required: [x] Yes [ ] No │
│ MaxLength: [500 ] │
│ │
│ Preview: │
│ [Property("Notes", Required = true, MaxLength = 500)] │
│ public partial string Notes { get; } │
│ │
│ [Enter] Confirm [Esc] Cancel │
└─────────────────────────────────────────────────────────┘Feature Wizard
The Feature Wizard provides guided creation of epics, features, stories, and tasks with acceptance criteria prompts.
┌─ Feature Wizard ────────────────────────────────────────┐
│ │
│ Step 1 of 3: Select or Create Epic │
│ │
│ Existing Epics: │
│ > DomainModelingEpic (5 features) │
│ ContentManagementEpic (2 features) │
│ [+] Create New Epic │
│ │
│ [Enter] Select [N]ew Epic [Esc] Cancel │
└─────────────────────────────────────────────────────────┘┌─ Feature Wizard ────────────────────────────────────────┐
│ │
│ Step 1 of 3: Select or Create Epic │
│ │
│ Existing Epics: │
│ > DomainModelingEpic (5 features) │
│ ContentManagementEpic (2 features) │
│ [+] Create New Epic │
│ │
│ [Enter] Select [N]ew Epic [Esc] Cancel │
└─────────────────────────────────────────────────────────┘After selecting an epic:
┌─ Feature Wizard ────────────────────────────────────────┐
│ │
│ Step 2 of 3: Define Feature │
│ Parent: DomainModelingEpic │
│ │
│ Name: [InventoryManagement ] │
│ Title: [Inventory tracking and stock management ] │
│ Priority: [High ] (Critical/High/Medium/Low) │
│ Owner: [warehouse-team ] │
│ │
│ [Enter] Next: Define Acceptance Criteria │
└─────────────────────────────────────────────────────────┘┌─ Feature Wizard ────────────────────────────────────────┐
│ │
│ Step 2 of 3: Define Feature │
│ Parent: DomainModelingEpic │
│ │
│ Name: [InventoryManagement ] │
│ Title: [Inventory tracking and stock management ] │
│ Priority: [High ] (Critical/High/Medium/Low) │
│ Owner: [warehouse-team ] │
│ │
│ [Enter] Next: Define Acceptance Criteria │
└─────────────────────────────────────────────────────────┘Acceptance criteria definition:
┌─ Feature Wizard ────────────────────────────────────────┐
│ │
│ Step 3 of 3: Acceptance Criteria │
│ Feature: InventoryManagementFeature │
│ │
│ Defined ACs: │
│ 1. StockIsDecrementedOnOrder(ProductId, int quantity) │
│ 2. OutOfStockPreventsOrder(ProductId) │
│ │
│ Add AC: │
│ Method name: [LowStockAlertSent ] │
│ Parameters: │
│ 1. [ProductId product ] [+] Add param │
│ 2. [int threshold ] │
│ 3. [Email alertRecipient ] │
│ │
│ Preview: │
│ public abstract AcceptanceCriterionResult │
│ LowStockAlertSent( │
│ ProductId product, │
│ int threshold, │
│ Email alertRecipient); │
│ │
│ [Enter] Add AC [F]inish Feature [Esc] Cancel │
└─────────────────────────────────────────────────────────┘┌─ Feature Wizard ────────────────────────────────────────┐
│ │
│ Step 3 of 3: Acceptance Criteria │
│ Feature: InventoryManagementFeature │
│ │
│ Defined ACs: │
│ 1. StockIsDecrementedOnOrder(ProductId, int quantity) │
│ 2. OutOfStockPreventsOrder(ProductId) │
│ │
│ Add AC: │
│ Method name: [LowStockAlertSent ] │
│ Parameters: │
│ 1. [ProductId product ] [+] Add param │
│ 2. [int threshold ] │
│ 3. [Email alertRecipient ] │
│ │
│ Preview: │
│ public abstract AcceptanceCriterionResult │
│ LowStockAlertSent( │
│ ProductId product, │
│ int threshold, │
│ Email alertRecipient); │
│ │
│ [Enter] Add AC [F]inish Feature [Esc] Cancel │
└─────────────────────────────────────────────────────────┘On finish, the wizard generates the feature file and opens it in the configured editor:
Created: src/MyStore.Requirements/Features/InventoryManagementFeature.cs
Epic: DomainModelingEpic
ACs: StockIsDecrementedOnOrder, OutOfStockPreventsOrder, LowStockAlertSent
Opening in editor...Created: src/MyStore.Requirements/Features/InventoryManagementFeature.cs
Epic: DomainModelingEpic
ACs: StockIsDecrementedOnOrder, OutOfStockPreventsOrder, LowStockAlertSent
Opening in editor...Workflow Builder
The Workflow Builder provides a visual editor for workflow stages, transitions, and gates -- all in the terminal.
┌─ Workflow Builder ──────────────────────────────────────┐
│ │
│ Workflow: EditorialWorkflow (BlogPost) │
│ │
│ Stages: │
│ ┌───────┐ ┌────────┐ ┌─────────────┐ ┌─────┐ │
│ │ Draft ├───>│ Review ├───>│ Translation ├───>│ Pub │ │
│ └───┬───┘ └────┬───┘ └──────┬──────┘ └─────┘ │
│ │ │ │ │
│ └─────────────┘ │ │
│ (reject: back │ │
│ to Draft) │ │
│ └───────────────┘ │
│ (reject: back │
│ to Review) │
│ │
│ Gates: │
│ Draft -> Review: RequiresRole("editor") │
│ Review -> Translation: RequiresRole("reviewer") │
│ Translation -> Pub: AllLocalesComplete │
│ RequiresRole("publisher") │
│ │
│ [S]tage [T]ransition [G]ate [B]ack │
└─────────────────────────────────────────────────────────┘┌─ Workflow Builder ──────────────────────────────────────┐
│ │
│ Workflow: EditorialWorkflow (BlogPost) │
│ │
│ Stages: │
│ ┌───────┐ ┌────────┐ ┌─────────────┐ ┌─────┐ │
│ │ Draft ├───>│ Review ├───>│ Translation ├───>│ Pub │ │
│ └───┬───┘ └────┬───┘ └──────┬──────┘ └─────┘ │
│ │ │ │ │
│ └─────────────┘ │ │
│ (reject: back │ │
│ to Draft) │ │
│ └───────────────┘ │
│ (reject: back │
│ to Review) │
│ │
│ Gates: │
│ Draft -> Review: RequiresRole("editor") │
│ Review -> Translation: RequiresRole("reviewer") │
│ Translation -> Pub: AllLocalesComplete │
│ RequiresRole("publisher") │
│ │
│ [S]tage [T]ransition [G]ate [B]ack │
└─────────────────────────────────────────────────────────┘Adding a gate:
┌─ Add Gate ──────────────────────────────────────────────┐
│ │
│ Transition: Review -> Translation │
│ │
│ Gate type: │
│ > RequiresRole │
│ AllLocalesComplete │
│ RequirementCompliance │
│ CustomExpression │
│ │
│ Selected: RequiresRole │
│ Role: [reviewer ] │
│ │
│ Preview: │
│ [Gate("ReviewApproval", RequiresRole = "reviewer")] │
│ │
│ [Enter] Confirm [Esc] Cancel │
└─────────────────────────────────────────────────────────┘┌─ Add Gate ──────────────────────────────────────────────┐
│ │
│ Transition: Review -> Translation │
│ │
│ Gate type: │
│ > RequiresRole │
│ AllLocalesComplete │
│ RequirementCompliance │
│ CustomExpression │
│ │
│ Selected: RequiresRole │
│ Role: [reviewer ] │
│ │
│ Preview: │
│ [Gate("ReviewApproval", RequiresRole = "reviewer")] │
│ │
│ [Enter] Confirm [Esc] Cancel │
└─────────────────────────────────────────────────────────┘The RequirementCompliance gate type links workflow transitions to requirement coverage:
Gate type: RequirementCompliance
Feature: [OrderFulfillmentFeature ]
MinCoverage: [80 ] %
Preview:
[Gate("QualityCheck",
RequirementCompliance = typeof(OrderFulfillmentFeature),
MinCoverage = 80)]
// Blocks transition unless 80% of ACs have passing testsGate type: RequirementCompliance
Feature: [OrderFulfillmentFeature ]
MinCoverage: [80 ] %
Preview:
[Gate("QualityCheck",
RequirementCompliance = typeof(OrderFulfillmentFeature),
MinCoverage = 80)]
// Blocks transition unless 80% of ACs have passing testsRequirement Dashboard
The Requirement Dashboard displays compliance status across all features, with drill-down into individual ACs.
┌─ Requirement Dashboard ─────────────────────────────────┐
│ │
│ Overall Coverage: 8/10 ACs (80%) │
│ ████████████████░░░░ │
│ │
│ Feature ACs Tested Coverage │
│ ───────────────────────── ──── ────── ──────── │
│ OrderFulfillmentFeature 3 3 100% ████ │
│ InventoryManagement 2 1 50% ██░░ │
│> BlogContentFeature 2 2 100% ████ │
│ ProcessPaymentStory 2 1 50% ██░░ │
│ ShipOrderStory 1 1 100% ████ │
│ │
├─ BlogContentFeature (2/2 ACs) ──────────────────────────┤
│ │
│ AC: BlogPostCanBeCreated │
│ Spec: IBlogContentSpec.CreatePost [OK] │
│ Impl: BlogService.CreatePost [OK] │
│ Tests: 2 passing [OK] │
│ Coverage: 91% [OK] │
│ │
│ AC: BlogPostCanBePublished │
│ Spec: IBlogContentSpec.PublishPost [OK] │
│ Impl: BlogService.PublishPost [OK] │
│ Tests: 1 passing [OK] │
│ Coverage: 85% [OK] │
│ │
│ [Enter] Drill down [R]efresh [B]ack │
└─────────────────────────────────────────────────────────┘┌─ Requirement Dashboard ─────────────────────────────────┐
│ │
│ Overall Coverage: 8/10 ACs (80%) │
│ ████████████████░░░░ │
│ │
│ Feature ACs Tested Coverage │
│ ───────────────────────── ──── ────── ──────── │
│ OrderFulfillmentFeature 3 3 100% ████ │
│ InventoryManagement 2 1 50% ██░░ │
│> BlogContentFeature 2 2 100% ████ │
│ ProcessPaymentStory 2 1 50% ██░░ │
│ ShipOrderStory 1 1 100% ████ │
│ │
├─ BlogContentFeature (2/2 ACs) ──────────────────────────┤
│ │
│ AC: BlogPostCanBeCreated │
│ Spec: IBlogContentSpec.CreatePost [OK] │
│ Impl: BlogService.CreatePost [OK] │
│ Tests: 2 passing [OK] │
│ Coverage: 91% [OK] │
│ │
│ AC: BlogPostCanBePublished │
│ Spec: IBlogContentSpec.PublishPost [OK] │
│ Impl: BlogService.PublishPost [OK] │
│ Tests: 1 passing [OK] │
│ Coverage: 85% [OK] │
│ │
│ [Enter] Drill down [R]efresh [B]ack │
└─────────────────────────────────────────────────────────┘Untested ACs view:
┌─ Untested Acceptance Criteria ──────────────────────────┐
│ │
│ The following ACs have no [Verifies] test: │
│ │
│ 1. InventoryManagement.OutOfStockPreventsOrder │
│ Spec: IInventorySpec.PreventOutOfStock [OK] │
│ Impl: InventoryService.PreventOutOfStock [OK] │
│ Tests: NONE [!!] │
│ │
│ 2. ProcessPaymentStory.RefundIsProcessed │
│ Spec: IPaymentSpec.ProcessRefund [OK] │
│ Impl: PaymentService.ProcessRefund [OK] │
│ Tests: NONE [!!] │
│ │
│ Action: These ACs need [Verifies] test methods. │
│ Run: cmf add test --feature InventoryManagement │
│ cmf add test --story ProcessPaymentStory │
│ │
│ [B]ack │
└─────────────────────────────────────────────────────────┘┌─ Untested Acceptance Criteria ──────────────────────────┐
│ │
│ The following ACs have no [Verifies] test: │
│ │
│ 1. InventoryManagement.OutOfStockPreventsOrder │
│ Spec: IInventorySpec.PreventOutOfStock [OK] │
│ Impl: InventoryService.PreventOutOfStock [OK] │
│ Tests: NONE [!!] │
│ │
│ 2. ProcessPaymentStory.RefundIsProcessed │
│ Spec: IPaymentSpec.ProcessRefund [OK] │
│ Impl: PaymentService.ProcessRefund [OK] │
│ Tests: NONE [!!] │
│ │
│ Action: These ACs need [Verifies] test methods. │
│ Run: cmf add test --feature InventoryManagement │
│ cmf add test --story ProcessPaymentStory │
│ │
│ [B]ack │
└─────────────────────────────────────────────────────────┘Live Code Preview
The live code preview is a panel that appears in every designer. As the developer models (adds properties, compositions, ACs), the panel shows the C# code that will be generated -- both the developer-authored code and the source-generated output.
The preview uses a two-column layout:
┌─ You Write ─────────────────────┬─ Compiler Generates ──────────────┐
│ │ │
│ [AggregateRoot("Order")] │ // Order.g.cs │
│ public partial class Order │ public partial class Order │
│ { │ { │
│ [EntityId] │ private OrderId _id; │
│ public partial OrderId Id; │ public partial OrderId Id │
│ │ => _id; │
│ [Composition] │ │
│ public partial │ private readonly │
│ IReadOnlyList<OrderLine> │ List<OrderLine> _lines = []; │
│ Lines { get; } │ public partial │
│ │ IReadOnlyList<OrderLine> │
│ [Invariant("...")] │ Lines => _lines.AsReadOnly(); │
│ private Result HasLines() │ │
│ => Lines.Count > 0 │ public Result │
│ ? Result.Success() │ EnsureInvariants() │
│ : Result.Failure("..."); │ => Result.Aggregate( │
│ } │ HasLines()); │
│ │ } │
│ │ │
│ │ // OrderBuilder.g.cs │
│ │ public class OrderBuilder { ... } │
│ │ │
│ │ // OrderConfiguration.g.cs │
│ │ builder.ToTable("Orders"); │
│ │ builder.HasKey(x => x.Id); │
│ │ builder.HasMany(x => x.Lines) │
│ │ .OnDelete(Cascade); │
└─────────────────────────────────┴────────────────────────────────────┘┌─ You Write ─────────────────────┬─ Compiler Generates ──────────────┐
│ │ │
│ [AggregateRoot("Order")] │ // Order.g.cs │
│ public partial class Order │ public partial class Order │
│ { │ { │
│ [EntityId] │ private OrderId _id; │
│ public partial OrderId Id; │ public partial OrderId Id │
│ │ => _id; │
│ [Composition] │ │
│ public partial │ private readonly │
│ IReadOnlyList<OrderLine> │ List<OrderLine> _lines = []; │
│ Lines { get; } │ public partial │
│ │ IReadOnlyList<OrderLine> │
│ [Invariant("...")] │ Lines => _lines.AsReadOnly(); │
│ private Result HasLines() │ │
│ => Lines.Count > 0 │ public Result │
│ ? Result.Success() │ EnsureInvariants() │
│ : Result.Failure("..."); │ => Result.Aggregate( │
│ } │ HasLines()); │
│ │ } │
│ │ │
│ │ // OrderBuilder.g.cs │
│ │ public class OrderBuilder { ... } │
│ │ │
│ │ // OrderConfiguration.g.cs │
│ │ builder.ToTable("Orders"); │
│ │ builder.HasKey(x => x.Id); │
│ │ builder.HasMany(x => x.Lines) │
│ │ .OnDelete(Cascade); │
└─────────────────────────────────┴────────────────────────────────────┘CLI Architecture
Technology Stack
| Component | Technology | Role |
|---|---|---|
| CLI framework | System.CommandLine |
Command parsing, help text, tab completion |
| TUI rendering | Spectre.Console |
Tables, trees, panels, prompts, progress bars |
| Code generation | Roslyn Workspace API | Read and write .cs files, trigger compilation |
| Template engine | dotnet new templates |
cmf new scaffolding |
| File watching | FileSystemWatcher |
Live preview updates in TUI |
How the CLI Interacts with the Compilation
The CLI does not duplicate the source generator logic. Instead, it uses the Roslyn Workspace API to:
- Open the solution (
MSBuildWorkspace.Create().OpenSolutionAsync()) - Read the compiled model (same SemanticModel the generators use)
- Present the model visually in the TUI
- Write new
.csfiles when the developer adds entities/features - Trigger
dotnet buildto re-run generators - Read the updated model and refresh the TUI
This means the TUI always reflects the same model that the source generators see. There is no separate configuration file, no separate data model. The .cs files ARE the model.
Summary
The CMF CLI provides two complementary interfaces:
| Interface | Commands | Audience | Use Case |
|---|---|---|---|
| Standard CLI | cmf new, cmf add, cmf generate, cmf validate, cmf migrate, cmf report |
All developers, CI pipelines | Scripting, automation, quick scaffolding |
| Interactive TUI | cmf design |
Developers and architects | Visual modeling, exploration, compliance review |
Both interfaces operate on the same source of truth: the .cs files in the project. The CLI writes files and triggers dotnet build. The TUI reads the Roslyn compilation model and presents it visually. The source generators run identically whether the developer uses the CLI, the TUI, or edits files directly in their IDE.
No separate configuration format. No YAML. No JSON schema. The C# code is the model, and the tool is a lens for viewing and editing it.