Thanks for taking the time. MTC is a small project, so the process is light — the only
hard rule is that dotnet build and dotnet test must be clean before a PR.
You need the .NET 10 SDK. Nothing else.
git clone https://github.com/JuansesDev/MTC.git
cd MTC
dotnet build
dotnet testThe build should finish with 0 warnings. If you see warnings that were not there before your change, they count as breakage.
dotnet run swallows CLI arguments in confusing ways, so drive the binary directly:
dotnet build
./MTC/bin/Debug/net10.0/MTC listA good scratch loop is to generate into a throwaway directory and inspect the result:
mkdir -p /tmp/mtc-scratch && cd /tmp/mtc-scratch
/path/to/MTC/MTC/bin/Debug/net10.0/MTC new CleanArch MyShop
/path/to/MTC/MTC/bin/Debug/net10.0/MTC add feature Product --fields "Name:string Price:decimal"
dotnet build MyShop.sln # generated projects must compile clean too| Path | What lives there |
|---|---|
MTC/Program.cs |
CLI command definitions and handlers |
MTC/Services/ |
The real logic — one interface + implementation per concern |
MTC/Models/ |
Plain data types (Template, Property, ProjectContext) |
Templates/ |
The templates themselves, copied to the build output |
MTC.Tests/ |
Unit tests |
MTC.IntegrationTests/ |
Tests that run the compiled binary end to end |
The services worth knowing:
ContextService— walks up from the current directory to find a.sln, then decides whether the solution is Clean Architecture, Vertical Slice or MVC.TemplateService— discovers templates by scanning recursively formanifest.json.ScribanTemplateRenderer— renders template content and file/directory names.ScaffoldingService— walks a template directory and writes the rendered result.FieldParser— turns"Name:string Price:decimal"intoPropertyobjects.
A template is just a directory with a manifest.json and the files to render. Both file
contents and file/directory names go through Scriban, which is why you see paths
like Templates/Common/Dto/{{Name}}.cs.
{
"Name": "MyTemplate",
"Description": "What it generates",
"Version": "1.0.0",
"Author": "You",
"Tags": ["Tag"],
"Variables": { "Name": "string" }
}Name is what users type (mtc new MyTemplate ...), so it must be unique.
| Variable | Available in | Notes |
|---|---|---|
Name |
everywhere | the entity/project name the user typed |
ProjectName |
add feature |
taken from the .sln file name |
Namespace |
add value-object, add dto |
resolved from the detected architecture |
Properties |
add * |
list of { Name, Type, IsValueType } |
Member names are case-sensitive: write {{ prop.Name }}, not {{ prop.name }}.
1. Control whitespace, or the output looks generated. Use {{~ and ~}} around
loops so the rendered file is indented like hand-written code:
public class {{Name}}
{
{{~ for prop in Properties ~}}
public {{ prop.Type }} {{ prop.Name }} { get; set; }
{{~ end ~}}
}
2. Emit required for reference types. Generated projects have
<Nullable>enable</Nullable>, so a plain public string Name { get; set; } raises CS8618.
That is what IsValueType is for:
public {{ if !prop.IsValueType }}required {{ end }}{{ prop.Type }} {{ prop.Name }} { get; set; }
Anything MTC generates must compile with zero warnings. Always build the generated solution before opening a PR, not just MTC itself.
- Put unit tests in
MTC.Tests, next to the service they cover. MTC.IntegrationTestsruns the compiled binary, so it needs a build first. It locates the executable by searchingMTC/bin/Debug/— don't hardcode a framework folder.- New behaviour needs a test. Bug fixes should come with a test that fails without the fix.
Follow the surrounding code; there is no separate style config.
- 4 spaces, file-scoped namespaces, nullable enabled.
- Services go behind an interface and get registered in
Program.cs. - Commands report failures with
AnsiConsole.MarkupLine("[red]Error:[/] ...")and setEnvironment.ExitCode = 1. Silent failures with exit code 0 break scripted use. - Prefer a short explanatory comment over a long one. Don't commit commented-out code or notes to yourself.
- Branch off
main. - Keep the change focused — one concern per PR.
- Make sure
dotnet buildanddotnet testare clean. - Add an entry under
## [Unreleased]in CHANGELOG.md. Describe the effect on users, not the diff. - Say what you changed and why. If it changes CLI behaviour, update the README too.
CI runs build and tests on every push and PR to main.
Version numbers live in two places and must match:
MTC/MTC.csproj→<Version>publish.sh→VERSION
Rename the ## [Unreleased] heading in CHANGELOG.md to the version and date, then tag
and push:
git tag v1.1.0
git push origin v1.1.0Tags may be written v1.1.0, V1.1.0, V.1.1.0 or 1.1.0 — the workflow strips the
prefix either way. Releases up to 1.0.2 used the V. form.
The release workflow builds the cross-platform binaries and the .deb, publishes the
GitHub release, and pushes the package to NuGet. AUR is published separately with
./publish_aur.sh.
Open an issue with the MTC version, your OS, the exact command, what you expected, and what happened. For anything about architecture detection, include the output of:
mtc debug-contextContributions are accepted under the MIT License.